From e2ae5b464b873f28c794bdb2477c7156df8d3c18 Mon Sep 17 00:00:00 2001 From: Mohammed S Date: Wed, 11 Oct 2023 18:22:57 +0530 Subject: [PATCH 01/17] Alpha to main (#768) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Spaces backend examples (#522) * fix: spaces documentation * Update README.md * fix: formatting issues * fix: uncommented code * Spaces SDK alpha publish (#520) * refactor: added initial SpacesUI class architecture * Update packages/uiweb/src/lib/components/space/SpacesUI.tsx Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> * Update packages/uiweb/src/lib/components/space/SpacesUI.tsx Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> * fix: refine code structure * refactor: change structure and added a test sample * feat: added themeprovider for spaces ui components (#436) * feat: feat: add themeprovider for spaces * fix: resolved conflicts * refactor: cleaned up provider * refactor: resolved comments * feat: added colors and themeing in demoreactapp * refactor: cleaned code * refactor: resolved comments * refactor: added a basic style structure for ease of use (#453) * feat: SpaceBanner Component (#438) * feat: SpaceBanner * refactor: removed-optionals * feat: space-banner functionality * fix: reverted app.tsx * fix: reverted app.tsx * fix: removed test svg file * refactor: change structure and added a test sample * feat: space-banner functionality * refactor: api-implementation * fix: removed-svg * refactor: mobile version * Revert "fix: removed test svg file" This reverts commit 7af756badaeb8435db60712c42ca57ef9ce9f74c. * Revert "refactor: change structure and added a test sample" This reverts commit b1e8745948a9ce5e0485ad51a8cffd07010a5186. * fix: revert * refactor: lastest-pull * feat: added-spaceBanner-test * refactor: cache-to-context * refactor: custom hook added for data * refactor: spaceDTO * fix: SpaceDTO implementation * refactor: participant-container * refactor: cleanups * fix: ui-fixes * fix: EOF newlines * fix: overflow-fixed * refactor: tweaked context structure a bit --------- Co-authored-by: Samarendra Gouda Co-authored-by: Nilesh Gupta * refactor: updated theme object along with light and dark theme * feat: Create Space Component (#454) * refactor: resolved merge conflicts * feat: added modals and modal behaviour logic * feat: modal inputs * feat: change modal logic + integr8 API * feat: added CSS for modals and inputs * fix: resolved PR comments * refactor: improve css * refactor: rft create modal * feat: added search input component * Refactor/space widget component (#458) * refactor: added basic design * refactor: added full widget with style * refactor: reduced fixed height of scheduled widget content * refactor: changes requested * refactor: resolved review comments * feat: add spaces info component (#474) * Feat/added members modal (#477) * refactor: added modal * refactor: added members modal * refactor: review comments * refactor: resolve review comments * fix: review comments * 424 spaces functions webrtc logic (#482) * fix: add spaces for functions * fix: Separate page for space in the demo react APP * fix: start/stop spaces functions * fix: fix image and description types * fix: added functions to add and remove members from group * fix: spaces functions * fix: spaces functions refactoring * fix: few more changes * fix: spaces function testing * fix: spaces functions * fix: SDK bug fixes * fix: SDK bug fixes * fix: minor fixes * fix: minor fix * fix: minor fix * feat(video): add create mesh connection logic in Video class * feat(spaces): make video mesh compatible & add spaces class * feat(spaces): add backend methods in Space class --------- Co-authored-by: Shoaib Mohammed Co-authored-by: Madhur Gupta * refactor: spaces UI Components refactor (#478) > replaced hardcoded styles with context theme >added smoother animations on collapsoble components >added profile cards to invite modal * refactor: added spaceUI class variables into context as well (#488) * refactor: added spaceUI class variables into context as well * refactor: resolve review comments * refactor: added a clickHandler in spaceBanner component for extra flexibility (#489) * fix(spaces): fix console errs and refactor create, update methods (#492) * fix: add spaces for functions * fix: Separate page for space in the demo react APP * fix: start/stop spaces functions * fix: fix image and description types * fix: added functions to add and remove members from group * fix: spaces functions * fix: spaces functions refactoring * fix: few more changes * fix: spaces function testing * fix: spaces functions * fix: SDK bug fixes * fix: SDK bug fixes * fix: minor fixes * fix: minor fix * fix: minor fix * feat(video): add create mesh connection logic in Video class * feat(spaces): make video mesh compatible & add spaces class * feat(spaces): add backend methods in Space class * fix: Merge branch 'alpha' into 424-spaces-functions-webrtc-logic * fix(spaces): fix console errors and move out create, update functions from Space class --------- Co-authored-by: Shoaib Mohammed * refactor: modified init class method * refactor/added-space-feed-component(#481) * feat: space-feed * refactor: space-feed * Deployment (#440) * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.0 * fix(component): dummy * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.0 * fix(component): dummy * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.1 * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.1 * fix(component): dummy * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.2 * fix(component): dummy * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.2 * fix(component): dummy * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.3 * fix: success progressHook * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.4 * ci(restapi): šŸŽ‰ cut release to restapi-v1.1.0 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.0 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.0 * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.0 * fix(component): dummy * fix(component): dummy * fix(component): dummy * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.2 * fix(component): dummy * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.3 * ci(restapi): šŸŽ‰ cut release to restapi-v1.0.4 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.1 * fix: local for local development (#295) Co-Authored-By: aman035 * fix: version update * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.2 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.3 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.4 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.5 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.6 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.7 * ci(socket): šŸŽ‰ cut release to socket-v0.5.0 * fix: test commit * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.8 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.0.0 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.0.0 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.0.1 * fix: added ci-version-beta * fix: added ci-version-beta * ci(restapi): šŸŽ‰ cut beta release to restapi-v1.2.9 * fix: added releaseType * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-beta.0 * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-beta.0 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.10 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.10 * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-beta.1 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.11 * fix: linkedListHash test cases removed & CI version corrected * fix: update name to beta * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.12 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.12 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.13 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.14 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.15 * fix: update package json * fix: updated socket version * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.0.2 * fix: socket lib update * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.0.2 * ci(restapi): šŸŽ‰ cut release to restapi-v1.2.16 * ci(restapi): šŸŽ‰ cut release to restapi-v1.3.0 * ci(restapi): šŸŽ‰ cut release to restapi-v1.3.1 * ci(socket): šŸŽ‰ cut release to socket-v0.5.1 * ci(restapi): šŸŽ‰ cut release to restapi-v1.3.2 * ci(restapi): šŸŽ‰ cut release to restapi-v1.3.3 * ci(restapi): šŸŽ‰ cut release to restapi-v1.3.4 --------- Co-authored-by: aman035 * feat: space-feed * refactor: space-feed * fix: api-call-custom-hooks * fix: api-call * refactor: enums/changes * fix: scrollable * fix: scrolling-issue * feat: filter-changes * fix: added types * fix: loader-issue * fix: tab-button-color * feat: new-filter and prop * fix: ended-logic && participant-number logic * refactor: add onBannerClick * fix: conflicts * revert: messed-up spaceBanner * fix: onClick issue * fix: onClick and cleanups * refactor: new pagination interface * fix: scroll-logic * fix: scroll-final * fix: .. * fix: removed any * fix: onClick-issue * fix: loading-context-to-state * refactor: add-NoSpaceIcons * refactor: new-ui-layout * refactor: new-ui-layout --------- Co-authored-by: Mohammed S Co-authored-by: aman035 Co-authored-by: Nilesh Gupta * refactor: added structure for spaces invites modal component * refactor: invite-component * fix: minor-bug * feat: Create Space API Integration (#494) * feat: create spaces integration * feat: added datetime picker * feat: completed create space API integration * feat: changed flow and time component * feat(spaces): initiate livepeer playback in start method (#499) * fix: add spaces for functions * fix: Separate page for space in the demo react APP * fix: start/stop spaces functions * fix: fix image and description types * fix: added functions to add and remove members from group * fix: spaces functions * fix: spaces functions refactoring * fix: few more changes * fix: spaces function testing * fix: spaces functions * fix: SDK bug fixes * fix: SDK bug fixes * fix: minor fixes * fix: minor fix * fix: minor fix * feat(video): add create mesh connection logic in Video class * feat(spaces): make video mesh compatible & add spaces class * feat(spaces): add backend methods in Space class * fix: Merge branch 'alpha' into 424-spaces-functions-webrtc-logic * fix(spaces): fix console errors and move out create, update functions from Space class * feat(spaces): initiate livepeer playback in start method * feat(spaces): store livepeer playback id in space description --------- Co-authored-by: Shoaib Mohammed * Add initialize method (#505) * fix: add spaces for functions * fix: Separate page for space in the demo react APP * fix: start/stop spaces functions * fix: fix image and description types * fix: added functions to add and remove members from group * fix: spaces functions * fix: spaces functions refactoring * fix: few more changes * fix: spaces function testing * fix: spaces functions * fix: SDK bug fixes * fix: SDK bug fixes * fix: minor fixes * fix: minor fix * fix: minor fix * feat(video): add create mesh connection logic in Video class * feat(spaces): make video mesh compatible & add spaces class * feat(spaces): add backend methods in Space class * fix: Merge branch 'alpha' into 424-spaces-functions-webrtc-logic * fix(spaces): fix console errors and move out create, update functions from Space class * feat(spaces): initiate livepeer playback in start method * feat(spaces): store livepeer playback id in space description * feat(spaces): add intitialize method --------- Co-authored-by: Shoaib Mohammed * refactor: resolved css in space banner * refactor: added a skeleton loading effect on space Banner component * feat: add invite functionality to create space (#506) * feat: added invite functionality * fix: add logic for time selector * fix: css fixes * refactor: info-on-widget (#504) * refactor: info-on-widget * refactor: add Space class to context * fix: padding, added new mic icons * fix: added-ref to context * feat: start added * fix: remove optional spaceId * fix: inf-bug * fix: effectAdded * fix(spaces): add is supported check in start * refactor: added-join-functionalities (#507) * refactor: added-join-functionalities * fix: screens * fix(spaces): fix livepeer stream creation in start --------- Co-authored-by: Madhur Gupta * Fix join and pgpPrivateKey in demoreact (#508) * refactor: info-on-widget * refactor: add Space class to context * fix: padding, added new mic icons * fix: added-ref to context * feat: start added * fix: remove optional spaceId * fix: inf-bug * fix: effectAdded * fix(spaces): add is supported check in start * refactor: added-join-functionalities * fix: screens * refactor: added-join-functionalities (#507) * refactor: added-join-functionalities * fix: screens * fix(spaces): fix livepeer stream creation in start * fix: join function --------- Co-authored-by: samarendra-push Co-authored-by: Samarendra Gouda <134079446+samarendra-push@users.noreply.github.com> * refactor: added feature of triggering widget from sdk * fix(spaces): fix join as speaker and listner * fix: resolved updating env and other class variables * Spaces/UI migration (#510) * fix: ui-migration * fix: migration fixes * refactor: added notification socket for space in uiweb * feat: add remove and admin func to invite modal (#509) * feat: add remove and admin func to invite modal * fix: fix null * fix(spaces): fix start * feat: add join functionality to invited spaces (#512) * feat: add join functionality to invited spaces * fix: open space widget after joining * refactor: add audio playback from space speakers (#511) * refactor: added a hidden Video container * fix: migrated initSpaceObject to parent component * feat(spaces): add user feeds socket handler logic * fix: fix create invite UI edge cases (#515) * fix: fix create invite UI edge cases * fix: fix env * fix: fix state behaviour * fix: add wallet as name if name string empty add wallet as name if name string empty * refactor: added join function and socket code (#517) * refactor: added join function and socket code * refactor(spaces): fix mesh creation logic * refactor(spaces): remove unused imports from context * refactor(spaces): remove unused code and improve positioning of code blocks --------- Co-authored-by: Madhur Gupta * Fix isJoined, add hidden video tag (#514) * refactor: added a hidden Video container * fix: migrated initSpaceObject to parent component * fix: WIP isJoined * fix: join as a listener --------- Co-authored-by: Madhur Gupta * fix(spaces): add create audio call before join for speakers (#518) * Widget/videoplayer (#519) * refactor: added a hidden Video container * fix: migrated initSpaceObject to parent component * fix: WIP isJoined * fix: revert husky * fix: nx.json revert * fix: join as a listener * fix: start ui-logic refactor * fix: merge-conflicts * fix: isListener * fix(spaces): fix isSpeaker and isListner logic * feat: added blockies (#523) --------- Co-authored-by: Nilesh Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Samarendra Gouda <134079446+samarendra-push@users.noreply.github.com> Co-authored-by: Samarendra Gouda Co-authored-by: Shoaib Mohammed Co-authored-by: aman035 Co-authored-by: samarendra-push * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.23 * Update README.md * fix: rename chats to spaces * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.24 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.0 * fix: spaces examples fixes * fix: renamed spaces variables and removed some unused variables * Update README.md * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.1 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.0 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.1 * fix: corrections * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.2 * fix: space api * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.25 * fix: use SpaceIFeeds * fix: signer compatibility with viem and ethers * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.3 * fix: space feed API path fix * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.4 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.2 * fix: revert space changes * fix: merge main * fix: signer compatibility with viem and ethers (#567) * fix: signer compatibility with viem and ethers * fix: revert space changes * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.26 * Update README.md * Update README.md * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.3 * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.27 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.4 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.5 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.6 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.7 * fix: add: scw sig verification (#593) * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.28 * fix: url correction * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.29 * ci(socket): šŸŽ‰ cut release to socket-v0.5.2 * fix: fixed subscribe and unsubscribe * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.5 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.8 * Feat/chat components (#621) * feat: created architechture * fix: added context values (#594) * Chat dataprovider (#596) * feat: data provider for chat component * fix: replaced react.usestate to usestate * fix: added props as the initial state and changed state name * fix: reverted chat context changes and renamed values * fix: added test page for chat ui components (#597) * added chatbubble component (#602) * feat: added chatbubble component * fix: made the messageBubble's width to fit-content --------- Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> * fix: added theme * Group chat bubble (#604) * feat: moved test component to testui folder & replaced dummy data with sdk response * feat: added twitter card and address for group chat received msg * feat: made the messageaddress reusable, added account from context * fix: removed unnecessary div and unused props and console logs * feat: adding pfp in text bubbles * fix: replaced hook with function and added pfp to messagebubble * fix: fixed image alignment * fix: changed border-radius of msg bubble and changed function name * fix: fixed theme and decryptedPrivateKey name (#616) * fix: fixed theme and decryptedPrivateKey name * fix: fixed bug * fix: fixed theme reviews * Message list (#615) * fix: message list comp * fix: message list comp * fix: added pagination * fix: added pagination * fix: pagination * fix: create useChatData hook * fix: fixed minor bug * fix: socket issues fixed * fix: added theme in msgbubble (#620) * fix: added theme in msgbubble * fix: fixed import --------- Co-authored-by: Monalisha Mishra --------- Co-authored-by: Satyam <100528412+KlausMikhaelson@users.noreply.github.com> Co-authored-by: KlausMikhaelson * fix: add alpha support to UI web * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.0 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.6 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.9 * Feat/chat components (#625) * feat: created architechture * fix: added context values (#594) * Chat dataprovider (#596) * feat: data provider for chat component * fix: replaced react.usestate to usestate * fix: added props as the initial state and changed state name * fix: reverted chat context changes and renamed values * fix: added test page for chat ui components (#597) * added chatbubble component (#602) * feat: added chatbubble component * fix: made the messageBubble's width to fit-content --------- Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> * fix: added theme * Group chat bubble (#604) * feat: moved test component to testui folder & replaced dummy data with sdk response * feat: added twitter card and address for group chat received msg * feat: made the messageaddress reusable, added account from context * fix: removed unnecessary div and unused props and console logs * feat: adding pfp in text bubbles * fix: replaced hook with function and added pfp to messagebubble * fix: fixed image alignment * fix: changed border-radius of msg bubble and changed function name * fix: fixed theme and decryptedPrivateKey name (#616) * fix: fixed theme and decryptedPrivateKey name * fix: fixed bug * fix: fixed theme reviews * Message list (#615) * fix: message list comp * fix: message list comp * fix: added pagination * fix: added pagination * fix: pagination * fix: create useChatData hook * fix: fixed minor bug * fix: socket issues fixed * fix: added theme in msgbubble (#620) * fix: added theme in msgbubble * fix: fixed import --------- Co-authored-by: Monalisha Mishra * fix: exported the theme (#623) * fix: exported the theme * fix: fixed issues --------- Co-authored-by: Monalisha Mishra --------- Co-authored-by: Satyam <100528412+KlausMikhaelson@users.noreply.github.com> Co-authored-by: KlausMikhaelson * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.1 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.7 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.8 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.10 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.9 * 632 group access control sdk changes (#640) * fix: group access control changes * fix: get group access SDK fix * fix: removed unnecessary param * Update README.md * Update README.md * Update README.md * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.30 * Reduce profile creation signature to 2 (#639) * fix: reduced signatures * fix: fixed examples * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.31 * fix: Read me fixes * fix: Space rules * Update README.md * Update README.md * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.32 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.10 * Feat/chat components (#658) * feat: created architechture * fix: added context values (#594) * Chat dataprovider (#596) * feat: data provider for chat component * fix: replaced react.usestate to usestate * fix: added props as the initial state and changed state name * fix: reverted chat context changes and renamed values * fix: added test page for chat ui components (#597) * added chatbubble component (#602) * feat: added chatbubble component * fix: made the messageBubble's width to fit-content --------- Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> * fix: added theme * Group chat bubble (#604) * feat: moved test component to testui folder & replaced dummy data with sdk response * feat: added twitter card and address for group chat received msg * feat: made the messageaddress reusable, added account from context * fix: removed unnecessary div and unused props and console logs * feat: adding pfp in text bubbles * fix: replaced hook with function and added pfp to messagebubble * fix: fixed image alignment * fix: changed border-radius of msg bubble and changed function name * fix: fixed theme and decryptedPrivateKey name (#616) * fix: fixed theme and decryptedPrivateKey name * fix: fixed bug * fix: fixed theme reviews * Message list (#615) * fix: message list comp * fix: message list comp * fix: added pagination * fix: added pagination * fix: pagination * fix: create useChatData hook * fix: fixed minor bug * fix: socket issues fixed * fix: added theme in msgbubble (#620) * fix: added theme in msgbubble * fix: fixed import --------- Co-authored-by: Monalisha Mishra * fix: exported the theme (#623) * fix: exported the theme * fix: fixed issues --------- Co-authored-by: Monalisha Mishra * Typebar component (#631) * feat: added typebar UI * feat: added functions to typebar * fix: added icon * fix: fixed theme issues --------- Co-authored-by: Monalisha Mishra * feat: added connectbutton * fix: fixed connectbtn ui and remove disconnect and fixed error on disconnect * fix: fixed create account getting called twice * Profile Header Component (#636) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * Message container (#635) * fix: added theme in msgbubble * fix: fixed import * fix: fixed message-list * fix: added approve intent * fix: added fixes * fix: fixed socket bug * fix: fixed message from socket * fix: fixed minor issues * fix: fixed typebar theming * fix: fixed env issue * fix: fixed message not updating issue * refactor: added isConnected prop in msgContainer * refactor: resolve merge conflicts * fix: fixed request sending * fix: fixed decryption * fix: fixed env issue * feat: added profile header in message container --------- Co-authored-by: KlausMikhaelson Co-authored-by: Nilesh Gupta * fix: updated svg to react component * fix: changed svg to tsx component * fix: fixed review changes (#646) * fix: fixed review changes * fix: resolved issues --------- Co-authored-by: Nilesh Gupta * Profile Header -> Chat Profile fixes (#647) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * fix: notifs * fix: correct theme colors * fix: renaming profileHeader * fix: qa fixes * fix: loader * refactor: resolved issues * fix: edit types --------- Co-authored-by: Nilesh Gupta * fix: resolved package not added issue * Fix: modal issue in group info && alert remove members (#653) * fix: modal * fix: update changes * refactor: resolved issue --------- Co-authored-by: Nilesh Gupta * fix: QA fixes (#654) --------- Co-authored-by: Monalisha Mishra Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> Co-authored-by: Satyam <100528412+KlausMikhaelson@users.noreply.github.com> Co-authored-by: KlausMikhaelson Co-authored-by: Kolade * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.11 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.12 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.11 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.13 * fix: update read me file * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.14 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.15 * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.12 * Feat/chat components (#679) * feat: created architechture * fix: added context values (#594) * Chat dataprovider (#596) * feat: data provider for chat component * fix: replaced react.usestate to usestate * fix: added props as the initial state and changed state name * fix: reverted chat context changes and renamed values * fix: added test page for chat ui components (#597) * added chatbubble component (#602) * feat: added chatbubble component * fix: made the messageBubble's width to fit-content --------- Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> * fix: added theme * Group chat bubble (#604) * feat: moved test component to testui folder & replaced dummy data with sdk response * feat: added twitter card and address for group chat received msg * feat: made the messageaddress reusable, added account from context * fix: removed unnecessary div and unused props and console logs * feat: adding pfp in text bubbles * fix: replaced hook with function and added pfp to messagebubble * fix: fixed image alignment * fix: changed border-radius of msg bubble and changed function name * fix: fixed theme and decryptedPrivateKey name (#616) * fix: fixed theme and decryptedPrivateKey name * fix: fixed bug * fix: fixed theme reviews * Message list (#615) * fix: message list comp * fix: message list comp * fix: added pagination * fix: added pagination * fix: pagination * fix: create useChatData hook * fix: fixed minor bug * fix: socket issues fixed * fix: added theme in msgbubble (#620) * fix: added theme in msgbubble * fix: fixed import --------- Co-authored-by: Monalisha Mishra * fix: exported the theme (#623) * fix: exported the theme * fix: fixed issues --------- Co-authored-by: Monalisha Mishra * Typebar component (#631) * feat: added typebar UI * feat: added functions to typebar * fix: added icon * fix: fixed theme issues --------- Co-authored-by: Monalisha Mishra * feat: added connectbutton * fix: fixed connectbtn ui and remove disconnect and fixed error on disconnect * fix: fixed create account getting called twice * Profile Header Component (#636) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * Message container (#635) * fix: added theme in msgbubble * fix: fixed import * fix: fixed message-list * fix: added approve intent * fix: added fixes * fix: fixed socket bug * fix: fixed message from socket * fix: fixed minor issues * fix: fixed typebar theming * fix: fixed env issue * fix: fixed message not updating issue * refactor: added isConnected prop in msgContainer * refactor: resolve merge conflicts * fix: fixed request sending * fix: fixed decryption * fix: fixed env issue * feat: added profile header in message container --------- Co-authored-by: KlausMikhaelson Co-authored-by: Nilesh Gupta * fix: updated svg to react component * fix: changed svg to tsx component * fix: fixed review changes (#646) * fix: fixed review changes * fix: resolved issues --------- Co-authored-by: Nilesh Gupta * Profile Header -> Chat Profile fixes (#647) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * fix: notifs * fix: correct theme colors * fix: renaming profileHeader * fix: qa fixes * fix: loader * refactor: resolved issues * fix: edit types --------- Co-authored-by: Nilesh Gupta * fix: resolved package not added issue * Fix: modal issue in group info && alert remove members (#653) * fix: modal * fix: update changes * refactor: resolved issue --------- Co-authored-by: Nilesh Gupta * fix: QA fixes (#654) * Connect btn revamp (#668) * fix: added theme in msgbubble * fix: fixed import * fix: fixed message-list * fix: added approve intent * fix: added fixes * fix: fixed socket bug * fix: fixed message from socket * fix: fixed minor issues * fix: fixed typebar theming * fix: fixed env issue * fix: fixed message not updating issue * refactor: added isConnected prop in msgContainer * refactor: resolve merge conflicts * fix: fixed request sending * fix: fixed decryption * fix: fixed env issue * feat: added profile header in message container * feat: adding rainbowkit for btn * fix: fix issues * fix: addec onnect functionality * fix: connect button * fix: added hack for rainbowkit css --------- Co-authored-by: KlausMikhaelson Co-authored-by: Nilesh Gupta * Typebar UI issue (#666) * fix: fixed typebar space not working and theme * fix: fixed gif and emoji --------- Co-authored-by: KlausMikhaelson * fix: fixed theme colours * fix: added theme for brb * fix: added filter hat * fix: merged with main * fix: fixed msg border * fix: fixed theme * fix: fixed fonts * fix: fixed bug * Access control (#672) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * fix: added minor fix --------- Co-authored-by: Monalisha Mishra * fix: fixed padding issues * fix(f): fixed build issues * fix: fix for chat status test * Check rules access control (#678) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * 662 group access control v2 changes (#663) * fix: spaces access API * fix: custom endpoint doc impl * Update README.md * Update README.md * fix: review comments change * fix: read me update * Update README.md * Update README.md * fix: origin in user creation (#665) * fix: origin in user creation * Update README.md * fix: review comments * 662 group access control v2 changes (#677) * fix: spaces access API * fix: custom endpoint doc impl * Update README.md * Update README.md * fix: review comments change * fix: read me update * Update README.md * Update README.md * fix(spaces): broadcast changes and UX fixes (#674) * fix: fix UI grid view (#642) * fix: fix UI grid view * refactor: conditional added * fix: resolved muting/unmuting try catch error (#657) * fix(spaces): fix livepeer broadcast (#656) * fix(spaces): fix livepeer broadcast * fix(spaces): fix data shown for unjoined space & promote listener logic --------- Co-authored-by: Madhur Gupta * refactor(spaces): use local state instead of get from server while meta message fire (#676) --------- Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Nilesh Gupta * feat: added verification option to show only on token gated groups * fix: fixing group access control --------- Co-authored-by: Monalisha Mishra Co-authored-by: Mohammed S Co-authored-by: Madhur Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Nilesh Gupta * fix: fixed build --------- Co-authored-by: Satyam <100528412+KlausMikhaelson@users.noreply.github.com> Co-authored-by: KlausMikhaelson Co-authored-by: Kolade Co-authored-by: Nilesh Gupta Co-authored-by: Mohammed S Co-authored-by: Madhur Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.2 * fix: dummy change * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.13 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.16 * Join group option (#681) * feat: created architechture * fix: added context values (#594) * Chat dataprovider (#596) * feat: data provider for chat component * fix: replaced react.usestate to usestate * fix: added props as the initial state and changed state name * fix: reverted chat context changes and renamed values * fix: added test page for chat ui components (#597) * added chatbubble component (#602) * feat: added chatbubble component * fix: made the messageBubble's width to fit-content --------- Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> * fix: added theme * Group chat bubble (#604) * feat: moved test component to testui folder & replaced dummy data with sdk response * feat: added twitter card and address for group chat received msg * feat: made the messageaddress reusable, added account from context * fix: removed unnecessary div and unused props and console logs * feat: adding pfp in text bubbles * fix: replaced hook with function and added pfp to messagebubble * fix: fixed image alignment * fix: changed border-radius of msg bubble and changed function name * fix: fixed theme and decryptedPrivateKey name (#616) * fix: fixed theme and decryptedPrivateKey name * fix: fixed bug * fix: fixed theme reviews * Message list (#615) * fix: message list comp * fix: message list comp * fix: added pagination * fix: added pagination * fix: pagination * fix: create useChatData hook * fix: fixed minor bug * fix: socket issues fixed * fix: added theme in msgbubble (#620) * fix: added theme in msgbubble * fix: fixed import --------- Co-authored-by: Monalisha Mishra * fix: exported the theme (#623) * fix: exported the theme * fix: fixed issues --------- Co-authored-by: Monalisha Mishra * Typebar component (#631) * feat: added typebar UI * feat: added functions to typebar * fix: added icon * fix: fixed theme issues --------- Co-authored-by: Monalisha Mishra * feat: added connectbutton * fix: fixed connectbtn ui and remove disconnect and fixed error on disconnect * fix: fixed create account getting called twice * Profile Header Component (#636) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * Message container (#635) * fix: added theme in msgbubble * fix: fixed import * fix: fixed message-list * fix: added approve intent * fix: added fixes * fix: fixed socket bug * fix: fixed message from socket * fix: fixed minor issues * fix: fixed typebar theming * fix: fixed env issue * fix: fixed message not updating issue * refactor: added isConnected prop in msgContainer * refactor: resolve merge conflicts * fix: fixed request sending * fix: fixed decryption * fix: fixed env issue * feat: added profile header in message container --------- Co-authored-by: KlausMikhaelson Co-authored-by: Nilesh Gupta * fix: updated svg to react component * fix: changed svg to tsx component * fix: fixed review changes (#646) * fix: fixed review changes * fix: resolved issues --------- Co-authored-by: Nilesh Gupta * Profile Header -> Chat Profile fixes (#647) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * fix: notifs * fix: correct theme colors * fix: renaming profileHeader * fix: qa fixes * fix: loader * refactor: resolved issues * fix: edit types --------- Co-authored-by: Nilesh Gupta * fix: resolved package not added issue * Fix: modal issue in group info && alert remove members (#653) * fix: modal * fix: update changes * refactor: resolved issue --------- Co-authored-by: Nilesh Gupta * fix: QA fixes (#654) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * Connect btn revamp (#668) * fix: added theme in msgbubble * fix: fixed import * fix: fixed message-list * fix: added approve intent * fix: added fixes * fix: fixed socket bug * fix: fixed message from socket * fix: fixed minor issues * fix: fixed typebar theming * fix: fixed env issue * fix: fixed message not updating issue * refactor: added isConnected prop in msgContainer * refactor: resolve merge conflicts * fix: fixed request sending * fix: fixed decryption * fix: fixed env issue * feat: added profile header in message container * feat: adding rainbowkit for btn * fix: fix issues * fix: addec onnect functionality * fix: connect button * fix: added hack for rainbowkit css --------- Co-authored-by: KlausMikhaelson Co-authored-by: Nilesh Gupta * Typebar UI issue (#666) * fix: fixed typebar space not working and theme * fix: fixed gif and emoji --------- Co-authored-by: KlausMikhaelson * fix: fixed theme colours * fix: added theme for brb * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * fix: added filter hat * fix: merged with main * fix: fixed msg border * fix: fixed theme * fix: fixed fonts * fix: fixed bug * Access control (#672) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * fix: added minor fix --------- Co-authored-by: Monalisha Mishra * fix: fixed padding issues * fix(f): fixed build issues * fix: fix for chat status test * feat: added verification option to show only on token gated groups * fix: fixing group access control * Check rules access control (#678) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * 662 group access control v2 changes (#663) * fix: spaces access API * fix: custom endpoint doc impl * Update README.md * Update README.md * fix: review comments change * fix: read me update * Update README.md * Update README.md * fix: origin in user creation (#665) * fix: origin in user creation * Update README.md * fix: review comments * 662 group access control v2 changes (#677) * fix: spaces access API * fix: custom endpoint doc impl * Update README.md * Update README.md * fix: review comments change * fix: read me update * Update README.md * Update README.md * fix(spaces): broadcast changes and UX fixes (#674) * fix: fix UI grid view (#642) * fix: fix UI grid view * refactor: conditional added * fix: resolved muting/unmuting try catch error (#657) * fix(spaces): fix livepeer broadcast (#656) * fix(spaces): fix livepeer broadcast * fix(spaces): fix data shown for unjoined space & promote listener logic --------- Co-authored-by: Madhur Gupta * refactor(spaces): use local state instead of get from server while meta message fire (#676) --------- Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Nilesh Gupta * feat: added verification option to show only on token gated groups * fix: fixing group access control --------- Co-authored-by: Monalisha Mishra Co-authored-by: Mohammed S Co-authored-by: Madhur Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Nilesh Gupta * fix: fixed build * feat: added send request to join group * fix: fixed verification on send * fix: fixed verify access btn not showing after joining group * fix: fixed join group showing for members too * fix: fixed join group showing to members * 667 chat.send enhancement (#673) * fix: changes messageObj for meta and reaction message type * fix: enhance send fn * fix: fixed intent issues * 680 group rules cosmetic changes (#682) * fix: fixed guild condition * fix: add did validation * fix: fixed * fix: fixed minor issues * fix: fixe dissue * fix: fixed minor issues * fix: check for scroll * fix: fixed scrolling * fix: fixed theme * fix: fixed * fix: fixed * fix: fixed * fix: fixed issues * fix: fixed * feat: added toast in join group btn for pvt groups * fix: fixed blurr --------- Co-authored-by: Monalisha Mishra Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> Co-authored-by: Kolade Co-authored-by: Nilesh Gupta Co-authored-by: Mohammed S Co-authored-by: Madhur Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Aman Gupta * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.3 * Join group option (#687) * feat: created architechture * fix: added context values (#594) * Chat dataprovider (#596) * feat: data provider for chat component * fix: replaced react.usestate to usestate * fix: added props as the initial state and changed state name * fix: reverted chat context changes and renamed values * fix: added test page for chat ui components (#597) * added chatbubble component (#602) * feat: added chatbubble component * fix: made the messageBubble's width to fit-content --------- Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> * fix: added theme * Group chat bubble (#604) * feat: moved test component to testui folder & replaced dummy data with sdk response * feat: added twitter card and address for group chat received msg * feat: made the messageaddress reusable, added account from context * fix: removed unnecessary div and unused props and console logs * feat: adding pfp in text bubbles * fix: replaced hook with function and added pfp to messagebubble * fix: fixed image alignment * fix: changed border-radius of msg bubble and changed function name * fix: fixed theme and decryptedPrivateKey name (#616) * fix: fixed theme and decryptedPrivateKey name * fix: fixed bug * fix: fixed theme reviews * Message list (#615) * fix: message list comp * fix: message list comp * fix: added pagination * fix: added pagination * fix: pagination * fix: create useChatData hook * fix: fixed minor bug * fix: socket issues fixed * fix: added theme in msgbubble (#620) * fix: added theme in msgbubble * fix: fixed import --------- Co-authored-by: Monalisha Mishra * fix: exported the theme (#623) * fix: exported the theme * fix: fixed issues --------- Co-authored-by: Monalisha Mishra * Typebar component (#631) * feat: added typebar UI * feat: added functions to typebar * fix: added icon * fix: fixed theme issues --------- Co-authored-by: Monalisha Mishra * feat: added connectbutton * fix: fixed connectbtn ui and remove disconnect and fixed error on disconnect * fix: fixed create account getting called twice * Profile Header Component (#636) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * Message container (#635) * fix: added theme in msgbubble * fix: fixed import * fix: fixed message-list * fix: added approve intent * fix: added fixes * fix: fixed socket bug * fix: fixed message from socket * fix: fixed minor issues * fix: fixed typebar theming * fix: fixed env issue * fix: fixed message not updating issue * refactor: added isConnected prop in msgContainer * refactor: resolve merge conflicts * fix: fixed request sending * fix: fixed decryption * fix: fixed env issue * feat: added profile header in message container --------- Co-authored-by: KlausMikhaelson Co-authored-by: Nilesh Gupta * fix: updated svg to react component * fix: changed svg to tsx component * fix: fixed review changes (#646) * fix: fixed review changes * fix: resolved issues --------- Co-authored-by: Nilesh Gupta * Profile Header -> Chat Profile fixes (#647) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * fix: notifs * fix: correct theme colors * fix: renaming profileHeader * fix: qa fixes * fix: loader * refactor: resolved issues * fix: edit types --------- Co-authored-by: Nilesh Gupta * fix: resolved package not added issue * Fix: modal issue in group info && alert remove members (#653) * fix: modal * fix: update changes * refactor: resolved issue --------- Co-authored-by: Nilesh Gupta * fix: QA fixes (#654) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * Connect btn revamp (#668) * fix: added theme in msgbubble * fix: fixed import * fix: fixed message-list * fix: added approve intent * fix: added fixes * fix: fixed socket bug * fix: fixed message from socket * fix: fixed minor issues * fix: fixed typebar theming * fix: fixed env issue * fix: fixed message not updating issue * refactor: added isConnected prop in msgContainer * refactor: resolve merge conflicts * fix: fixed request sending * fix: fixed decryption * fix: fixed env issue * feat: added profile header in message container * feat: adding rainbowkit for btn * fix: fix issues * fix: addec onnect functionality * fix: connect button * fix: added hack for rainbowkit css --------- Co-authored-by: KlausMikhaelson Co-authored-by: Nilesh Gupta * Typebar UI issue (#666) * fix: fixed typebar space not working and theme * fix: fixed gif and emoji --------- Co-authored-by: KlausMikhaelson * fix: fixed theme colours * fix: added theme for brb * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * fix: added filter hat * fix: merged with main * fix: fixed msg border * fix: fixed theme * fix: fixed fonts * fix: fixed bug * Access control (#672) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * fix: added minor fix --------- Co-authored-by: Monalisha Mishra * fix: fixed padding issues * fix(f): fixed build issues * fix: fix for chat status test * feat: added verification option to show only on token gated groups * fix: fixing group access control * Check rules access control (#678) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * 662 group access control v2 changes (#663) * fix: spaces access API * fix: custom endpoint doc impl * Update README.md * Update README.md * fix: review comments change * fix: read me update * Update README.md * Update README.md * fix: origin in user creation (#665) * fix: origin in user creation * Update README.md * fix: review comments * 662 group access control v2 changes (#677) * fix: spaces access API * fix: custom endpoint doc impl * Update README.md * Update README.md * fix: review comments change * fix: read me update * Update README.md * Update README.md * fix(spaces): broadcast changes and UX fixes (#674) * fix: fix UI grid view (#642) * fix: fix UI grid view * refactor: conditional added * fix: resolved muting/unmuting try catch error (#657) * fix(spaces): fix livepeer broadcast (#656) * fix(spaces): fix livepeer broadcast * fix(spaces): fix data shown for unjoined space & promote listener logic --------- Co-authored-by: Madhur Gupta * refactor(spaces): use local state instead of get from server while meta message fire (#676) --------- Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Nilesh Gupta * feat: added verification option to show only on token gated groups * fix: fixing group access control --------- Co-authored-by: Monalisha Mishra Co-authored-by: Mohammed S Co-authored-by: Madhur Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Nilesh Gupta * fix: fixed build * feat: added send request to join group * fix: fixed verification on send * fix: fixed verify access btn not showing after joining group * fix: fixed join group showing for members too * fix: fixed join group showing to members * 667 chat.send enhancement (#673) * fix: changes messageObj for meta and reaction message type * fix: enhance send fn * fix: fixed intent issues * 680 group rules cosmetic changes (#682) * fix: fixed guild condition * fix: add did validation * fix: fixed * fix: fixed minor issues * fix: fixe dissue * fix: fixed minor issues * fix: check for scroll * fix: fixed scrolling * fix: fixed theme * fix: fixed * fix: fixed * fix: fixed * fix: fixed issues * fix: fixed * feat: added toast in join group btn for pvt groups * fix: fixed blurr * fix: fixed minor issues rules * fix: fixed * fix: fixed --------- Co-authored-by: Satyam <100528412+KlausMikhaelson@users.noreply.github.com> Co-authored-by: KlausMikhaelson Co-authored-by: Kolade Co-authored-by: Nilesh Gupta Co-authored-by: Mohammed S Co-authored-by: Madhur Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Aman Gupta * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.4 * Join group option (#688) * feat: created architechture * fix: added context values (#594) * Chat dataprovider (#596) * feat: data provider for chat component * fix: replaced react.usestate to usestate * fix: added props as the initial state and changed state name * fix: reverted chat context changes and renamed values * fix: added test page for chat ui components (#597) * added chatbubble component (#602) * feat: added chatbubble component * fix: made the messageBubble's width to fit-content --------- Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> * fix: added theme * Group chat bubble (#604) * feat: moved test component to testui folder & replaced dummy data with sdk response * feat: added twitter card and address for group chat received msg * feat: made the messageaddress reusable, added account from context * fix: removed unnecessary div and unused props and console logs * feat: adding pfp in text bubbles * fix: replaced hook with function and added pfp to messagebubble * fix: fixed image alignment * fix: changed border-radius of msg bubble and changed function name * fix: fixed theme and decryptedPrivateKey name (#616) * fix: fixed theme and decryptedPrivateKey name * fix: fixed bug * fix: fixed theme reviews * Message list (#615) * fix: message list comp * fix: message list comp * fix: added pagination * fix: added pagination * fix: pagination * fix: create useChatData hook * fix: fixed minor bug * fix: socket issues fixed * fix: added theme in msgbubble (#620) * fix: added theme in msgbubble * fix: fixed import --------- Co-authored-by: Monalisha Mishra * fix: exported the theme (#623) * fix: exported the theme * fix: fixed issues --------- Co-authored-by: Monalisha Mishra * Typebar component (#631) * feat: added typebar UI * feat: added functions to typebar * fix: added icon * fix: fixed theme issues --------- Co-authored-by: Monalisha Mishra * feat: added connectbutton * fix: fixed connectbtn ui and remove disconnect and fixed error on disconnect * fix: fixed create account getting called twice * Profile Header Component (#636) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * Message container (#635) * fix: added theme in msgbubble * fix: fixed import * fix: fixed message-list * fix: added approve intent * fix: added fixes * fix: fixed socket bug * fix: fixed message from socket * fix: fixed minor issues * fix: fixed typebar theming * fix: fixed env issue * fix: fixed message not updating issue * refactor: added isConnected prop in msgContainer * refactor: resolve merge conflicts * fix: fixed request sending * fix: fixed decryption * fix: fixed env issue * feat: added profile header in message container --------- Co-authored-by: KlausMikhaelson Co-authored-by: Nilesh Gupta * fix: updated svg to react component * fix: changed svg to tsx component * fix: fixed review changes (#646) * fix: fixed review changes * fix: resolved issues --------- Co-authored-by: Nilesh Gupta * Profile Header -> Chat Profile fixes (#647) * feat: profile header * feat: update profile header * fix: update hooks * fix: video icon ui * feat: add group modal * fix: add modal info * fix: edit components * fix: commit modal theme * fix: updating UI * fix: ensname * fix: add notifs * fix: remove alerts * fix: remove alert logs * fix: push fixes * fix: conflicts * fix: notifs * fix: correct theme colors * fix: renaming profileHeader * fix: qa fixes * fix: loader * refactor: resolved issues * fix: edit types --------- Co-authored-by: Nilesh Gupta * fix: resolved package not added issue * Fix: modal issue in group info && alert remove members (#653) * fix: modal * fix: update changes * refactor: resolved issue --------- Co-authored-by: Nilesh Gupta * fix: QA fixes (#654) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * Connect btn revamp (#668) * fix: added theme in msgbubble * fix: fixed import * fix: fixed message-list * fix: added approve intent * fix: added fixes * fix: fixed socket bug * fix: fixed message from socket * fix: fixed minor issues * fix: fixed typebar theming * fix: fixed env issue * fix: fixed message not updating issue * refactor: added isConnected prop in msgContainer * refactor: resolve merge conflicts * fix: fixed request sending * fix: fixed decryption * fix: fixed env issue * feat: added profile header in message container * feat: adding rainbowkit for btn * fix: fix issues * fix: addec onnect functionality * fix: connect button * fix: added hack for rainbowkit css --------- Co-authored-by: KlausMikhaelson Co-authored-by: Nilesh Gupta * Typebar UI issue (#666) * fix: fixed typebar space not working and theme * fix: fixed gif and emoji --------- Co-authored-by: KlausMikhaelson * fix: fixed theme colours * fix: added theme for brb * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * fix: added filter hat * fix: merged with main * fix: fixed msg border * fix: fixed theme * fix: fixed fonts * fix: fixed bug * Access control (#672) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * fix: added minor fix --------- Co-authored-by: Monalisha Mishra * fix: fixed padding issues * fix(f): fixed build issues * fix: fix for chat status test * feat: added verification option to show only on token gated groups * fix: fixing group access control * Check rules access control (#678) * fix: fixed typebar space not working and theme * feat: created access control UI * feat: created verify access control hook * fix: fixed access control hook issue * feat: added access control verification * feat: added onclick props for integration team to pass function and fixed btn ui * fix: added link for learn more * fix: fixed msg not updating in socket issue * 662 group access control v2 changes (#663) * fix: spaces access API * fix: custom endpoint doc impl * Update README.md * Update README.md * fix: review comments change * fix: read me update * Update README.md * Update README.md * fix: origin in user creation (#665) * fix: origin in user creation * Update README.md * fix: review comments * 662 group access control v2 changes (#677) * fix: spaces access API * fix: custom endpoint doc impl * Update README.md * Update README.md * fix: review comments change * fix: read me update * Update README.md * Update README.md * fix(spaces): broadcast changes and UX fixes (#674) * fix: fix UI grid view (#642) * fix: fix UI grid view * refactor: conditional added * fix: resolved muting/unmuting try catch error (#657) * fix(spaces): fix livepeer broadcast (#656) * fix(spaces): fix livepeer broadcast * fix(spaces): fix data shown for unjoined space & promote listener logic --------- Co-authored-by: Madhur Gupta * refactor(spaces): use local state instead of get from server while meta message fire (#676) --------- Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Nilesh Gupta * feat: added verification option to show only on token gated groups * fix: fixing group access control --------- Co-authored-by: Monalisha Mishra Co-authored-by: Mohammed S Co-authored-by: Madhur Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Nilesh Gupta * fix: fixed build * feat: added send request to join group * fix: fixed verification on send * fix: fixed verify access btn not showing after joining group * fix: fixed join group showing for members too * fix: fixed join group showing to members * 667 chat.send enhancement (#673) * fix: changes messageObj for meta and reaction message type * fix: enhance send fn * fix: fixed intent issues * 680 group rules cosmetic changes (#682) * fix: fixed guild condition * fix: add did validation * fix: fixed * fix: fixed minor issues * fix: fixe dissue * fix: fixed minor issues * fix: check for scroll * fix: fixed scrolling * fix: fixed theme * fix: fixed * fix: fixed * fix: fixed * fix: fixed issues * fix: fixed * feat: added toast in join group btn for pvt groups * fix: fixed blurr * fix: fixed minor issues rules * fix: fixed * fix: fixed * fix: fixed socket issue * fix: fixed --------- Co-authored-by: Satyam <100528412+KlausMikhaelson@users.noreply.github.com> Co-authored-by: KlausMikhaelson Co-authored-by: Kolade Co-authored-by: Nilesh Gupta Co-authored-by: Mohammed S Co-authored-by: Madhur Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Aman Gupta * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.5 * fix: fixed (#689) * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.6 * chat Load issue fixed (#690) * fix: fixed * fix: fixed minor issues * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.7 * fix: fixed msg bubble width (#691) * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.8 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.17 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.18 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.19 * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.33 * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.33 * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.9 * Onboarding blocknative (#702) * fix: added reusable folder * fix: fixed theme for modal * fix: removed reusables from export * fix: fixed fallback for message input * feat: replaced rainbowkit with blocknative * fix: removed unused code --------- Co-authored-by: Monalisha Mishra * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.10 * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.33 * Fix connect button issue (#710) * fix: fixed connect button blocknative * fix: fixed message fetching * fix: removed console * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.11 * fix: added condition while showing tokengatedIcon (#715) * fix: added disconnect for wallet (#721) * fix: added disconnect for wallet * fix: added autoConnect * Wallet disconnect (#722) * fix: added disconnect for wallet * fix: added autoConnect * fix: changed onClick to onGetTokenClick * Wallet disconnect (#723) * fix: added disconnect for wallet * fix: added autoConnect * fix: changed onClick to onGetTokenClick * fix: fixed errors * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.12 * Changed messageInput params Case (#725) * fix: added disconnect for wallet * fix: added autoConnect * fix: changed onClick to onGetTokenClick * fix: fixed errors * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.13 * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.20 * Wallet disconnect (#728) * fix: added disconnect for wallet * fix: added autoConnect * fix: changed onClick to onGetTokenClick * fix: fixed errors * fix: added coinbase wallet * fix: img added * fix: fixed metamask not showing if not present in it --------- Co-authored-by: KlausMikhaelson * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.14 * Wallet disconnect (#732) * fix: added disconnect for wallet * fix: added autoConnect * fix: changed onClick to onGetTokenClick * fix: fixed errors * fix: added coinbase wallet * fix: img added * fix: fixed metamask not showing if not present in it * fix: fixed injected wallets --------- Co-authored-by: KlausMikhaelson * ci(uiweb): šŸŽ‰ cut beta release to uiweb-v0.0.1-alpha.15 * fix: added class based implementation for notification (#699) * fix: added class based implementation for notification * fix: class based implementation for notification * fix: small fixes * fix: minor fixes and testcases * fix: fixes for testcases * fix: updated core abi and minor fixes * fix: removed comment * fix: minor fixes and additional checks * fix: minor fixes * fix: viem support for contract and new folder structure * fix: changed typescript 5.0.2 and configured eslint * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.34 * 720 pushstream class implementation (#729) * chore: added an example of automated chat using the new class based initialization * fix: fixed usecases * chore: added an example of automated chat using the new class based iā€¦ (#712) * chore: added an example of automated chat using the new class based initialization * fix: fixed usecases --------- Co-authored-by: aman035 * chore(automated chat example): tweaked automated chat example * fix: stream changes * fix: stream changes * fix: added chat.decrypt (#726) * fix: added chat.decrypt * fix: fix examples * fix: stream changes * fix: PUSH Stream Changes * fix: chat classes split * fix: Notification socket initialisations and rules backward compatibility * fix: added message decrypt * fix: review comments * fix: socket events * fix: test case fix and UserInfo to user * fix: rip PushNotification class * fix: additional check --------- Co-authored-by: harshrajat Co-authored-by: aman035 Co-authored-by: Harsh | Push Co-authored-by: akp111 * fix: remove only from test * fix: error fixed * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.35 * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.35 * fix: stream cases * 720 pushstream class implementation (#737) * fix: stream changes * fix: stream changes * fix: stream changes * fix: PUSH Stream Changes * fix: chat classes split * fix: Notification socket initialisations and rules backward compatibility * fix: added message decrypt * fix: review comments * fix: socket events * fix: test case fix and UserInfo to user * fix: rip PushNotification class * fix: additional check * fix: minor fixes --------- Co-authored-by: Mohammed S * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.21 * fix: corrected example * fix: some changes on stream * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.36 * fix: minor fixes * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.37 * fix: error handling in socket events * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.38 * fix: more fixes * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.39 * fix: minor fixes * fix: socket enabled * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.40 * fix: minor fixes * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.41 * Add counter for update channel (#740) * fix: added counter to update group * fix: added counter logic, moved delegate and alias function to channel class * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.42 * Add counter for update channel (#741) * fix: added counter to update group * fix: added counter logic, moved delegate and alias function to channel class * fix: added readme for notification * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.43 * fix: minor fix * ci(uiweb): šŸŽ‰ cut release to uiweb-v1.1.14 * fix: log removed * ci(restapi): šŸŽ‰ cut beta release to restapi-v0.0.1-alpha.44 * docs: fix tests * Feat/space backup (#746) * refactor: added code for joining livekit room for listeners (#731) * feat: drop in livekit inplace of livepeer (#736) * feat: drop in livekit inplace of livepeer * feat: added microphone * feat: added mic * feat: added access control --------- Co-authored-by: Nilesh Gupta * fix: Notification event mutation * Notification setting changes (#752) * Arbitrum changes (#735) * fix: inital implementation for arbitrum changes * fix: more changes * fix: added final changes * fix: minor fixes * fix: fixed typo * fix: notification settings related changes * fix: added index parameter for notification * fix: restored config * Arnab/livekit fixes (#754) * refactor: added code for joining livekit room for listeners (#731) * feat: drop in livekit inplace of livepeer * feat: added microphone * feat: added mic * feat: added access control * feat: fixes for livekit API call --------- Co-authored-by: Nilesh Gupta * ci(restapi): šŸŽ‰ cut release to restapi-v1.4.22 * fix: added settings * fix: added settings (#762) * fix: build fix * fix: minor fix * fix: minor fix * Create group chat comp (#766) * Chat profile refractor (#727) * fix: fixed minor bugs and made add wallets reusable * fix: refactored code * fix: fixed ens domains not working while adding more wallets to a group * fix: fixed back btn in add wallet and cofirm btn ui * fix: fixed theme in chat profile * fix: refractor of addWallet * fix: ens issues * fix: adde changes * fix: fixed lint issues * fix: fixed review comments * fix: fixed minor review comments --------- Co-authored-by: KlausMikhaelson * Refactoring of messageInput, chatBubble, connectButton,chatViewList, chatViewComponent (#718) * fix: added accoutn hook * fix: refactoring code of messageInput chatlist chatbubble * fix: fixed review comment * fix: fixed * fix: added reusables * fix: fixed toast * feat: create group modal * fix: fixed condition in messageInput * fix: fixed * fix: fixed code * feat: create group info validation done (#738) * fix: fixed autoconnect issues * Group criteria added (#764) * feat: created define criteria and add criteria * feat: added some more dropdowns in add criteria * fix: added critera options * feat: worked on criterias * Arbitrum changes (#735) * fix: inital implementation for arbitrum changes * fix: more changes * fix: added final changes * fix: fixed few ui issues * fix: fixed minor issue * fix: added operstor * fix: added dummy 2d data * fix: fixed test * feat: condition type added * feat: adder done * feat: all/any added on condition box * fix: minor fixes * fix: fixed typo * fix: fixed upload group image ui * Multiple group criteria (#747) * fix: fixed multiple criteria Ui * fix: fixed conditions code --------- Co-authored-by: KlausMikhaelson * docs: added Class Examples (#739) * docs: added documentation * docs: fixed chat class examples * docs: fix notif class * docs: fixed stream examples * docs: fix nft grp update * docs: fixed logs * Message Type Implementations (#730) * fix: added video & audio messages * fix: fixed meta, reaction & added intent & readReceipt * fix: added reply * fix: added composite, fixed receipt * fix: local tests (#744) * fix(env and index): fixes typo RECIPEINT to RECIPIENT (#743) fix #733 * Update README.md (#742) Changed the discord link * feat: add criteria done * fix: fixed themes * feat: rule deletion done * feat: condition update done * feat: add criteria update lablel done * feat: edit rule auto fill done * feat: pr changes done * feat: refactor type and hooks * fix: fixed mobilde view * fix: fixed scroll * feat: added icon in groupinfo to show if it is token gated or not (#748) * feat: added icon in groupinfo to show if it is token gated or not * fix: fixed minor text * feat: created group criteria info modal and fixed dropdown * fix: made changes as per the review * fix: removed more options from conditions in group info * fix: fixed minor issues --------- Co-authored-by: Monalisha Mishra * feat: drop down labels * fix: fixed * fix: fixed dropdown * Gp states fix (#763) * feat: conditions to chat added * feat: generation of payload done * refactor: rule generation done * feat: operator undefined fix * fix: fixed condition ui * feat: create group rest api call done * fix: fixed dropdown * feat: group detail info fix done * fix: added validation for guild --------- Co-authored-by: Abishek Bashyal --------- Co-authored-by: KlausMikhaelson Co-authored-by: Ashis Kumar Pradhan <38760485+akp111@users.noreply.github.com> Co-authored-by: Abishek Bashyal Co-authored-by: akp111 Co-authored-by: Aman Gupta Co-authored-by: strykerin Co-authored-by: Rahul Pandey Co-authored-by: dinesh <67892133+dinesh11515@users.noreply.github.com> Co-authored-by: Satyam <100528412+KlausMikhaelson@users.noreply.github.com> * feat: group msg fix (#765) * feat: group msg fix * fix: minor changes --------- Co-authored-by: Monalisha Mishra --------- Co-authored-by: KlausMikhaelson Co-authored-by: Abishek Bashyal Co-authored-by: Ashis Kumar Pradhan <38760485+akp111@users.noreply.github.com> Co-authored-by: akp111 Co-authored-by: Aman Gupta Co-authored-by: strykerin Co-authored-by: Rahul Pandey Co-authored-by: dinesh <67892133+dinesh11515@users.noreply.github.com> Co-authored-by: Satyam <100528412+KlausMikhaelson@users.noreply.github.com> * Sub response (#767) * fix: modified subscription response * fix: modified error response * Sub response (#770) * fix: modified subscription response * fix: modified error response * fix: added docs --------- Co-authored-by: Madhur Gupta Co-authored-by: Nilesh Gupta Co-authored-by: Arnab Chatterjee <60937304+arn4b@users.noreply.github.com> Co-authored-by: Samarendra Gouda <134079446+samarendra-push@users.noreply.github.com> Co-authored-by: Samarendra Gouda Co-authored-by: aman035 Co-authored-by: samarendra-push Co-authored-by: Monalisha Mishra Co-authored-by: Monalisha Mishra <42746736+mishramonalisha76@users.noreply.github.com> Co-authored-by: Satyam <100528412+KlausMikhaelson@users.noreply.github.com> Co-authored-by: KlausMikhaelson Co-authored-by: Kolade Co-authored-by: Ashis Kumar Pradhan <38760485+akp111@users.noreply.github.com> Co-authored-by: akp111 Co-authored-by: harshrajat Co-authored-by: Harsh | Push Co-authored-by: Abishek Bashyal Co-authored-by: strykerin Co-authored-by: Rahul Pandey Co-authored-by: dinesh <67892133+dinesh11515@users.noreply.github.com> --- .../sdk-backend-node/pushAPI/channel.ts | 192 +++ .../examples/sdk-backend-node/pushAPI/chat.ts | 227 +++ .../sdk-backend-node/pushAPI/encryption.ts | 38 + .../sdk-backend-node/pushAPI/notification.ts | 58 + .../sdk-backend-node/pushAPI/profile.ts | 38 + .../sdk-backend-node/pushAPI/stream.ts | 202 +++ .../src/app/ChatUITest/ChatProfile.tsx | 2 +- .../src/app/ChatUITest/ChatViewComponent.tsx | 9 +- .../sdk-frontend-react/src/app/app.tsx | 2 +- .../restapi/PushNotificationLowLevelAPI.md | 70 +- packages/restapi/README.md | 99 +- packages/restapi/package.json | 2 +- packages/restapi/project.json | 2 +- packages/restapi/src/lib/channels/index.ts | 4 +- .../src/lib/channels/signature.helpers.ts | 62 +- .../restapi/src/lib/channels/subscribeV2.ts | 111 ++ .../restapi/src/lib/channels/unsubscribeV2.ts | 110 ++ packages/restapi/src/lib/payloads/helpers.ts | 3 + .../pushNotification/PushNotificationTypes.ts | 26 +- .../src/lib/pushNotification/notification.ts | 13 +- .../pushNotification/pushNotificationBase.ts | 47 + packages/restapi/src/lib/types/index.ts | 1 + .../lib/channel/sendNotification.test.ts | 70 + .../tests/lib/channel/subscribeV2.test.ts | 56 + .../tests/lib/pushNotification/alias.test.ts | 2 +- .../lib/pushNotification/notification.test.ts | 2 +- .../lib/pushNotification/onchain.test.ts | 161 +- .../chat/ChatProfile/AddWalletContent.tsx | 525 +++--- .../chat/ChatProfile/ChatProfile.tsx | 410 ++--- .../chat/ChatProfile/GroupInfoModal.tsx | 1506 ++++++++++------- .../chat/ChatProfile/MemberListContainer.tsx | 294 ++-- .../chat/ChatProfile/MemberProfileCard.tsx | 154 ++ .../chat/ChatProfile/ProfileCard.tsx | 158 -- .../chat/ChatViewBubble/ChatViewBubble.tsx | 367 ++-- .../chat/ChatViewList/ChatViewList.tsx | 18 +- .../chat/ConnectButton/ConnectButton.tsx | 55 +- .../components/chat/ConnectButton/index.tsx | 33 +- .../chat/CreateGroup/AddButtons.tsx | 27 + .../chat/CreateGroup/AddCriteria.tsx | 557 ++++++ .../chat/CreateGroup/ConditionsComponent.tsx | 322 ++++ .../chat/CreateGroup/CreateGroupModal.tsx | 341 ++++ .../chat/CreateGroup/CreateGroupType.tsx | 275 +++ .../chat/CreateGroup/DefineCondition.tsx | 159 ++ .../chat/CreateGroup/OperatorContainer.tsx | 46 + .../lib/components/chat/CreateGroup/index.ts | 1 + .../chat/MessageInput/MessageInput.tsx | 634 ++++--- .../chat/MessageInput/VerificationFailed.tsx | 107 -- .../chat/ProfileHeader/AddWalletContent.tsx | 354 ---- .../lib/components/chat/constants/index.ts | 56 + .../src/lib/components/chat/exportedTypes.ts | 45 +- .../src/lib/components/chat/helpers/Modal.tsx | 101 -- .../src/lib/components/chat/helpers/Toast.tsx | 190 --- .../src/lib/components/chat/helpers/group.ts | 56 + .../src/lib/components/chat/helpers/helper.ts | 59 +- .../src/lib/components/chat/helpers/index.ts | 8 +- .../chat/helpers/tokenGatedGroup.ts | 67 + .../uiweb/src/lib/components/chat/index.ts | 1 + .../lib/components/chat/reusables/Button.tsx | 106 ++ .../chat/reusables/ChatSearchInput.tsx | 64 +- .../components/chat/reusables/Checkbox.tsx | 48 + .../{ChatProfile => reusables}/DropDown.tsx | 117 +- .../chat/reusables/DropDownInput.tsx | 116 ++ .../lib/components/chat/reusables/Modal.tsx | 138 ++ .../chat/{helpers => reusables}/NewToast.tsx | 48 +- .../chat/reusables/OptionButtons.tsx | 125 ++ .../chat/reusables/ProfileContainer.tsx | 47 + .../chat/reusables/QuantityInput.tsx | 117 ++ .../components/chat/reusables/TextArea.tsx | 82 + .../components/chat/reusables/TextInput.tsx | 107 ++ .../components/chat/reusables/ToggleInput.tsx | 114 ++ .../lib/components/chat/reusables/index.ts | 15 +- .../src/lib/components/chat/theme/index.ts | 118 +- .../src/lib/components/chat/types/index.ts | 76 + .../chat/types/tokenGatedGroupCreationType.ts | 75 + .../space/SpaceWidget/LiveWidgetContent.tsx | 4 +- packages/uiweb/src/lib/config/constants.ts | 73 +- packages/uiweb/src/lib/helpers/chat/search.ts | 54 +- packages/uiweb/src/lib/hooks/chat/index.ts | 2 + .../uiweb/src/lib/hooks/chat/useAccount.ts | 35 + .../src/lib/hooks/chat/useCreateGatedGroup.ts | 43 + .../src/lib/hooks/chat/useCriteriaState.ts | 228 +++ .../src/lib/hooks/chat/usePushSendMessage.ts | 2 +- .../src/lib/hooks/chat/useUpdateGroup.ts | 52 + .../lib/hooks/chat/useVerifyAccessControl.ts | 2 +- packages/uiweb/src/lib/hooks/index.ts | 4 +- .../src/lib/hooks/useCreateChatProfile.ts | 33 + .../uiweb/src/lib/hooks/useDecryptPGPKey.ts | 39 + packages/uiweb/src/lib/icons/Arbitrum.tsx | 20 + packages/uiweb/src/lib/icons/Back.tsx | 6 +- packages/uiweb/src/lib/icons/Bsc.tsx | 17 + packages/uiweb/src/lib/icons/CaretUp.svg | 4 +- packages/uiweb/src/lib/icons/EditSvg.svg | 4 + packages/uiweb/src/lib/icons/Ethereum.tsx | 21 + packages/uiweb/src/lib/icons/MoreDark.tsx | 8 +- packages/uiweb/src/lib/icons/PolygonIcon.tsx | 16 + packages/uiweb/src/lib/icons/RemoveSvg.svg | 4 + packages/uiweb/src/lib/icons/SpamIcon.tsx | 8 + .../uiweb/src/lib/icons/TokenGatedIcon.svg | 3 + packages/uiweb/src/lib/icons/bsc.svg | 4 + packages/uiweb/src/lib/icons/ethereum.svg | 9 + packages/uiweb/src/lib/icons/optimisim.svg | 12 + packages/uiweb/src/lib/icons/polygon.svg | 13 + packages/uiweb/yarn.lock | 1 - 103 files changed, 7520 insertions(+), 3249 deletions(-) create mode 100644 packages/examples/sdk-backend-node/pushAPI/channel.ts create mode 100644 packages/examples/sdk-backend-node/pushAPI/chat.ts create mode 100644 packages/examples/sdk-backend-node/pushAPI/encryption.ts create mode 100644 packages/examples/sdk-backend-node/pushAPI/notification.ts create mode 100644 packages/examples/sdk-backend-node/pushAPI/profile.ts create mode 100644 packages/examples/sdk-backend-node/pushAPI/stream.ts create mode 100644 packages/restapi/src/lib/channels/subscribeV2.ts create mode 100644 packages/restapi/src/lib/channels/unsubscribeV2.ts create mode 100644 packages/restapi/tests/lib/channel/sendNotification.test.ts create mode 100644 packages/restapi/tests/lib/channel/subscribeV2.test.ts create mode 100644 packages/uiweb/src/lib/components/chat/ChatProfile/MemberProfileCard.tsx delete mode 100644 packages/uiweb/src/lib/components/chat/ChatProfile/ProfileCard.tsx create mode 100644 packages/uiweb/src/lib/components/chat/CreateGroup/AddButtons.tsx create mode 100644 packages/uiweb/src/lib/components/chat/CreateGroup/AddCriteria.tsx create mode 100644 packages/uiweb/src/lib/components/chat/CreateGroup/ConditionsComponent.tsx create mode 100644 packages/uiweb/src/lib/components/chat/CreateGroup/CreateGroupModal.tsx create mode 100644 packages/uiweb/src/lib/components/chat/CreateGroup/CreateGroupType.tsx create mode 100644 packages/uiweb/src/lib/components/chat/CreateGroup/DefineCondition.tsx create mode 100644 packages/uiweb/src/lib/components/chat/CreateGroup/OperatorContainer.tsx create mode 100644 packages/uiweb/src/lib/components/chat/CreateGroup/index.ts delete mode 100644 packages/uiweb/src/lib/components/chat/MessageInput/VerificationFailed.tsx delete mode 100644 packages/uiweb/src/lib/components/chat/ProfileHeader/AddWalletContent.tsx create mode 100644 packages/uiweb/src/lib/components/chat/constants/index.ts delete mode 100644 packages/uiweb/src/lib/components/chat/helpers/Modal.tsx delete mode 100644 packages/uiweb/src/lib/components/chat/helpers/Toast.tsx create mode 100644 packages/uiweb/src/lib/components/chat/helpers/group.ts create mode 100644 packages/uiweb/src/lib/components/chat/helpers/tokenGatedGroup.ts create mode 100644 packages/uiweb/src/lib/components/chat/reusables/Button.tsx create mode 100644 packages/uiweb/src/lib/components/chat/reusables/Checkbox.tsx rename packages/uiweb/src/lib/components/chat/{ChatProfile => reusables}/DropDown.tsx (66%) create mode 100644 packages/uiweb/src/lib/components/chat/reusables/DropDownInput.tsx create mode 100644 packages/uiweb/src/lib/components/chat/reusables/Modal.tsx rename packages/uiweb/src/lib/components/chat/{helpers => reusables}/NewToast.tsx (83%) create mode 100644 packages/uiweb/src/lib/components/chat/reusables/OptionButtons.tsx create mode 100644 packages/uiweb/src/lib/components/chat/reusables/ProfileContainer.tsx create mode 100644 packages/uiweb/src/lib/components/chat/reusables/QuantityInput.tsx create mode 100644 packages/uiweb/src/lib/components/chat/reusables/TextArea.tsx create mode 100644 packages/uiweb/src/lib/components/chat/reusables/TextInput.tsx create mode 100644 packages/uiweb/src/lib/components/chat/reusables/ToggleInput.tsx create mode 100644 packages/uiweb/src/lib/components/chat/types/index.ts create mode 100644 packages/uiweb/src/lib/components/chat/types/tokenGatedGroupCreationType.ts create mode 100644 packages/uiweb/src/lib/hooks/chat/useAccount.ts create mode 100644 packages/uiweb/src/lib/hooks/chat/useCreateGatedGroup.ts create mode 100644 packages/uiweb/src/lib/hooks/chat/useCriteriaState.ts create mode 100644 packages/uiweb/src/lib/hooks/chat/useUpdateGroup.ts create mode 100644 packages/uiweb/src/lib/hooks/useCreateChatProfile.ts create mode 100644 packages/uiweb/src/lib/hooks/useDecryptPGPKey.ts create mode 100644 packages/uiweb/src/lib/icons/Arbitrum.tsx create mode 100644 packages/uiweb/src/lib/icons/Bsc.tsx create mode 100644 packages/uiweb/src/lib/icons/EditSvg.svg create mode 100644 packages/uiweb/src/lib/icons/Ethereum.tsx create mode 100644 packages/uiweb/src/lib/icons/PolygonIcon.tsx create mode 100644 packages/uiweb/src/lib/icons/RemoveSvg.svg create mode 100644 packages/uiweb/src/lib/icons/SpamIcon.tsx create mode 100644 packages/uiweb/src/lib/icons/TokenGatedIcon.svg create mode 100644 packages/uiweb/src/lib/icons/bsc.svg create mode 100644 packages/uiweb/src/lib/icons/ethereum.svg create mode 100644 packages/uiweb/src/lib/icons/optimisim.svg create mode 100644 packages/uiweb/src/lib/icons/polygon.svg diff --git a/packages/examples/sdk-backend-node/pushAPI/channel.ts b/packages/examples/sdk-backend-node/pushAPI/channel.ts new file mode 100644 index 000000000..7e6b236a0 --- /dev/null +++ b/packages/examples/sdk-backend-node/pushAPI/channel.ts @@ -0,0 +1,192 @@ +import { PushAPI } from '@pushprotocol/restapi'; +import { config } from '../config'; +import { ethers } from 'ethers'; + +// CONFIGS +const { env, showAPIResponse } = config; + +export const runPushAPIChannelCases = async (): Promise => { + if (!process.env.WALLET_PRIVATE_KEY) { + console.log( + 'skipping PushAPI.channel examples, no private key passed in .env' + ); + return; + } + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + // Signer Generation + const provider = new ethers.providers.JsonRpcProvider( + 'https://goerli.blockpi.network/v1/rpc/public' // Goerli Provider + ); + const signer = new ethers.Wallet( + `0x${process.env.WALLET_PRIVATE_KEY}`, + provider + ); + const randomWallet1 = ethers.Wallet.createRandom().address; + const randomWallet2 = ethers.Wallet.createRandom().address; + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + const userAlice = await PushAPI.initialize(signer, { env }); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.info'); + const channelInfo = await userAlice.channel.info(); + if (showAPIResponse) { + console.log(channelInfo); + } + console.log('PushAPI.channel.info | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.search'); + const searchedChannels = await userAlice.channel.search( + 'push' // search by name or address + ); + if (showAPIResponse) { + console.log(searchedChannels); + } + console.log('PushAPI.channel.search | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.subscribers'); + const channelSubscribers = await userAlice.channel.subscribers(); + if (showAPIResponse) { + console.log(channelSubscribers); + } + console.log('PushAPI.channel.subscribers | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.send'); + if (channelInfo) { + const broadcastNotif = await userAlice.channel.send(['*'], { + notification: { + title: 'test', + body: 'test', + }, + }); + const targetedNotif = await userAlice.channel.send([randomWallet1], { + notification: { + title: 'test', + body: 'test', + }, + }); + const subsetNotif = await userAlice.channel.send( + [randomWallet1, randomWallet2], + { + notification: { + title: 'test', + body: 'test', + }, + } + ); + if (showAPIResponse) { + console.log(broadcastNotif, targetedNotif, subsetNotif); + } + console.log('PushAPI.channel.send | Response - 200 OK\n\n'); + } else { + console.log( + 'skipping PushAPI.channel.send as no channel exists with the signer\n\n' + ); + } + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + // These Examples requires wallet to hold some ETH & PUSH + const balance = await provider.getBalance(signer.address); + if (parseFloat(ethers.utils.formatEther(balance)) < 0.001) { + console.log( + 'skipping PushAPI.channel examples, wallet does not have enough balance to pay fee' + ); + } + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.create'); + if (channelInfo) { + console.log('skipping PushAPI.channel.create as it already exists\n\n'); + } else { + const createdChannel = await userAlice.channel.create({ + name: 'Test Channel', + description: 'Test Description', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAz0lEQVR4AcXBsU0EQQyG0e+saWJ7oACiKYDMEZVs6GgSpC2BIhzRwAS0sgk9HKn3gpFOAv3v3V4/3+4U4Z1q5KTy42Ql940qvFONnFSGmCFmiN2+fj7uCBlihpgh1ngwcvKfwjuVIWaIGWKNB+GdauSk8uNkJfeNKryzYogZYoZY40m5b/wlQ8wQM8TayMlKeKcaOVkJ71QjJyuGmCFmiDUe+HFy4VyEd57hx0mV+0ZliBlihlgL71w4FyMnVXhnZeSkiu93qheuDDFDzBD7BcCyMAOfy204AAAAAElFTkSuQmCC', + url: 'https://push.org', + }); + if (showAPIResponse) { + console.log(createdChannel); + } + console.log('PushAPI.channel.create | Response - 200 OK\n\n'); + } + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.update'); + const updatedChannel = await userAlice.channel.update({ + name: 'Updated Name', + description: 'Testing new description', + url: 'https://google.com', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAz0lEQVR4AcXBsU0EQQyG0e+saWJ7oACiKYDMEZVs6GgSpC2BIhzRwAS0sgk9HKn3gpFOAv3v3V4/3+4U4Z1q5KTy42Ql940qvFONnFSGmCFmiN2+fj7uCBlihpgh1ngwcvKfwjuVIWaIGWKNB+GdauSk8uNkJfeNKryzYogZYoZY40m5b/wlQ8wQM8TayMlKeKcaOVkJ71QjJyuGmCFmiDUe+HFy4VyEd57hx0mV+0ZliBlihlgL71w4FyMnVXhnZeSkiu93qheuDDFDzBD7BcCyMAOfy204AAAAAElFTkSuQmCC', + }); + if (showAPIResponse) { + console.log(updatedChannel); + } + console.log('PushAPI.channel.update | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.verify'); + // only verified channels can verify other channels (otherwise this action is skipped by sdk) + if (channelInfo.verified_status) { + const verifiedTrx = await userAlice.channel.verify( + '0x35B84d6848D16415177c64D64504663b998A6ab4' + ); + if (showAPIResponse) { + console.log(verifiedTrx); + } + } + console.log('PushAPI.channel.verify | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.setting'); + const channelSettingTrx = await userAlice.channel.setting([ + { type: 0, default: 1, description: 'My Notif Settings' }, + ]); + if (showAPIResponse) { + console.log(channelSettingTrx); + } + console.log('PushAPI.channel.setting | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.delegate.add'); + const addedDelegate = await userAlice.channel.delegate.add( + `eip155:5:${randomWallet1}` + ); + + if (showAPIResponse) { + console.log(addedDelegate); + } + console.log('PushAPI.channel.delegate.add | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.delegate.get'); + const delegates = await userAlice.channel.delegate.get(); + if (showAPIResponse) { + console.log(delegates); + } + console.log('PushAPI.channel.delegate.get | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.delegate.remove'); + const removedDelegate = await userAlice.channel.delegate.remove( + `eip155:5:${randomWallet1}` + ); + if (showAPIResponse) { + console.log(removedDelegate); + } + console.log('PushAPI.channel.delegate.remove | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.channel.alias.info'); + const aliasInfo = await userAlice.channel.alias.info({ + alias: '0x35B84d6848D16415177c64D64504663b998A6ab4', + aliasChain: 'POLYGON', + }); + if (showAPIResponse) { + console.log(aliasInfo); + } + console.log('PushAPI.channel.alias.info | Response - 200 OK\n\n'); +}; diff --git a/packages/examples/sdk-backend-node/pushAPI/chat.ts b/packages/examples/sdk-backend-node/pushAPI/chat.ts new file mode 100644 index 000000000..f34973ec1 --- /dev/null +++ b/packages/examples/sdk-backend-node/pushAPI/chat.ts @@ -0,0 +1,227 @@ +import { PushAPI } from '@pushprotocol/restapi'; +import { + adjectives, + animals, + colors, + uniqueNamesGenerator, +} from 'unique-names-generator'; +import { config } from '../config'; +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; +import { createWalletClient, http } from 'viem'; +import { goerli } from 'viem/chains'; + +// CONFIGS +const { env, showAPIResponse } = config; + +/***************** SAMPLE SIGNER GENERATION *********************/ +// Uing VIEM +// Random Wallet Signers +const signer = createWalletClient({ + account: privateKeyToAccount(generatePrivateKey()), + chain: goerli, + transport: http(), +}); +const signerAddress = signer.account.address; +const secondSigner = createWalletClient({ + account: privateKeyToAccount(generatePrivateKey()), + chain: goerli, + transport: http(), +}); +const secondSignerAddress = secondSigner.account.address; +const thirdSigner = createWalletClient({ + account: privateKeyToAccount(generatePrivateKey()), + chain: goerli, + transport: http(), +}); +const thirdSignerAddress = thirdSigner.account.address; + +// Dummy Wallet Addresses +const randomWallet1 = privateKeyToAccount(generatePrivateKey()).address; +const randomWallet2 = privateKeyToAccount(generatePrivateKey()).address; +const randomWallet3 = privateKeyToAccount(generatePrivateKey()).address; +/****************************************************************/ + +/***************** SAMPLE GROUP DATA ****************************/ +const groupName = uniqueNamesGenerator({ + dictionaries: [adjectives, colors, animals], +}); +const groupDescription = uniqueNamesGenerator({ + dictionaries: [adjectives, colors, animals], +}); +const groupImage = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAvklEQVR4AcXBsW2FMBiF0Y8r3GQb6jeBxRauYRpo4yGQkMd4A7kg7Z/GUfSKe8703fKDkTATZsJsrr0RlZSJ9r4RLayMvLmJjnQS1d6IhJkwE2bT13U/DBzp5BN73xgRZsJMmM1HOolqb/yWiWpvjJSUiRZWopIykTATZsJs5g+1N6KSMiO1N/5DmAkzYTa9Lh6MhJkwE2ZzSZlo7xvRwson3txERzqJhJkwE2bT6+JhoKTMJ2pvjAgzYSbMfgDlXixqjH6gRgAAAABJRU5ErkJggg=='; +/***************** SAMPLE GROUP DATA ****************************/ + +export const runPushAPIChatCases = async (): Promise => { + const userAlice = await PushAPI.initialize(signer, { env }); + const userBob = await PushAPI.initialize(secondSigner, { env }); + const tempUser = await PushAPI.initialize(thirdSigner, { env }); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.chat.list'); + const aliceChats = await userAlice.chat.list('CHATS'); + const aliceRequests = await userAlice.chat.list('REQUESTS'); + if (showAPIResponse) { + console.log(aliceChats); + console.log(aliceRequests); + } + console.log('PushAPI.chat.list | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.chat.latest'); + const aliceLatestChatWithBob = await userAlice.chat.latest( + secondSignerAddress + ); + if (showAPIResponse) { + console.log(aliceLatestChatWithBob); + } + console.log('PushAPI.chat.latest | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.chat.history'); + const aliceChatHistoryWithBob = await userAlice.chat.history( + secondSignerAddress + ); + if (showAPIResponse) { + console.log(aliceChatHistoryWithBob); + } + console.log('PushAPI.chat.history | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.chat.send'); + const aliceMessagesBob = await userAlice.chat.send(secondSignerAddress, { + content: 'Hello Bob!', + type: 'Text', + }); + if (showAPIResponse) { + console.log(aliceMessagesBob); + } + console.log('PushAPI.chat.send | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.chat.accept'); + const bobAcceptsRequest = await userBob.chat.accept(signerAddress); + if (showAPIResponse) { + console.log(bobAcceptsRequest); + } + console.log('PushAPI.chat.accept | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.chat.reject'); + await tempUser.chat.send(secondSignerAddress, { + content: 'Sending malicious message', + type: 'Text', + }); + const bobRejectsRequest = await userBob.chat.reject(thirdSignerAddress); + if (showAPIResponse) { + console.log(bobRejectsRequest); + } + console.log('PushAPI.chat.reject | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.chat.block'); + const AliceBlocksBob = await userAlice.chat.block([secondSignerAddress]); + if (showAPIResponse) { + console.log(AliceBlocksBob); + } + console.log('PushAPI.chat.block | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.chat.unblock'); + const AliceUnblocksBob = await userAlice.chat.unblock([secondSignerAddress]); + if (showAPIResponse) { + console.log(AliceUnblocksBob); + } + console.log('PushAPI.chat.unblock | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.group.create'); + const createdGroup = await userAlice.chat.group.create(groupName, { + description: groupDescription, + image: groupImage, + members: [randomWallet1, randomWallet2], + admins: [], + private: false, + }); + const groupChatId = createdGroup.chatId; // to be used in other examples + if (showAPIResponse) { + console.log(createdGroup); + } + console.log('PushAPI.group.create | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.group.permissions'); + const grouppermissions = await userAlice.chat.group.permissions(groupChatId); + if (showAPIResponse) { + console.log(grouppermissions); + } + console.log('PushAPI.group.permissions | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.group.info'); + const groupInfo = await userAlice.chat.group.info(groupChatId); + if (showAPIResponse) { + console.log(groupInfo); + } + console.log('PushAPI.group.info | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.group.update'); + const updatedGroup = await userAlice.chat.group.update(groupChatId, { + description: 'Updated Description', + }); + if (showAPIResponse) { + console.log(updatedGroup); + } + console.log('PushAPI.group.update | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.group.add'); + const addMember = await userAlice.chat.group.add(groupChatId, { + role: 'MEMBER', + accounts: [randomWallet3], + }); + if (showAPIResponse) { + console.log(addMember); + } + console.log('PushAPI.group.add | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.group.remove'); + const removeMember = await userAlice.chat.group.remove(groupChatId, { + role: 'MEMBER', + accounts: [randomWallet3], + }); + if (showAPIResponse) { + console.log(removeMember); + } + console.log('PushAPI.group.remove | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.group.join'); + const joinGrp = await userBob.chat.group.join(groupChatId); + if (showAPIResponse) { + console.log(joinGrp); + } + console.log('PushAPI.group.join | Response - 200 OK\n\n'); + //------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.group.leave'); + const leaveGrp = await userBob.chat.group.leave(groupChatId); + if (showAPIResponse) { + console.log(leaveGrp); + } + console.log('PushAPI.group.leave | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.group.reject'); + const sampleGrp = await userAlice.chat.group.create('Sample Grp', { + description: groupDescription, + image: groupImage, + members: [secondSignerAddress], // invite bob + admins: [], + private: true, + }); + await userBob.chat.group.reject(sampleGrp.chatId); + console.log('PushAPI.group.reject | Response - 200 OK\n\n'); +}; diff --git a/packages/examples/sdk-backend-node/pushAPI/encryption.ts b/packages/examples/sdk-backend-node/pushAPI/encryption.ts new file mode 100644 index 000000000..67300c65f --- /dev/null +++ b/packages/examples/sdk-backend-node/pushAPI/encryption.ts @@ -0,0 +1,38 @@ +import { PushAPI } from '@pushprotocol/restapi'; +import { config } from '../config'; +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; +import { createWalletClient, http } from 'viem'; +import { goerli } from 'viem/chains'; + +// CONFIGS +const { env, showAPIResponse } = config; + +/***************** SAMPLE SIGNER GENERATION *********************/ +// Uing VIEM +// Random Wallet Signers +const signer = createWalletClient({ + account: privateKeyToAccount(generatePrivateKey()), + chain: goerli, + transport: http(), +}); + +export const runPushAPIEncryptionCases = async (): Promise => { + const userAlice = await PushAPI.initialize(signer, { env }); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.encryption.info'); + const encryptionInfo = await userAlice.encryption.info(); + if (showAPIResponse) { + console.log(encryptionInfo); + } + console.log('PushAPI.encryption.info | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.encryption.update'); + const PGP_V3 = 'eip191-aes256-gcm-hkdf-sha256'; + const encryptionUpdate = await userAlice.encryption.update(PGP_V3 as any); + if (showAPIResponse) { + console.log(encryptionUpdate); + } + console.log('PushAPI.encryption.update | Response - 200 OK\n\n'); +}; diff --git a/packages/examples/sdk-backend-node/pushAPI/notification.ts b/packages/examples/sdk-backend-node/pushAPI/notification.ts new file mode 100644 index 000000000..579ea1e2b --- /dev/null +++ b/packages/examples/sdk-backend-node/pushAPI/notification.ts @@ -0,0 +1,58 @@ +import { PushAPI } from '@pushprotocol/restapi'; +import { config } from '../config'; +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; +import { createWalletClient, http } from 'viem'; +import { goerli } from 'viem/chains'; + +// CONFIGS +const { env, showAPIResponse } = config; + +/***************** SAMPLE SIGNER GENERATION *********************/ +// Uing VIEM +// Random Wallet Signers +const signer = createWalletClient({ + account: privateKeyToAccount(generatePrivateKey()), + chain: goerli, + transport: http(), +}); + +export const runPushAPINotificationCases = async (): Promise => { + const userAlice = await PushAPI.initialize(signer, { env }); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.notification.list'); + const inboxNotifications = await userAlice.notification.list('INBOX'); + const spamNotifications = await userAlice.notification.list('SPAM'); + if (showAPIResponse) { + console.log(inboxNotifications, spamNotifications); + } + console.log('PushAPI.notification.list | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.notification.subscribe'); + const subscribeResponse = await userAlice.notification.subscribe( + 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681' // channel to subscribe + ); + if (showAPIResponse) { + console.log(subscribeResponse); + } + console.log('PushAPI.notification.subscribe | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.notification.subscriptions'); + const aliceSubscriptions = await userAlice.notification.subscriptions(); + if (showAPIResponse) { + console.log(aliceSubscriptions); + } + console.log('PushAPI.notification.subscriptions | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.notification.unsubscribe'); + const unsubscribeResponse = await userAlice.notification.unsubscribe( + 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681' // channel to unsubscribe + ); + if (showAPIResponse) { + console.log(unsubscribeResponse); + } + console.log('PushAPI.notification.unsubscribe | Response - 200 OK\n\n'); +}; diff --git a/packages/examples/sdk-backend-node/pushAPI/profile.ts b/packages/examples/sdk-backend-node/pushAPI/profile.ts new file mode 100644 index 000000000..f72d93f1b --- /dev/null +++ b/packages/examples/sdk-backend-node/pushAPI/profile.ts @@ -0,0 +1,38 @@ +import { PushAPI } from '@pushprotocol/restapi'; +import { config } from '../config'; +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; +import { createWalletClient, http } from 'viem'; +import { goerli } from 'viem/chains'; + +// CONFIGS +const { env, showAPIResponse } = config; + +/***************** SAMPLE SIGNER GENERATION *********************/ +// Uing VIEM +// Random Wallet Signers +const signer = createWalletClient({ + account: privateKeyToAccount(generatePrivateKey()), + chain: goerli, + transport: http(), +}); + +export const runPushAPIProfileCases = async (): Promise => { + const userAlice = await PushAPI.initialize(signer, { env }); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.profile.info'); + const userAliceProfileInfo = await userAlice.profile.info(); + if (showAPIResponse) { + console.log(userAliceProfileInfo); + } + console.log('PushAPI.profile.info | Response - 200 OK\n\n'); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('PushAPI.profile.update'); + const updatedName = 'Bob The Builder'; + const response = await userAlice.profile.update({ name: updatedName }); + if (showAPIResponse) { + console.log(response); + } + console.log('PushAPI.profile.update | Response - 200 OK\n\n'); +}; diff --git a/packages/examples/sdk-backend-node/pushAPI/stream.ts b/packages/examples/sdk-backend-node/pushAPI/stream.ts new file mode 100644 index 000000000..e10c599c8 --- /dev/null +++ b/packages/examples/sdk-backend-node/pushAPI/stream.ts @@ -0,0 +1,202 @@ +import { PushAPI } from '@pushprotocol/restapi'; +import { config } from '../config'; +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; +import { createWalletClient, http } from 'viem'; +import { goerli } from 'viem/chains'; +import { STREAM } from '@pushprotocol/restapi/src/lib/pushstream/pushStreamTypes'; + +// CONFIGS +const { env, showAPIResponse } = config; + +/***************** SAMPLE SIGNER GENERATION *********************/ +// Uing VIEM +// Random Wallet Signers +const signer = createWalletClient({ + account: privateKeyToAccount(generatePrivateKey()), + chain: goerli, + transport: http(), +}); +const signerAddress = signer.account.address; +const secondSigner = createWalletClient({ + account: privateKeyToAccount(generatePrivateKey()), + chain: goerli, + transport: http(), +}); +const secondSignerAddress = secondSigner.account.address; +const thirdSigner = createWalletClient({ + account: privateKeyToAccount(generatePrivateKey()), + chain: goerli, + transport: http(), +}); +const thirdSignerAddress = thirdSigner.account.address; +// Dummy Wallet Addresses +const randomWallet1 = privateKeyToAccount(generatePrivateKey()).address; + +const eventlistener = async ( + pushAPI: PushAPI, + eventName: string +): Promise => { + pushAPI.stream.on(eventName, (data: any) => { + if (showAPIResponse) { + console.log(data); + } + }); +}; + +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +export const runPushAPIStreamCases = async (): Promise => { + const userAlice = await PushAPI.initialize(signer, { env }); + const userBob = await PushAPI.initialize(secondSigner, { env }); + const userKate = await PushAPI.initialize(thirdSigner, { env }); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log(`Listening ${STREAM.PROFILE} Events`); + eventlistener(userAlice, STREAM.PROFILE); + console.log(`Listening ${STREAM.ENCRYPTION} Events`); + eventlistener(userAlice, STREAM.ENCRYPTION); + console.log(`Listening ${STREAM.NOTIF} Events`); + eventlistener(userAlice, STREAM.NOTIF); + console.log(`Listening ${STREAM.NOTIF_OPS} Events`); + eventlistener(userAlice, STREAM.NOTIF_OPS); + console.log(`Listening ${STREAM.CHAT} Events`); + eventlistener(userAlice, STREAM.CHAT); + console.log(`Listening ${STREAM.CHAT_OPS} Events`); + eventlistener(userAlice, STREAM.CHAT_OPS); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('\n\nNew Chat Request, Expected Events:\n1. chat.request'); + await userAlice.chat.send(secondSignerAddress, { + content: 'Hello Bob! from Alice', + }); + await delay(3000); // Delay added to log the events in order + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('\n\nNew Chat Request, Expected Events:\n1. chat.request'); + await userAlice.chat.send(thirdSignerAddress, { + content: 'Hello Kate! from Alice', + }); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('\n\nChat Request Accept, Expected Events:\n1. chat.accept'); + await userBob.chat.accept(signerAddress); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('\n\nChat Request Reject, Expected Events:\n1. chat.reject'); + await userKate.chat.reject(signerAddress); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('\n\nCreate Chat Group, Expected Events:\n1. chat.group.create'); + const groupChatId = ( + await userAlice.chat.group.create('Test Grp', { + description: 'Test Desc', + image: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAvklEQVR4AcXBsW2FMBiF0Y8r3GQb6jeBxRauYRpo4yGQkMd4A7kg7Z/GUfSKe8703fKDkTATZsJsrr0RlZSJ9r4RLayMvLmJjnQS1d6IhJkwE2bT13U/DBzp5BN73xgRZsJMmM1HOolqb/yWiWpvjJSUiRZWopIykTATZsJs5g+1N6KSMiO1N/5DmAkzYTa9Lh6MhJkwE2ZzSZlo7xvRwson3txERzqJhJkwE2bT6+JhoKTMJ2pvjAgzYSbMfgDlXixqjH6gRgAAAABJRU5ErkJggg==', + members: [secondSignerAddress, thirdSignerAddress], + admins: [], + private: false, + }) + ).chatId; + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('\n\nUpdate Chat Group, Expected Events:\n1. chat.group.update'); + await userAlice.chat.group.update(groupChatId, { + description: 'Updated Test Desc', + image: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAvklEQVR4AcXBsW2FMBiF0Y8r3GQb6jeBxRauYRpo4yGQkMd4A7kg7Z/GUfSKe8703fKDkTATZsJsrr0RlZSJ9r4RLayMvLmJjnQS1d6IhJkwE2bT13U/DBzp5BN73xgRZsJMmM1HOolqb/yWiWpvjJSUiRZWopIykTATZsJs5g+1N6KSMiO1N/5DmAkzYTa9Lh6MhJkwE2ZzSZlo7xvRwson3txERzqJhJkwE2bT6+JhoKTMJ2pvjAgzYSbMfgDlXixqjH6gRgAAAABJRU5ErkJggg==', + }); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('\n\nAdd member to Group, Expected Events:\n1. chat.request'); + await userAlice.chat.group.add(groupChatId, { + role: 'MEMBER', + accounts: [randomWallet1], + }); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log( + '\n\nRemove member from Group, Expected Events:\n1. chat.group.participant.remove' + ); + await userAlice.chat.group.remove(groupChatId, { + role: 'MEMBER', + accounts: [randomWallet1], + }); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('\n\nAdd Admin to Group, Expected Events:\n1. chat.request'); + await userAlice.chat.group.add(groupChatId, { + role: 'ADMIN', + accounts: [randomWallet1], + }); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log( + '\n\nRemove Admin from Group, Expected Events:\n1. chat.group.participant.remove' + ); + await userAlice.chat.group.remove(groupChatId, { + role: 'ADMIN', + accounts: [randomWallet1], + }); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log('\n\nJoin Group, Expected Events:\n1. chat.accept'); + await userBob.chat.group.join(groupChatId); + await delay(3000); + //------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log( + '\n\nLeave Group, Expected Events:\n1. chat.group.participant.leave' + ); + await userBob.chat.group.leave(groupChatId); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + console.log( + '\n\nReject Group Joining Request, Expected Events:\n1. chat.reject' + ); + await userKate.chat.group.reject(groupChatId); + await delay(3000); + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + if (process.env.WALLET_PRIVATE_KEY) { + // create signer + const channelSigner = createWalletClient({ + account: privateKeyToAccount(`0x${process.env.WALLET_PRIVATE_KEY}`), + chain: goerli, + transport: http(), + }); + + await userAlice.notification.subscribe( + `eip155:5:${channelSigner.account.address}` // channel to subscribe + ); + + const channelUser = await PushAPI.initialize(channelSigner, { env }); + console.log( + '\n\nSend channel notification, Expected Events:\n1. notif.send' + ); + await channelUser.channel.send(['*'], { + notification: { + title: 'test', + body: 'test', + }, + }); + await delay(3000); + + await userAlice.notification.unsubscribe( + `eip155:5:${channelSigner.account.address}` // channel to subscribe + ); + } else { + console.log( + 'Skipping channel notification streams, as WALLET_PRIVATE_KEY is not present in .env' + ); + } +}; diff --git a/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatProfile.tsx b/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatProfile.tsx index b889f7daf..fc8c7eefb 100644 --- a/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatProfile.tsx +++ b/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatProfile.tsx @@ -6,7 +6,7 @@ export const ChatProfileTest = () => {
diff --git a/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewComponent.tsx b/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewComponent.tsx index ba0f92847..2537443d6 100644 --- a/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewComponent.tsx +++ b/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewComponent.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components'; import { Section } from '../components/StyledComponents'; - +import { CreateGroupModal } from "@pushprotocol/uiweb"; import { ChatViewComponent } from '@pushprotocol/uiweb'; const ChatViewComponentTest = () => { @@ -13,11 +13,9 @@ const ChatViewComponentTest = () => { return (

Chat UI Test page

+ {console.log('in close')}} /> - - console.log("BOIIII RETURNNNSSSSS")} chatId='b8e068e02fe12d7136bc2f24408835573f30c6fbf0b65ea26ab4c7055a2c85f1' limit={10} isConnected={true} autoConnect={false}/> - - + console.log("BOIIII RETURNNNSSSSS")} chatId='4ac5ab85c9c3d57adbdf2dba79357e56b2f9ef0256befe750d9f93af78d2ca68' limit={10} isConnected={true} />
); @@ -29,3 +27,4 @@ const ChatViewComponentCard = styled(Section)` height: 60vh; `; //c2d544ad9d1efd5c5a593b143bf8232875c926cf28015564e70ad078b95f807e +//4ac5ab85c9c3d57adbdf2dba79357e56b2f9ef0256befe750d9f93af78d2ca68 diff --git a/packages/examples/sdk-frontend-react/src/app/app.tsx b/packages/examples/sdk-frontend-react/src/app/app.tsx index 7f1db9f4c..98a5bd2c7 100644 --- a/packages/examples/sdk-frontend-react/src/app/app.tsx +++ b/packages/examples/sdk-frontend-react/src/app/app.tsx @@ -313,7 +313,7 @@ export function App() { - + @@ -1027,8 +1027,8 @@ Allowed Options (params with _ are mandatory) Expected response (Opt in to channel) ```typescript -// PushAPI.channels.subscribe | Response - 200 OK -{ status: 'success', message: 'successfully opted into channel' } +// PushAPI.channels.subscribe | Response - 204 +{ status: 204, message: 'successfully opted into channel' } ``` @@ -1068,8 +1068,8 @@ Allowed Options (params with _ are mandatory) Expected response (Opt out of a channel) ```typescript -// PushAPI.channels.unsubscribe | Response - 200 OK -{ status: 'success', message: 'successfully opted out channel' } +// PushAPI.channels.unsubscribe | Response - 204 +{ status: 204, message: 'successfully opted out channel' } ``` diff --git a/packages/restapi/README.md b/packages/restapi/README.md index 96c528a13..93c9f0f87 100644 --- a/packages/restapi/README.md +++ b/packages/restapi/README.md @@ -176,6 +176,8 @@ Binance Mainnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa Binance Testnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa Optimism Mainnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa Optimism Testnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa +Arbitrum Mainnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa +Arbitrum One Testnet - 0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa ``` # SDK Features @@ -470,10 +472,7 @@ Allowed Options (params with \* are mandatory) - This method is used to enable/disable the video (from `data.local.stream`) for a push video call. - Can be triggered on the initiator as well as receivers end. - <<<<<<< HEAD -- # **Note -** If video was not enabled during `create()` then it will always remain off. - **Note -** If video was not enabled during `create()` then it will always remain off. - > > > > > > > main ```typescript export type EnableVideoInputOptions = { @@ -494,10 +493,7 @@ Allowed Options (params with \* are mandatory) - This method is used to enable/disable the audio (from `data.local.stream`) for a push video call. - Can be triggered on the initiator as well as receivers end. - <<<<<<< HEAD -- # **Note -** If audio was not enabled during `create()` then it will always remain off. - **Note -** If audio was not enabled during `create()` then it will always remain off. - > > > > > > > main ```typescript export type EnableAudioInputOptions = { @@ -3107,15 +3103,14 @@ const aliceMessagesBob = await userAlice.chat.send(bobAddress, { }); ``` -| Param | Type | Default | Remarks | -| ---------------------- | ---------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- | -| `recipient` | `string` | - | Recipient ( For Group Chats target is chatId, for 1 To 1 chat target is Push DID ) | -| `options` | `object` | - | Configuration for message to be sent | -| `options.type` \* | `Text` or `Image` or `File` or `MediaEmbed` or `GIF` or `Meta` or `Reaction` | - | Type of message Content | -| `options.content` | `string` | - | Message Content | -| `options.action` \* | `string` | - | Message action ( Only available for Meta & Reaction Messages ) | -| `options.reference` \* | `string` or `null` | - | Message reference hash ( Only available for Reaction Messages ) | -| `options.info` \* | `{ affected : string[]: arbitrary?: { [key: string]: any } }` | - | Message reference hash ( Only available for Meta Messages ) | +| Param | Type | Default | Remarks | +| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- | +| `recipient` | `string` | - | Recipient ( For Group Chats target is chatId, for 1 To 1 chat target is Push DID ) | +| `options` | `object` | - | Configuration for message to be sent | +| `options.type` \* | `Text` or `Image` or `Audio` or `Video` or `File` or `MediaEmbed` or `GIF` or `Meta` or `Reaction` or `Receipt` or `Intent` or `Reply` or `Composite` | - | Type of message Content | +| `options.content` | `string` or `{type: `Text`or`Image`or`Audio`or`Video`or`File`or`MediaEmbed`or`GIF` ; content: string}` [For Reply] or `{type: `Text`or`Image`or`Audio`or`Video`or`File`or`MediaEmbed`or`GIF` ; content: string}[]` [For Composite] | - | Message Content | +| `options.reference` \* | `string` | - | Message reference hash ( Only available for Reaction & Reply Messages ) | +| `options.info` \* | `{ affected : string[]: arbitrary?: { [key: string]: any } }` | - | Message reference hash ( Only available for Meta & UserActivity Messages ) | \* - Optional @@ -5497,7 +5492,6 @@ const aliceInfo = await userAlice.notification.list(); ``` - **Parameters:** | Parameter | Type | Default | Description | @@ -5694,7 +5688,6 @@ const subscriptions = await userAlice.notification.subscriptions(); ``` - **Parameters:** | Parameter | Type | Default | Description | @@ -5712,37 +5705,37 @@ const subscriptions = await userAlice.notification.subscriptions(); ```typescript // PushAPI.user.getSubscriptions | Response - 200 OK [ - { channel: '0x0000000000000000000000000000000000000000' }, - { channel: '0xa3B6712fB922cdbbdce9AB22571e75d0d81B3b00' }, - { channel: '0xde3aEA26fDC3ADdC1dB32baf1a058Cf0878FEac1' }, - { channel: '0x69e666767Ba3a661369e1e2F572EdE7ADC926029' }, - { channel: '0x466AEEf0943C5F098dBcEf3c1eEC03322E1F97eD' }, - { channel: '0xcE98113b998380729B04596e3eA0255fbA138D34' }, - { channel: '0xa89523351BE1e2De64937AA9AF61Ae06eAd199C7' }, - { channel: '0x0a651cF7A9b60082fecdb5f30DB7914Fd7d2cf93' }, - { channel: '0x0b5E9fa12C4C1946fA2f14b7271cC60541508f23' }, - { channel: '0x2AEcb6DeE3652dA1dD6b54D5fd4f7D8F43DaEb78' }, - { channel: '0xcB6C7b2E340D50701d45d55507f19A5cE5d72330' }, - { channel: '0xB59Cdc85Cacd15097ecE4C77ed9D225014b4D56D' }, - { channel: '0xA5E269eec042Bf61183DEf9911D03359597494b7' }, - { channel: '0x6bf1ee9DE5D11Fa558c1FA8D8855E26C38Fa582A' }, - { channel: '0x72Ac64A3aE0ab60D725980b73Ef460ED9e742cc7' }, - { channel: '0xEc6CbD318CB7BA8a0fBbffF697681C0a4ADA0349' }, - { channel: '0xAb9415961F58eBD6d79029bC76F261Fa65a80D3D' }, - { channel: '0x08D77bD7500a07d791dD1323919C22e1FDb72224' }, - { channel: '0xa1016081D6Da53b4246178eD83922C55F7171e54' }, - { channel: '0x6A06014AC6BdE2906D194e63ec3b1B5B4c9C2Abb' }, - { channel: '0xf69389475E082f4BeFDb9dee4a1E9fe6cd29f6e7' }, - { channel: '0x9601f08b9EcB981D273B72e7f33964Cb98f977fe' }, - { channel: '0x47A2910432016CA9f62B20dCE09b89d357d0c3d7' }, - { channel: '0x74415Bc4C4Bf4Baecc2DD372426F0a1D016Fa924' }, - { channel: '0x14c0157f9eEA7AEe61ba2606E75716E210b4697a' }, - { channel: '0x025846389950A13292E63e4794C7D148FF57F995' }, - { channel: '0x2aecb6dee3652da1dd6b54d5fd4f7d8f43daeb77' }, - { channel: '0xD8634C39BBFd4033c0d3289C4515275102423681' }, - { channel: '0x19fB80f16EAFCfb5BBFa07451CC5694E8932EA52' }, - { channel: '0x94c3016ef3e503774630fC71F59B8Da9f7D470B7' }, -]; + { channel: '0x0000000000000000000000000000000000000000',user_settings: null }, + { channel: '0xa3B6712fB922cdbbdce9AB22571e75d0d81B3b00',user_settings: '[{"type": 1, "user": false, "index": 1, "default": false, "description": "test1"}, {"type": 2, "user": 25, "index": 2, "default": 25, "enabled": 1, "lowerLimit": 23, "upperLimit": 35, "description": "test3"}, {"type": 2, "user": 64, "index": 3, "default": 56, "enabled": 1, "lowerLimit": 43, "upperLimit": 78, "description": "test5"}]' }, + { channel: '0xde3aEA26fDC3ADdC1dB32baf1a058Cf0878FEac1',user_settings: null }, + { channel: '0x69e666767Ba3a661369e1e2F572EdE7ADC926029',user_settings: null }, + { channel: '0x466AEEf0943C5F098dBcEf3c1eEC03322E1F97eD',user_settings: null }, + { channel: '0xcE98113b998380729B04596e3eA0255fbA138D34',user_settings: null }, + { channel: '0xa89523351BE1e2De64937AA9AF61Ae06eAd199C7',user_settings: null }, + { channel: '0x0a651cF7A9b60082fecdb5f30DB7914Fd7d2cf93',user_settings: null }, + { channel: '0x0b5E9fa12C4C1946fA2f14b7271cC60541508f23',user_settings: null }, + { channel: '0x2AEcb6DeE3652dA1dD6b54D5fd4f7D8F43DaEb78',user_settings: null }, + { channel: '0xcB6C7b2E340D50701d45d55507f19A5cE5d72330',user_settings: null }, + { channel: '0xB59Cdc85Cacd15097ecE4C77ed9D225014b4D56D',user_settings: null }, + { channel: '0xA5E269eec042Bf61183DEf9911D03359597494b7',user_settings: null }, + { channel: '0x6bf1ee9DE5D11Fa558c1FA8D8855E26C38Fa582A',user_settings: null }, + { channel: '0x72Ac64A3aE0ab60D725980b73Ef460ED9e742cc7',user_settings: null }, + { channel: '0xEc6CbD318CB7BA8a0fBbffF697681C0a4ADA0349',user_settings: null }, + { channel: '0xAb9415961F58eBD6d79029bC76F261Fa65a80D3D',user_settings: null }, + { channel: '0x08D77bD7500a07d791dD1323919C22e1FDb72224',user_settings: null }, + { channel: '0xa1016081D6Da53b4246178eD83922C55F7171e54',user_settings: null }, + { channel: '0x6A06014AC6BdE2906D194e63ec3b1B5B4c9C2Abb',user_settings: null }, + { channel: '0xf69389475E082f4BeFDb9dee4a1E9fe6cd29f6e7',user_settings: null }, + { channel: '0x9601f08b9EcB981D273B72e7f33964Cb98f977fe',user_settings: null }, + { channel: '0x47A2910432016CA9f62B20dCE09b89d357d0c3d7',user_settings: null }, + { channel: '0x74415Bc4C4Bf4Baecc2DD372426F0a1D016Fa924',user_settings: null }, + { channel: '0x14c0157f9eEA7AEe61ba2606E75716E210b4697a',user_settings: null }, + { channel: '0x025846389950A13292E63e4794C7D148FF57F995',user_settings: null }, + { channel: '0x2aecb6dee3652da1dd6b54d5fd4f7d8f43daeb77',user_settings: null }, + { channel: '0xD8634C39BBFd4033c0d3289C4515275102423681',user_settings: null }, + { channel: '0x19fB80f16EAFCfb5BBFa07451CC5694E8932EA52',user_settings: null }, + { channel: '0x94c3016ef3e503774630fC71F59B8Da9f7D470B7',user_settings: null }, +] ``` @@ -5772,8 +5765,8 @@ const subscribeStatus = await userAlice.notification.subscribe(channelInCAIP) Expected response (Opt in to channel) ```typescript -// PushAPI.channels.subscribe | Response - 200 OK -{ status: 'success', message: 'successfully opted into channel' } +// PushAPI.channels.subscribe | Response - 204 +{ status: 204, message: 'successfully opted into channel' } ``` @@ -5804,8 +5797,8 @@ const unsubscribeStatus = await userAlice.notification.unsubscribe(channelInCAIP Expected response (Opt out of a channel) ```typescript -// PushAPI.channels.unsubscribe | Response - 200 OK -{ status: 'success', message: 'successfully opted out channel' } +// PushAPI.channels.unsubscribe | Response - 204 +{ status: 204, message: 'successfully opted out channel' } ``` @@ -6766,4 +6759,4 @@ const aliasInfo = userAlice.channel.alias.info({alias: '0xABC', aliasChain:'POLY } } ``` - \ No newline at end of file + diff --git a/packages/restapi/package.json b/packages/restapi/package.json index c9d214e70..7d07fba25 100644 --- a/packages/restapi/package.json +++ b/packages/restapi/package.json @@ -1,6 +1,6 @@ { "name": "@pushprotocol/restapi", - "version": "0.0.1-alpha.44", + "version": "1.4.22", "type": "commonjs", "publishConfig": { "registry": "https://registry.npmjs.org/" diff --git a/packages/restapi/project.json b/packages/restapi/project.json index 48ed574a1..6f7ec7b4b 100644 --- a/packages/restapi/project.json +++ b/packages/restapi/project.json @@ -35,7 +35,7 @@ "options": { "preset": "angular", "commitMessageFormat": "ci(${projectName}): šŸŽ‰ cut release to ${projectName}-v${version}", - "postTargets": ["restapi:build", "restapi:ci-publish"] + "postTargets": ["socket:build", "restapi:build", "restapi:ci-publish"] } }, "ci-version-beta": { diff --git a/packages/restapi/src/lib/channels/index.ts b/packages/restapi/src/lib/channels/index.ts index 51f12c24e..6e584d9d3 100644 --- a/packages/restapi/src/lib/channels/index.ts +++ b/packages/restapi/src/lib/channels/index.ts @@ -4,4 +4,6 @@ export * from './search'; export * from './subscribe'; export * from './unsubscribe'; export * from './_getSubscribers'; -export * from './getSubscribers'; \ No newline at end of file +export * from './getSubscribers'; +export * from './subscribeV2'; +export * from './unsubscribeV2'; diff --git a/packages/restapi/src/lib/channels/signature.helpers.ts b/packages/restapi/src/lib/channels/signature.helpers.ts index 33f977f4e..fadb4e480 100644 --- a/packages/restapi/src/lib/channels/signature.helpers.ts +++ b/packages/restapi/src/lib/channels/signature.helpers.ts @@ -1,47 +1,77 @@ - -type channelActionType = "Unsubscribe" | "Subscribe"; +type channelActionType = 'Unsubscribe' | 'Subscribe'; export const getDomainInformation = ( chainId: number, verifyingContract: string ) => { return { - name: "EPNS COMM V1", + name: 'EPNS COMM V1', chainId, verifyingContract, }; -} - +}; + export const getSubscriptionMessage = ( channel: string, userAddress: string, action: channelActionType ) => { - const actionTypeKey = (action === "Unsubscribe") ? "unsubscriber" : "subscriber"; - + const actionTypeKey = + action === 'Unsubscribe' ? 'unsubscriber' : 'subscriber'; + return { channel, [actionTypeKey]: userAddress, action: action, }; -} +}; + +export const getSubscriptionMessageV2 = ( + channel: string, + userAddress: string, + action: channelActionType, + userSetting?: string | null +) => { + const actionTypeKey = + action === 'Unsubscribe' ? 'unsubscriber' : 'subscriber'; + if (action == 'Subscribe') { + return JSON.stringify({ + channel, + [actionTypeKey]: userAddress, + action: action, + userSetting: userSetting?? '', + }, null, 4); + } else { + return JSON.stringify({ + channel, + [actionTypeKey]: userAddress, + action: action, + }, null, 4); + } +}; export const getTypeInformation = (action: string) => { - if (action === "Subscribe") { + if (action === 'Subscribe') { return { Subscribe: [ - { name: "channel", type: "address" }, - { name: "subscriber", type: "address" }, - { name: "action", type: "string" }, + { name: 'channel', type: 'address' }, + { name: 'subscriber', type: 'address' }, + { name: 'action', type: 'string' }, ], }; } return { Unsubscribe: [ - { name: "channel", type: "address" }, - { name: "unsubscriber", type: "address" }, - { name: "action", type: "string" }, + { name: 'channel', type: 'address' }, + { name: 'unsubscriber', type: 'address' }, + { name: 'action', type: 'string' }, ], }; -}; \ No newline at end of file +}; + +export const getTypeInformationV2 = () => { + return { + Data: [{ name: 'data', type: 'string' }], + }; +}; diff --git a/packages/restapi/src/lib/channels/subscribeV2.ts b/packages/restapi/src/lib/channels/subscribeV2.ts new file mode 100644 index 000000000..b86b8e937 --- /dev/null +++ b/packages/restapi/src/lib/channels/subscribeV2.ts @@ -0,0 +1,111 @@ +import axios from 'axios'; +import { + getCAIPAddress, + getConfig, + getCAIPDetails, + signTypedData, +} from '../helpers'; +import { + getDomainInformation, + getTypeInformationV2, + getSubscriptionMessageV2, +} from './signature.helpers'; +import Constants, { ENV } from '../constants'; +import { SignerType } from '../types'; + +export type SubscribeOptionsV2Type = { + signer: SignerType; + channelAddress: string; + userAddress: string; + settings?: string | null; + verifyingContractAddress?: string; + env?: ENV; + onSuccess?: () => void; + onError?: (err: Error) => void; +}; + +export const subscribeV2 = async (options: SubscribeOptionsV2Type) => { + const { + signer, + channelAddress, + userAddress, + settings = undefined, + verifyingContractAddress, + env = Constants.ENV.PROD, + onSuccess, + onError, + } = options || {}; + try { + const _channelAddress = await getCAIPAddress( + env, + channelAddress, + 'Channel' + ); + + const channelCAIPDetails = getCAIPDetails(_channelAddress); + if (!channelCAIPDetails) throw Error('Invalid Channel CAIP!'); + + const chainId = parseInt(channelCAIPDetails.networkId, 10); + + const _userAddress = await getCAIPAddress(env, userAddress, 'User'); + + const userCAIPDetails = getCAIPDetails(_userAddress); + if (!userCAIPDetails) throw Error('Invalid User CAIP!'); + + const { API_BASE_URL, EPNS_COMMUNICATOR_CONTRACT } = getConfig( + env, + channelCAIPDetails + ); + + const requestUrl = `${API_BASE_URL}/v1/channels/${_channelAddress}/subscribe`; + // get domain information + const domainInformation = getDomainInformation( + chainId, + verifyingContractAddress || EPNS_COMMUNICATOR_CONTRACT + ); + + // get type information + const typeInformation = getTypeInformationV2(); + + // get message + const messageInformation = { + data: getSubscriptionMessageV2( + channelCAIPDetails.address, + userCAIPDetails.address, + 'Subscribe', + settings + ), + }; + // sign a message using EIP712 + const signature = await signTypedData( + signer, + domainInformation, + typeInformation, + messageInformation, + 'Data' + ); + + const verificationProof = signature; // might change + + const body = { + verificationProof: `eip712v2:${verificationProof}`, + message: + messageInformation.data, + + }; + + const res = await axios.post(requestUrl, body); + + if (typeof onSuccess === 'function') onSuccess(); + + return { status: res.status, message: 'successfully opted into channel' }; + } catch (err: any) { + + if (typeof onError === 'function') onError(err as Error); + + return { + status: err?.response?.status?? '' , + message: err instanceof Error ? err.message : JSON.stringify(err), + }; + } +}; diff --git a/packages/restapi/src/lib/channels/unsubscribeV2.ts b/packages/restapi/src/lib/channels/unsubscribeV2.ts new file mode 100644 index 000000000..f792fb782 --- /dev/null +++ b/packages/restapi/src/lib/channels/unsubscribeV2.ts @@ -0,0 +1,110 @@ +import axios from 'axios'; +import { + getCAIPAddress, + getConfig, + getCAIPDetails, + signTypedData, +} from '../helpers'; +import { + getTypeInformation, + getDomainInformation, + getSubscriptionMessage, + getTypeInformationV2, + getSubscriptionMessageV2, +} from './signature.helpers'; +import Constants, { ENV } from '../constants'; +import { SignerType } from '../types'; + +export type UnSubscribeOptionsV2Type = { + signer: SignerType; + channelAddress: string; + userAddress: string; + verifyingContractAddress?: string; + env?: ENV; + onSuccess?: () => void; + onError?: (err: Error) => void; +}; + +export const unsubscribeV2 = async (options: UnSubscribeOptionsV2Type) => { + const { + signer, + channelAddress, + userAddress, + verifyingContractAddress, + env = Constants.ENV.PROD, + onSuccess, + onError, + } = options || {}; + + try { + const _channelAddress = await getCAIPAddress( + env, + channelAddress, + 'Channel' + ); + + const channelCAIPDetails = getCAIPDetails(_channelAddress); + if (!channelCAIPDetails) throw Error('Invalid Channel CAIP!'); + + const chainId = parseInt(channelCAIPDetails.networkId, 10); + + const _userAddress = await getCAIPAddress(env, userAddress, 'User'); + + const userCAIPDetails = getCAIPDetails(_userAddress); + if (!userCAIPDetails) throw Error('Invalid User CAIP!'); + + const { API_BASE_URL, EPNS_COMMUNICATOR_CONTRACT } = getConfig( + env, + channelCAIPDetails + ); + + const requestUrl = `${API_BASE_URL}/v1/channels/${_channelAddress}/unsubscribe`; + + // get domain information + const domainInformation = getDomainInformation( + chainId, + verifyingContractAddress || EPNS_COMMUNICATOR_CONTRACT + ); + + // get type information + const typeInformation = getTypeInformationV2(); + + // get message + const messageInformation = { + data: getSubscriptionMessageV2( + channelCAIPDetails.address, + userCAIPDetails.address, + 'Unsubscribe' + ), + }; + + // sign a message using EIP712 + const signature = await signTypedData( + signer, + domainInformation, + typeInformation, + messageInformation, + 'Unsubscribe' + ); + + const verificationProof = signature; // might change + + const body = { + verificationProof: `eip712v2:${verificationProof}`, + message: messageInformation.data, + }; + + const res = await axios.post(requestUrl, body); + + if (typeof onSuccess === 'function') onSuccess(); + + return { status: res.status, message: 'successfully opted into channel' }; + } catch (err: any) { + if (typeof onError === 'function') onError(err as Error); + + return { + status: err?.response?.status?? '' , + message: err instanceof Error ? err.message : JSON.stringify(err), + }; + } +}; diff --git a/packages/restapi/src/lib/payloads/helpers.ts b/packages/restapi/src/lib/payloads/helpers.ts index 1863ed1ca..0ef67d4f2 100644 --- a/packages/restapi/src/lib/payloads/helpers.ts +++ b/packages/restapi/src/lib/payloads/helpers.ts @@ -67,6 +67,9 @@ export function getPayloadForAPIInput( ...(inputOptions?.payload?.additionalMeta && { additionalMeta: inputOptions?.payload?.additionalMeta, }), + ...(inputOptions?.payload?.index && { + index: inputOptions?.payload?.index, + }), }, recipients: recipients, }; diff --git a/packages/restapi/src/lib/pushNotification/PushNotificationTypes.ts b/packages/restapi/src/lib/pushNotification/PushNotificationTypes.ts index bc83e881c..a010e3472 100644 --- a/packages/restapi/src/lib/pushNotification/PushNotificationTypes.ts +++ b/packages/restapi/src/lib/pushNotification/PushNotificationTypes.ts @@ -1,8 +1,6 @@ import { ProgressHookType } from '../types'; import { GetAliasInfoOptionsType } from '../alias'; -import { - ADDITIONAL_META_TYPE, -} from '../../lib/payloads/constants'; +import { ADDITIONAL_META_TYPE } from '../../lib/payloads/constants'; export type SubscriptionOptions = { account?: string; @@ -16,6 +14,12 @@ export type ChannelInfoOptions = { export type SubscribeUnsubscribeOptions = { onSuccess?: () => void; onError?: (err: Error) => void; + settings?: UserSetting[]; +}; + +export type UserSetting = { + enabled: boolean; + value?: number; }; export type AliasOptions = Omit; @@ -50,6 +54,10 @@ export type IPayload = { body?: string; cta?: string; embed?: string; + index?: { + index: number; + value?: number; + }; meta?: { domain?: string; type: `${ADDITIONAL_META_TYPE}+${number}`; @@ -92,13 +100,13 @@ export type CreateChannelOptions = { }; export type NotificationSetting = { - type: number, - default: number, - description: string, + type: number; + default: number; + description: string; data?: { upper: number; lower: number; - } -} + }; +}; -export type NotificationSettings = NotificationSetting[] \ No newline at end of file +export type NotificationSettings = NotificationSetting[]; diff --git a/packages/restapi/src/lib/pushNotification/notification.ts b/packages/restapi/src/lib/pushNotification/notification.ts index 67abdd976..ebd7ade15 100644 --- a/packages/restapi/src/lib/pushNotification/notification.ts +++ b/packages/restapi/src/lib/pushNotification/notification.ts @@ -1,7 +1,5 @@ import Constants, { ENV } from '../constants'; -import { - SignerType, -} from '../types'; +import { SignerType } from '../types'; import { SubscribeUnsubscribeOptions, SubscriptionOptions, @@ -121,7 +119,7 @@ export class Notification extends PushNotificationBaseClass { options?: SubscribeUnsubscribeOptions ) => { try { - const { onSuccess, onError } = options || {}; + const { onSuccess, onError, settings } = options || {}; // Vaidatiions // validates if signer object is present this.checkSignerObjectExists(); @@ -142,11 +140,14 @@ export class Notification extends PushNotificationBaseClass { this.account!, parseInt(caipDetail?.networkId as string) ); - return await PUSH_CHANNEL.subscribe({ + // convert the setting to minimal version + const minimalSetting = this.getMinimalUserSetting(settings!); + return await PUSH_CHANNEL.subscribeV2({ signer: this.signer!, channelAddress: channel, userAddress: userAddressInCaip, env: this.env, + settings: minimalSetting ?? '', onSuccess: onSuccess, onError: onError, }); @@ -188,7 +189,7 @@ export class Notification extends PushNotificationBaseClass { this.account!, parseInt(caipDetail?.networkId as string) ); - return await PUSH_CHANNEL.unsubscribe({ + return await PUSH_CHANNEL.unsubscribeV2({ signer: this.signer!, channelAddress: channel, userAddress: userAddressInCaip, diff --git a/packages/restapi/src/lib/pushNotification/pushNotificationBase.ts b/packages/restapi/src/lib/pushNotification/pushNotificationBase.ts index 1d9aab9ab..37d6b138c 100644 --- a/packages/restapi/src/lib/pushNotification/pushNotificationBase.ts +++ b/packages/restapi/src/lib/pushNotification/pushNotificationBase.ts @@ -4,6 +4,7 @@ import { NotificationOptions, CreateChannelOptions, NotificationSettings, + UserSetting, } from './PushNotificationTypes'; import CONFIG, * as config from '../config'; import { getAccountAddress } from '../chat/helpers'; @@ -27,6 +28,8 @@ const LENGTH_UPPER_LIMIT = 125; const LENGTH_LOWER_LIMTI = 1; const SETTING_DELIMITER = '-'; const SETTING_SEPARATOR = '+'; +const SLIDER_TYPE = 2; +const BOOLEAN_TYPE = 1; export const FEED_MAP = { INBOX: false, @@ -142,6 +145,20 @@ export class PushNotificationBaseClass { } const notificationType = this.getNotificationType(recipients, channel); const identityType = IDENTITY_TYPE.DIRECT_PAYLOAD; + // fetch the minimal version based on conifg that was passed + let index; + if (options.payload?.index) { + if (options.payload?.index.value) { + index = + options.payload.index.index + + SETTING_DELIMITER + + SLIDER_TYPE + + SETTING_DELIMITER + + options.payload.index.value; + } else { + index = options.payload.index.index + SETTING_DELIMITER + BOOLEAN_TYPE; + } + } const notificationPayload: ISendNotificationInputOptions = { signer: signer, channel: channel, @@ -157,6 +174,7 @@ export class PushNotificationBaseClass { etime: options.config?.expiry, silent: options.config?.silent, additionalMeta: options.payload?.meta, + index: options.payload?.index ? index : '', }, recipients: notificationType.recipient, graph: options.advanced?.graph, @@ -681,4 +699,33 @@ export class PushNotificationBaseClass { description: notificationSettingDescription.replace(/^\+/, ''), }; } + + protected getMinimalUserSetting(setting: UserSetting[]) { + if(!setting){ + return null; + } + let userSetting = ''; + let numberOfSettings = 0; + for (let i = 0; i < setting.length; i++) { + const ele = setting[i]; + const enabled = ele.enabled ? 1 : 0; + if (ele.enabled) numberOfSettings++; + // slider type + if (Object.keys(ele).includes('value')) { + userSetting = + userSetting + + SLIDER_TYPE + + SETTING_DELIMITER + + enabled + + SETTING_DELIMITER + + ele.value; + } else { + // boolean type + userSetting = userSetting + BOOLEAN_TYPE + SETTING_DELIMITER + enabled; + } + if (i != setting.length - 1) + userSetting = userSetting + SETTING_SEPARATOR; + } + return numberOfSettings + SETTING_SEPARATOR + userSetting; + } } diff --git a/packages/restapi/src/lib/types/index.ts b/packages/restapi/src/lib/types/index.ts index e75a057a5..336175364 100644 --- a/packages/restapi/src/lib/types/index.ts +++ b/packages/restapi/src/lib/types/index.ts @@ -116,6 +116,7 @@ export interface ISendNotificationInputOptions { * use additionalMeta instead */ metadata?: any; + index?: string; }; recipients?: string | string[]; // CAIP or plain ETH channel: string; // CAIP or plain ETH diff --git a/packages/restapi/tests/lib/channel/sendNotification.test.ts b/packages/restapi/tests/lib/channel/sendNotification.test.ts new file mode 100644 index 000000000..692e8e740 --- /dev/null +++ b/packages/restapi/tests/lib/channel/sendNotification.test.ts @@ -0,0 +1,70 @@ +import * as path from 'path'; +import * as dotenv from 'dotenv'; +dotenv.config({ path: path.resolve(__dirname, '../../.env') }); +import * as PUSH_PAYLOAD from '../../../src/lib/payloads'; +import { expect } from 'chai'; +import Constants from '../../../src/lib/constants'; +import { ethers } from 'ethers'; + +describe('PUSH_PAYLOAD.sendNotification functionality', () => { + let signer1: any; + let account1: string; + let signer2: any; + let account2: string; + + beforeEach(async () => { + signer1 = new ethers.Wallet( + '0xb9d00f786e1d024cfed08f696a775217ff75501f4aacef5ec0795fc4a2eb9df1' + ); + account1 = signer1.address; + + const WALLET2 = ethers.Wallet.createRandom(); + signer2 = new ethers.Wallet(WALLET2.privateKey); + account2 = WALLET2.address; + }); + + it('Should send notification with setting index', async () => { + enum IDENTITY_TYPE { + MINIMAL = 0, + IPFS = 1, + DIRECT_PAYLOAD = 2, + SUBGRAPH = 3, + } + + enum NOTIFICATION_TYPE { + BROADCAST = 1, + TARGETTED = 3, + SUBSET = 4, + } + + enum ENV { + PROD = 'prod', + STAGING = 'staging', + DEV = 'dev', + /** + * **This is for local development only** + */ + LOCAL = 'local', + } + const res = await PUSH_PAYLOAD.sendNotification({ + env: ENV.DEV, + signer: signer1, + type: NOTIFICATION_TYPE.BROADCAST, + identityType: IDENTITY_TYPE.DIRECT_PAYLOAD, + + notification: { + title: 'hey', + body: 'hey', + }, + payload: { + title: 'hey', + body: 'hey', + cta: '', + img: '', + index: '1-2-10', + }, + channel: `eip155:5:${account1}`, + }); + expect(res.status).to.be.equal(204); + }); +}); diff --git a/packages/restapi/tests/lib/channel/subscribeV2.test.ts b/packages/restapi/tests/lib/channel/subscribeV2.test.ts new file mode 100644 index 000000000..b1bd3f0b4 --- /dev/null +++ b/packages/restapi/tests/lib/channel/subscribeV2.test.ts @@ -0,0 +1,56 @@ +import * as path from 'path'; +import * as dotenv from 'dotenv'; +dotenv.config({ path: path.resolve(__dirname, '../../.env') }); +import * as PUSH_CHANNEL from '../../../src/lib/channels/'; +import { expect } from 'chai'; +import Constants from '../../../src/lib/constants'; +import { ethers } from 'ethers'; + +describe('PUSH_CHANNEL.subscribeV2 functionality', () => { + let signer1: any; + let account1: string; + let signer2: any; + let account2: string; + + beforeEach(async () => { + const WALLET1 = ethers.Wallet.createRandom(); + signer1 = new ethers.Wallet(WALLET1.privateKey); + account1 = WALLET1.address; + + const WALLET2 = ethers.Wallet.createRandom(); + signer2 = new ethers.Wallet(WALLET2.privateKey); + account2 = WALLET2.address; + }); + + it('Should subscribe to the channel via V2 without settings', async () => { + const res = await PUSH_CHANNEL.subscribeV2({ + signer: signer1, + channelAddress: 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681', + userAddress: `eip155:5:${account1}`, + env: Constants.ENV.STAGING, + }); + expect(res.status).to.be.equal('success'); + }); + + it('Should unsubscribe to the channel via V2 without settings', async () => { + const res = await PUSH_CHANNEL.unsubscribeV2({ + signer: signer1, + channelAddress: 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681', + userAddress: `eip155:5:${account1}`, + env: Constants.ENV.STAGING, + }); + console.log(res) + expect(res.status).to.be.equal('success'); + }); + + it('Should subscribe to the channel via V2 with settings', async () => { + const res = await PUSH_CHANNEL.subscribeV2({ + signer: signer1, + channelAddress: 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681', + userAddress: `eip155:5:${account1}`, + env: Constants.ENV.STAGING, + userSetting: '2-1-0+2-1', + }); + expect(res.status).to.be.equal('success'); + }); +}); diff --git a/packages/restapi/tests/lib/pushNotification/alias.test.ts b/packages/restapi/tests/lib/pushNotification/alias.test.ts index ae7050ef8..ae62cf864 100644 --- a/packages/restapi/tests/lib/pushNotification/alias.test.ts +++ b/packages/restapi/tests/lib/pushNotification/alias.test.ts @@ -46,7 +46,7 @@ describe('PushAPI.alias functionality', () => { alias: '0x93A829d16DE51745Db0530A0F8E8A9B8CA5370E5', aliasChain: 'POLYGON', }); - // console.log(res) + // console.log(res) expect(res).not.null; }); }); diff --git a/packages/restapi/tests/lib/pushNotification/notification.test.ts b/packages/restapi/tests/lib/pushNotification/notification.test.ts index 31442a287..85913a1c7 100644 --- a/packages/restapi/tests/lib/pushNotification/notification.test.ts +++ b/packages/restapi/tests/lib/pushNotification/notification.test.ts @@ -140,7 +140,7 @@ describe('PushAPI.notification functionality', () => { const res = await userAlice.notification.subscribe( 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681' ); - console.log(res) + // console.log(res) expect(res).not.null; }); diff --git a/packages/restapi/tests/lib/pushNotification/onchain.test.ts b/packages/restapi/tests/lib/pushNotification/onchain.test.ts index c8e91058c..647b20346 100644 --- a/packages/restapi/tests/lib/pushNotification/onchain.test.ts +++ b/packages/restapi/tests/lib/pushNotification/onchain.test.ts @@ -4,7 +4,7 @@ // import { expect } from 'chai'; // import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; // import { PushNotificationBaseClass } from '../../../src/lib/pushNotification/pushNotificationBase'; -// import * as config from "../../../src/lib/config" +// import * as config from '../../../src/lib/config'; // import { // createWalletClient, // http, @@ -16,21 +16,19 @@ // import { BigNumber, ethers } from 'ethers'; // enum ENV { -// PROD = 'prod', -// STAGING = 'staging', -// DEV = 'dev', -// /** -// * **This is for local development only** -// */ -// LOCAL = 'local', -// } +// PROD = 'prod', +// STAGING = 'staging', +// DEV = 'dev', +// /** +// * **This is for local development only** +// */ +// LOCAL = 'local', +// } // describe.only('test', () => { // const signer = createWalletClient({ // account: privateKeyToAccount(`0x${process.env['WALLET_PRIVATE_KEY']}`), // chain: goerli, -// transport: http( -// 'https://goerli.blockpi.network/v1/rpc/public' -// ), +// transport: http('https://goerli.blockpi.network/v1/rpc/public'), // }); // const signer3 = createWalletClient({ @@ -46,70 +44,85 @@ // `0x${process.env['WALLET_PRIVATE_KEY']}`, // provider // ); -// it('testing with viem', async () => { -// const account2 = await signer2.getAddress(); -// const viemUser = new PushNotificationBaseClass(signer, ENV.STAGING, account2) -// const contract = viemUser.createContractInstance("0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C", config.ABIS.CORE, goerli) -// const res = await viemUser.fetchUpdateCounter(contract, account2); -// console.log(res) -// const viemContract = await userViem.createContractInstance( -// '0x2b9bE9259a4F5Ba6344c1b1c07911539642a2D33', -// abi, -// goerli -// ); -// const balance = await userViem.fetchBalance( -// viemContract, -// '0xD8634C39BBFd4033c0d3289C4515275102423681' -// ); -// console.log(balance); -// const allowance = await userViem.fetchAllownace( -// viemContract, -// '0xD8634C39BBFd4033c0d3289C4515275102423681', -// '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C' -// ); -// console.log(allowance); -// const approveAmount = ethers.BigNumber.from(10000); -// const approveRes = await userViem.approveToken( -// viemContract, -// '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C', -// approveAmount -// ); -// console.log(approveRes); - -// const addDelegate = await userViem.delegate.add( -// 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681' -// ); -// console.log(addDelegate); -// }); -// it.only('test with ethers', async () => { +// it.only('Test minimal conversion', async () => { // const account2 = await signer2.getAddress(); -// const userEthers = new PushNotificationBaseClass(signer2, ENV.STAGING, account2,); -// const contract = userEthers.createContractInstance("0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C", config.ABIS.CORE, goerli) -// const res = await userEthers.fetchUpdateCounter(contract, account2); -// console.log(res) -// const ethersContract = await userEthers.createContractInstance( -// '0x2b9bE9259a4F5Ba6344c1b1c07911539642a2D33', -// abi, -// goerli +// const viemUser = new PushNotificationBaseClass( +// signer, +// ENV.STAGING, +// account2 // ); -// const balance2 = await userEthers.fetchBalance( -// ethersContract, -// '0xD8634C39BBFd4033c0d3289C4515275102423681' -// ); -// console.log(balance2); -// const allowance2 = await userEthers.fetchAllownace( -// ethersContract, -// '0xD8634C39BBFd4033c0d3289C4515275102423681', -// '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C' -// ); -// console.log(allowance2); -// const approveAmount2 = ethers.BigNumber.from(10000); -// const approveRes2 = await userEthers.approveToken( -// ethersContract, -// '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C', -// approveAmount2 -// ); -// console.log(approveRes2); +// viemUser.getMinimalUserSetting([ +// { enabled: true }, +// { enabled: false, value: 10 }, +// { enabled: false }, +// { enabled: true, value: 10 }, +// ]); // }); + // it('testing with viem', async () => { + // const account2 = await signer2.getAddress(); + // const viemUser = new PushNotificationBaseClass(signer, ENV.STAGING, account2) + // const contract = viemUser.createContractInstance("0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C", config.ABIS.CORE, goerli) + // const res = await viemUser.fetchUpdateCounter(contract, account2); + // console.log(res) + // const viemContract = await userViem.createContractInstance( + // '0x2b9bE9259a4F5Ba6344c1b1c07911539642a2D33', + // abi, + // goerli + // ); + // const balance = await userViem.fetchBalance( + // viemContract, + // '0xD8634C39BBFd4033c0d3289C4515275102423681' + // ); + // console.log(balance); + // const allowance = await userViem.fetchAllownace( + // viemContract, + // '0xD8634C39BBFd4033c0d3289C4515275102423681', + // '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C' + // ); + // console.log(allowance); + // const approveAmount = ethers.BigNumber.from(10000); + // const approveRes = await userViem.approveToken( + // viemContract, + // '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C', + // approveAmount + // ); + // console.log(approveRes); + + // const addDelegate = await userViem.delegate.add( + // 'eip155:5:0xD8634C39BBFd4033c0d3289C4515275102423681' + // ); + // console.log(addDelegate); + // }); + + // it.only('test with ethers', async () => { + // const account2 = await signer2.getAddress(); + // const userEthers = new PushNotificationBaseClass(signer2, ENV.STAGING, account2,); + // const contract = userEthers.createContractInstance("0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C", config.ABIS.CORE, goerli) + // const res = await userEthers.fetchUpdateCounter(contract, account2); + // console.log(res) + // const ethersContract = await userEthers.createContractInstance( + // '0x2b9bE9259a4F5Ba6344c1b1c07911539642a2D33', + // abi, + // goerli + // ); + // const balance2 = await userEthers.fetchBalance( + // ethersContract, + // '0xD8634C39BBFd4033c0d3289C4515275102423681' + // ); + // console.log(balance2); + // const allowance2 = await userEthers.fetchAllownace( + // ethersContract, + // '0xD8634C39BBFd4033c0d3289C4515275102423681', + // '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C' + // ); + // console.log(allowance2); + // const approveAmount2 = ethers.BigNumber.from(10000); + // const approveRes2 = await userEthers.approveToken( + // ethersContract, + // '0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C', + // approveAmount2 + // ); + // console.log(approveRes2); + // }); // }); diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/AddWalletContent.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/AddWalletContent.tsx index f2a00f5e4..dd8d4f64d 100644 --- a/packages/uiweb/src/lib/components/chat/ChatProfile/AddWalletContent.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatProfile/AddWalletContent.tsx @@ -1,335 +1,215 @@ -import { useContext, useEffect, useState } from "react"; -import styled from "styled-components"; -import { ThemeContext } from "../theme/ThemeProvider"; -import { useChatData } from "../../../hooks"; -import { displayDefaultUser, getAddress, walletToPCAIP10 } from "../../../helpers"; -import { IToast, ModalButtonProps, User } from "../exportedTypes"; -import * as PushAPI from '@pushprotocol/restapi'; -import { ethers } from "ethers"; -import { addWalletValidation } from "../helpers/helper"; -import ArrowGreyIcon from '../../../icons/CaretDownGrey.svg' -// import ArrowLeftIcon from '../../../icons/ArrowLeft.svg'; -import CloseIcon from '../../../icons/close.svg'; -import { Spinner } from "../../supportChat/spinner/Spinner"; -import { MoreLightIcon } from '../../../icons/MoreLight'; +import { useContext, useState } from 'react'; + +import styled from 'styled-components'; + +import { ThemeContext } from '../theme/ThemeProvider'; +import { useChatData } from '../../../hooks'; +import { MdError } from 'react-icons/md'; + +import { Spinner } from '../../supportChat/spinner/Spinner'; import { MoreDarkIcon } from '../../../icons/MoreDark'; -import { SearchIcon } from '../../../icons/SearchIcon'; -import { Section, Span, Image } from "../../reusables/sharedStyling"; +import { Section, Span, Image } from '../../reusables/sharedStyling'; import { AddUserDarkIcon } from '../../../icons/Adddark'; -import { device } from "../../../config"; -import { MemberListContainer } from "./MemberListContainer"; -import useMediaQuery from "../../../hooks/useMediaQuery"; -import useToast from "../helpers/NewToast"; -import { MdCheckCircle, MdError } from "react-icons/md"; -import { Modal } from "../helpers/Modal"; - - - -export const AddWalletContent = ({ onSubmit, handlePrevious, onClose, memberList, handleMemberList, title, groupMembers, isLoading }: {onSubmit: ()=> void ,onClose: ()=> void, handlePrevious: ()=> void, memberList: any, handleMemberList: any, title: string, groupMembers: any, isLoading?: boolean }) => { - const theme = useContext(ThemeContext); - - const [searchedUser, setSearchedUser] = useState(''); - const [filteredUserData, setFilteredUserData] = useState(null); - const [isInValidAddress, setIsInvalidAddress] = useState(false); - const [isLoadingSearch, setIsLoadingSearch] = useState(false); - const { account, env } = useChatData(); - const isMobile = useMediaQuery(device.mobileL); - const groupInfoToast = useToast(); - - - useEffect(() => { - if (isInValidAddress) { - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: 'Invalid Address', - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - } - }, [isInValidAddress]); - - const onChangeSearchBox = (e: any) => { - setSearchedUser(e.target.value); - }; - - const handleUserSearch = async (userSearchData: string): Promise => { - try{ - const caip10 = walletToPCAIP10(userSearchData); - let filteredData: User; - - if (userSearchData.length) { - filteredData = await PushAPI.user.get({ - account: caip10, - env: env - }); - - if (filteredData !== null) { - setFilteredUserData(filteredData); - } - // User is not in the protocol. Create new user - else { - if (ethers.utils.isAddress(userSearchData)) { - const displayUser = displayDefaultUser({ caip10 }); - setFilteredUserData(displayUser); - } else { - setIsInvalidAddress(true); - setFilteredUserData(null); - } - } - } else { - setFilteredUserData(null); - } - setIsLoadingSearch(false); - } - catch(error){ - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: 'Unsuccessful search, Try again', - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - } - }; - - const handleSearch = async (e: any): Promise => { - setIsLoadingSearch(true); - setIsInvalidAddress(false); - e.preventDefault(); - if (!ethers.utils.isAddress(searchedUser)) { - let address: string; - try { - address = await getAddress(searchedUser, env) as string; - // if (!address) { - // address = await library.resolveName(searchedUser); - // } - // this ensures address are checksummed - address = ethers.utils.getAddress(address?.toLowerCase()); - if (address) { - handleUserSearch(address); - } else { - setIsInvalidAddress(true); - setFilteredUserData(null); - } - } catch (err) { - setIsInvalidAddress(true); - setFilteredUserData(null); - } finally { - setIsLoadingSearch(false); - } - } else { - handleUserSearch(searchedUser); - } - }; - - const clearInput = () => { - setSearchedUser(''); - setFilteredUserData(null); - setIsLoadingSearch(false); - }; +import { MemberListContainer } from './MemberListContainer'; +import useMediaQuery from '../../../hooks/useMediaQuery'; +import useToast from '../reusables/NewToast'; + +import { + getNewChatUser, +} from '../../../helpers'; +import { ModalButtonProps, User } from '../exportedTypes'; +import { addWalletValidation } from '../helpers/helper'; +import { device } from '../../../config'; +import CloseIcon from '../../../icons/close.svg'; +import { ChatSearchInput, CustomStyleParamsType, ModalHeader } from '../reusables'; +import useGetChatProfile from '../../../hooks/useGetChatProfile'; +import { BackIcon } from '../../../icons/Back'; + + +type AddWalletContentProps = { + onSubmit: () => void; + onClose: () => void; + handlePrevious: () => void; + memberList: any; + handleMemberList: any; + groupMembers: any; + isLoading?: boolean; + modalHeader: string; +}; +export const AddWalletContent = ({ + onSubmit, + handlePrevious, + onClose, + memberList, + handleMemberList, + groupMembers, + isLoading, + modalHeader, +}: AddWalletContentProps) => { + const theme = useContext(ThemeContext); + + const [filteredUserData, setFilteredUserData] = useState(null); + const { account, env } = useChatData(); + const isMobile = useMediaQuery(device.mobileL); + const {fetchChatProfile} = useGetChatProfile(); + const groupInfoToast = useToast(); + const customSearchStyle:CustomStyleParamsType = { + background:theme.backgroundColor?.modalInputBackground, + border:theme.border?.modalInnerComponents, + placeholderColor:theme.textColor?.modalSubHeadingText, + fontSize:'15px', + fontWeight:'400' + }; + + const handleSearch = async ({searchedText}:{searchedText:string}): Promise => { + //fix ens search + const newChatUser = await getNewChatUser({ + searchText: searchedText, + fetchChatProfile, + env, + }); + if(newChatUser){ + setFilteredUserData(newChatUser); + } + else{ + groupInfoToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: 'Invalid Address', + toastType: 'ERROR', + getToastIcon: (size) => , + }); + } + }; + + const clearInput = () => { + setFilteredUserData(null); + }; const addMemberToList = (member: User) => { let errorMessage = ''; - errorMessage = addWalletValidation(member, memberList, groupMembers, account); + errorMessage = addWalletValidation( + member, + memberList, + groupMembers, + account + ); if (errorMessage) { groupInfoToast.showMessageToast({ toastTitle: 'Error', toastMessage: errorMessage, toastType: 'ERROR', - getToastIcon: (size) => ( - - ), + getToastIcon: (size) => , }); - } else { - handleMemberList((prev: any) => [...prev, { ...member, isAdmin: false }]); - } - - setFilteredUserData(''); - clearInput(); - }; - - const removeMemberFromList = (member: User) => { - const filteredMembers = memberList?.filter((user: any) => user.wallets !== member.wallets); - handleMemberList(filteredMembers); - }; - - return ( -
-
- - {/* handlePrevious()} cursor='pointer' /> */} - - Add Wallets - - onClose()} cursor='pointer' /> -
- -
- Add Wallets - - - {groupMembers - ? `0${memberList?.length + groupMembers?.length} / 09 Members` - : `0${memberList?.length} / 09 Members`} - -
- -
- - -
- {searchedUser.length > 0 && ( - clearInput()} cursor='pointer' /> - )} - {searchedUser.length == 0 && !filteredUserData && -
- -
- } -
-
-
- - {filteredUserData ? ( - - } - darkIcon={} - /> - - ) : isLoadingSearch ? ( -
- -
- ) : null} - - - {memberList?.map((member: any, index: any) => ( - } - darkIcon={} - /> - ))} - - -
- onSubmit()} - isLoading={isLoading} - memberListCount={memberList?.length > 0} - theme={theme} - > - {!isLoading && groupMembers ? 'Add To Group' : ''} - {isLoading && } - -
- -
- ) -} - -const SearchBarContent = styled.form` - position: relative; - display: flex; - flex: 1; -`; + } else { + handleMemberList((prev: any) => [...prev, { ...member, isAdmin: false }]); + } + + setFilteredUserData(''); + clearInput(); + }; + + const removeMemberFromList = (member: User) => { + const filteredMembers = memberList?.filter( + (user: any) => user.wallets.toLowerCase() !== member.wallets.toLowerCase() + ); + handleMemberList(filteredMembers); + }; + + return ( +
+ + +
+ + Add Wallets + + + + {groupMembers + ? `0${memberList?.length + groupMembers?.length} / 09 Members` + : `0${memberList?.length} / 09 Members`} + +
+ +
+ +
+ + {filteredUserData && ( + + } + /> + + ) } + + + {memberList?.map((member: any, index: any) => ( + } + /> + ))} + + +
+ onSubmit()} + isLoading={isLoading} + memberListCount={memberList?.length > 0} + theme={theme} + > + {!isLoading && groupMembers ? 'Add To Group' : ''} + {isLoading && } + +
+
+ ); +}; + + -const Input = styled.input` - box-sizing: border-box; - display: flex; - flex: 1; -// min-width: 445px; - height: 48px; - padding: 0px 50px 0px 16px; - margin: 10px 0px 0px; - border-radius: 99px; - border: 1px solid; - border-color: ${(props) => props.theme.modalSearchBarBorderColor}; - background: ${(props) => props.theme.modalSearchBarBackground}; - color: ${(props) => props.color || '#000'}; - &:focus { - outline: none; - background-image: linear-gradient( - ${(props) => props.theme.snapFocusBg}, - ${(props) => props.theme.snapFocusBg} - ), - linear-gradient( - to right, - rgba(182, 160, 245, 1), - rgba(244, 110, 246, 1), - rgba(255, 222, 211, 1), - rgba(255, 207, 197, 1) - ); - background-origin: border; - border: 1px solid transparent !important; - background-clip: padding-box, border-box; - } - &::placeholder { - color: #657795; - } - @media ${device.mobileL} { - min-width: 100%; - } -`; const MemberList = styled.div` - // justify-content: flex-start; - // padding: 0px 2px; - // margin: 0 0 34px 0; - flex: 1; - // background: red; - width: 100%; + flex: 1; + width: 100%; + margin-bottom:40px; `; const MultipleMemberList = styled.div` - // overflow-y: auto; height: fit-content; max-height: 216px; padding: 0px 2px; - // overflow-x: hidden; width: 100%; &::-webkit-scrollbar-track { - background-color: ${(props) => props.theme.scrollBg}; + background-color: ${(props) => props.theme.scrollbarColor}; } &::-webkit-scrollbar { - background-color: ${(props) => props.theme.scrollBg}; + background-color: ${(props) => props.theme.scrollbarColor}; width: 6px; } @@ -362,19 +242,24 @@ const MultipleMemberList = styled.div` `; const ModalConfirmButton = styled.button` - margin: 60px 0 0 0; - background: ${(props) => props.memberListCount ? '#CF1C84' : props.theme.groupButtonBackgroundColor}; - color: ${(props) => props.memberListCount ? '#fff' : props.theme.groupButtonTextColor}; - border: ${(props) => props.memberListCount ? 'none' : props.theme.modalConfirmButtonBorder}; - min-width: 50%; - box-sizing: border-box; - cursor: pointer; - border-radius: 15px; - padding: 16px; - font-size: 1.125rem; - font-weight: 500; - display: flex; - align-items: center; - justify-content: center; - box-shadow: none; -`; \ No newline at end of file + margin: 60px 0 0 0; + width: 197px; + background: ${(props) => + props.memberListCount ? props.theme.backgroundColor.buttonBackground : props.theme.backgroundColor.buttonDisableBackground}; + color: ${(props) => + props.memberListCount ? props.theme.textColor.buttonText : props.theme.textColor.buttonDisableText}; + border: ${(props) => + props.memberListCount ? 'none' : props.theme.border.modal}; + min-width: 50%; + box-sizing: border-box; + cursor: pointer; + border-radius: 12px; + padding: 16px; + font-size: 16px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + box-shadow: none; + height: 48px; +`; diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx index b9267b17b..fa6d4e27b 100644 --- a/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx @@ -1,188 +1,230 @@ // @typescript-eslint/no-non-null-asserted-optional-chain -import { useContext, useEffect, useRef, useState } from "react"; -import { Image, Section, Span } from "../../reusables"; -import styled from "styled-components"; -import TokenGatedIcon from '../../../icons/Token-Gated.svg'; +import { useContext, useEffect, useRef, useState } from 'react'; + +import styled from 'styled-components'; +import type { IUser } from '@pushprotocol/restapi'; +import { ethers } from 'ethers'; +import { ToastContainer } from 'react-toastify'; + +import { Image, Section, Span } from '../../reusables'; +import { useChatData, useClickAway } from '../../../hooks'; +import { ThemeContext } from '../theme/ThemeProvider'; +import useGetGroupByID from '../../../hooks/chat/useGetGroupByID'; +import useChatProfile from '../../../hooks/chat/useChatProfile'; +import { GroupInfoModal } from './GroupInfoModal'; +import useMediaQuery from '../../../hooks/useMediaQuery'; +import { createBlockie } from '../../space/helpers/blockies'; +import { ProfileContainer } from '../reusables'; +import 'react-toastify/dist/ReactToastify.min.css'; + +import { IGroup } from '../../../types'; +import { isValidETHAddress } from '../helpers/helper'; +import { IChatProfile, IChatTheme } from '../exportedTypes'; +import { InfuraAPIKey, allowedNetworks, device } from '../../../config'; +import { resolveNewEns, shortenText } from '../../../helpers'; +import TokenGatedIcon from '../../../icons/TokenGatedIcon.svg'; import PublicChatIcon from '../../../icons/Public-Chat.svg'; -import VideoChatIcon from '../../../icons/VideoCallIcon.svg'; import GreyImage from '../../../icons/greyImage.png'; import InfoIcon from '../../../icons/infodark.svg'; import VerticalEllipsisIcon from '../../../icons/VerticalEllipsis.svg'; -import type { IUser } from '@pushprotocol/restapi'; -import { useChatData, useClickAway, useDeviceWidthCheck } from "../../../hooks"; -import { ThemeContext } from "../theme/ThemeProvider"; -import { IChatTheme } from "../theme"; -import { pCAIP10ToWallet, resolveEns, resolveNewEns, shortenText } from "../../../helpers"; -import useGetGroupByID from "../../../hooks/chat/useGetGroupByID"; -import useChatProfile from "../../../hooks/chat/useChatProfile"; -import { IGroup } from "../../../types"; -import { GroupInfoModal } from "./GroupInfoModal"; -import { isValidETHAddress } from "../helpers/helper"; -import { ethers } from "ethers"; -import { IChatProfile, IToast, OptionProps } from "../exportedTypes"; -import { InfuraAPIKey, allowedNetworks, device } from "../../../config"; -import Toast from "../helpers/Toast"; -import useMediaQuery from "../../../hooks/useMediaQuery"; -import { createBlockie } from "../../space/helpers/blockies"; -// import { NewToast } from "../helpers/NewToast"; -import { ToastContainer, toast } from 'react-toastify'; -import 'react-toastify/dist/ReactToastify.min.css'; - -const Options = ({ options, setOptions, isGroup, chatInfo, groupInfo, setGroupInfo, theme }: OptionProps) => { - const DropdownRef = useRef(null); - const [modal, setModal] = useState(false); - - useClickAway(DropdownRef, () => { - setOptions(false); - }); - - const ShowModal = () => { - setModal(true); - } - console.log(groupInfo?.rules?.chat, "groupInfooo") - - if (groupInfo && isGroup) { - return ( -
- { - (groupInfo?.rules?.chat?.conditions || groupInfo?.rules?.entry?.conditions) && ( - - ) - } - - {groupInfo?.isPublic && - ()} - - setOptions(true)}> - - - {options && - ( - - - - - Group Info - - - )} - - {modal && - ()} - -
- ) - } else { - return null - } +type OptionProps = { + options: boolean; + setOptions: React.Dispatch>; + isGroup: boolean; + groupInfo: IGroup | null | undefined; + setGroupInfo: React.Dispatch>; + theme: IChatTheme; }; +const Options = ({ + options, + setOptions, + isGroup, + groupInfo, + setGroupInfo, + theme, +}: OptionProps) => { + const DropdownRef = useRef(null); + const [modal, setModal] = useState(false); + + useClickAway(DropdownRef, () => { + setOptions(false); + }); + + const ShowModal = () => { + setModal(true); + }; + + if (groupInfo && isGroup) { + return ( +
+ {/* {(groupInfo?.rules?.chat?.conditions || groupInfo.rules?.entry?.conditions) && ( + + )} */} + + + setOptions(true)}> + + + {options && ( + + + + Group Info + + + )} + + {modal && ( + + )} + +
+ ); + } else { + return null; + } +}; - - -export const ChatProfile: React.FC = ({ chatId, style }: { chatId: string, style: "Info" | "Preview" }) => { - const theme = useContext(ThemeContext); - const { account, env } = useChatData(); - const { getGroupByID } = useGetGroupByID(); - const { fetchUserChatProfile } = useChatProfile(); - - const [isGroup, setIsGroup] = useState(false); - const [options, setOptions] = useState(false); - const [chatInfo, setChatInfo] = useState(); - const [groupInfo, setGroupInfo] = useState(); - const [ensName, setEnsName] = useState(''); - const isMobile = useMediaQuery(device.tablet); - const l1ChainId = allowedNetworks[env].includes(1) ? 1 : 5; - const provider = new ethers.providers.InfuraProvider(l1ChainId, InfuraAPIKey); - - - - const fetchProfileData = async () => { - if (isValidETHAddress(chatId)) { - const ChatProfile = await fetchUserChatProfile({ profileId: chatId }); - setChatInfo(ChatProfile); - setGroupInfo(null); - setIsGroup(false); - } else { - const GroupProfile = await getGroupByID({ groupId: chatId }) - setGroupInfo(GroupProfile); - setChatInfo(null); - setIsGroup(true); - } +export const ChatProfile: React.FC = ({ + chatId, + style, +}: { + chatId: string; + style: 'Info' | 'Preview'; +}) => { + const theme = useContext(ThemeContext); + const { account, env } = useChatData(); + const { getGroupByID } = useGetGroupByID(); + const { fetchUserChatProfile } = useChatProfile(); + + const [isGroup, setIsGroup] = useState(false); + const [options, setOptions] = useState(false); + const [chatInfo, setChatInfo] = useState(); + const [groupInfo, setGroupInfo] = useState(); + const [ensName, setEnsName] = useState(''); + const isMobile = useMediaQuery(device.tablet); + const l1ChainId = allowedNetworks[env].includes(1) ? 1 : 5; + const provider = new ethers.providers.InfuraProvider(l1ChainId, InfuraAPIKey); + + const fetchProfileData = async () => { + if (isValidETHAddress(chatId)) { + const ChatProfile = await fetchUserChatProfile({ profileId: chatId }); + const result = await resolveNewEns(chatId, provider); + setEnsName(result); + setChatInfo(ChatProfile); + setGroupInfo(null); + setIsGroup(false); + } else { + const GroupProfile = await getGroupByID({ groupId: chatId }); + setGroupInfo(GroupProfile); + setChatInfo(null); + setIsGroup(true); } - - const getName = async (chatId: string) => { - if (isValidETHAddress(chatId)) { - const result = await resolveNewEns(chatId, provider); - // if(result) - setEnsName(result); - } + }; + + const getImage = () => { + if (chatInfo || groupInfo) { + return isGroup + ? groupInfo?.groupImage ?? GreyImage + : chatInfo?.profile?.picture ?? + createBlockie?.(chatId)?.toDataURL()?.toString(); + } else { + return createBlockie?.(chatId)?.toDataURL()?.toString(); } - - - useEffect(() => { - if (!chatId) return; - fetchProfileData(); - getName(chatId); - }, [chatId, account, env]) - - if (chatId && style === 'Info') { - return ( - - {chatInfo || groupInfo ? ( - - ) : ()} - - - - {isGroup ? groupInfo?.groupName : ensName ? `${ensName} (${isMobile ? shortenText(chatInfo?.did?.split(':')[1] ?? '', 4, true) : chatId})` : chatInfo ? shortenText(chatInfo.did?.split(':')[1] ?? '', 6, true) : shortenText(chatId, 6, true)} - - - - - - {/* {!isGroup && + }; + + const getProfileName = () => { + return isGroup + ? groupInfo?.groupName + : ensName + ? `${ensName} (${isMobile + ? shortenText(chatInfo?.did?.split(':')[1] ?? '', 4, true) + : chatId + })` + : chatInfo + ? shortenText(chatInfo.did?.split(':')[1] ?? '', 6, true) + : shortenText(chatId, 6, true); + }; + + useEffect(() => { + if (!chatId) return; + fetchProfileData(); + }, [chatId, account, env]); + + if (chatId && style === 'Info') { + return ( + + + + + + {/* {!isGroup && } */} - - - - - ) - } else { - return null; - } -} - + + + ); + } else { + return null; + } +}; const Container = styled.div` width: 100%; background: ${(props) => props.theme.backgroundColor.chatProfileBackground}; - border:${(props) => props.theme.border?.chatProfile}; - border-radius:${(props) => props.theme.borderRadius?.chatProfile}; + border: ${(props) => props.theme.border?.chatProfile}; + border-radius: ${(props) => props.theme.borderRadius?.chatProfile}; display: flex; flex-direction: row; align-items: center; @@ -195,28 +237,21 @@ const ImageItem = styled.div` position: relative; `; -const DummyImage = styled.div` - height: 48px; - width: 48px; - border-radius: 100%; - background: #ccc; -`; - const DropDownBar = styled.div` - position: absolute; - top: 30px; - left: -130px; - display: block; - min-width: 140px; - color: rgb(101, 119, 149); - border: ${(props) => `1px solid ${props.theme.defaultBorder}`}; - background: ${(props) => props.theme.backgroundColor.chatReceivedBubbleBackground}; - z-index: 10; - border-radius: 16px; + position: absolute; + top: 30px; + left: -130px; + display: block; + min-width: 140px; + color: rgb(101, 119, 149); + background: ${(props) => props.theme.backgroundColor.modalBackground}; + border: ${(props) => props.theme.border.modalInnerComponents}; + z-index: 10; + border-radius: ${(props) => props.theme.borderRadius.modalInnerComponents}; `; const VideoChatSection = styled.div` - margin: 0 25px 0 auto; + margin: 0 25px 0 auto; `; const DropDownItem = styled(Span)` @@ -231,17 +266,8 @@ const DropDownItem = styled(Span)` `; const TextItem = styled(Span)` - white-space: nowrap; - overflow: hidden; + white-space: nowrap; + overflow: hidden; `; - - - - - - - - - - +//auto update members when an user accepts not done diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/GroupInfoModal.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/GroupInfoModal.tsx index e684cef63..aca69ae8f 100644 --- a/packages/uiweb/src/lib/components/chat/ChatProfile/GroupInfoModal.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatProfile/GroupInfoModal.tsx @@ -1,635 +1,969 @@ -import { useRef, useState } from "react"; -import styled from "styled-components"; -import { useChatData, useClickAway } from "../../../hooks"; -import { IGroup } from "../../../types"; -import { IChatTheme } from "../theme"; -import * as PushAPI from '@pushprotocol/restapi'; -import { IToast, ShadowedProps, UpdateGroupType } from "../exportedTypes"; -import { convertToWalletAddressList, getAdminList, getUpdatedAdminList, getUpdatedMemberList } from "../helpers/helper"; -import { DropdownValueType } from "./DropDown"; +import { useEffect, useRef, useState } from 'react'; + +import styled from 'styled-components'; +import { MdCheckCircle, MdError } from 'react-icons/md'; + +import { useChatData, useClickAway } from '../../../hooks'; +import { DropdownValueType } from '../reusables/DropDown'; +import { Section, Span, Image, Div } from '../../reusables/sharedStyling'; +import { AddWalletContent } from './AddWalletContent'; +import { Modal, ModalHeader } from '../reusables'; +import useMediaQuery from '../../../hooks/useMediaQuery'; +import useToast from '../reusables/NewToast'; +import useUpdateGroup from '../../../hooks/chat/useUpdateGroup'; +import { MemberProfileCard } from './MemberProfileCard'; +import { ProfileContainer } from '../reusables'; + +import { IGroup } from '../../../types'; +import { IChatTheme } from '../theme'; +import { device } from '../../../config'; +import { + convertToWalletAddressList, + getAdminList, + getUpdatedAdminList, + getUpdatedMemberList, + isAccountOwnerAdmin, +} from '../helpers/group'; +import LockIcon from '../../../icons/Lock.png'; +import LockSlashIcon from '../../../icons/LockSlash.png'; +import ArrowIcon from '../../../icons/CaretUp.svg'; +import addIcon from '../../../icons/addicon.svg'; import DismissAdmin from '../../../icons/dismissadmin.svg'; import AddAdmin from '../../../icons/addadmin.svg'; import Remove from '../../../icons/remove.svg'; -import { Section, Span, Image } from "../../reusables/sharedStyling"; -import CloseIcon from '../../../icons/close.svg'; -import { ProfileCard } from "./ProfileCard"; -import addIcon from '../../../icons/addicon.svg'; -import { pCAIP10ToWallet, shortenText } from "../../../helpers"; -import LockIcon from '../../../icons/Lock.png' -import LockSlashIcon from '../../../icons/LockSlash.png' -import { AddWalletContent } from './AddWalletContent' -import ArrowIcon from '../../../icons/CaretDown.svg' -import { Modal } from "../helpers/Modal"; -import { device } from "../../../config"; -import useMediaQuery from "../../../hooks/useMediaQuery"; -import useToast from "../helpers/NewToast"; -import { MdCheckCircle, MdError } from "react-icons/md"; - - +import { shortenText } from '../../../helpers'; +import TokenGatedIcon from '../../../icons/TokenGatedIcon.svg'; +import ConditionsComponent from '../CreateGroup/ConditionsComponent'; +import { ConditionArray } from '../exportedTypes'; +import { ACCESS_TYPE_TITLE } from '../constants'; + +const UPDATE_KEYS = { + REMOVE_MEMBER: 'REMOVE_MEMBER', + ADD_MEMBER: 'ADD_MEMBER', + REMOVE_ADMIN: 'REMOVE_ADMIN', + ADD_ADMIN: 'ADD_ADMIN', +} as const; + +type UpdateKeys = typeof UPDATE_KEYS[keyof typeof UPDATE_KEYS]; +const SUCCESS_MESSAGE = { + REMOVE_MEMBER: 'Removed Member successfully', + ADD_MEMBER: 'Group Invitation sent', + REMOVE_ADMIN: 'Admin added successfully', + ADD_ADMIN: 'Removed added successfully', +}; + +type PendingMembersProps = { + groupInfo?: IGroup | null; + setShowPendingRequests: React.Dispatch>; + showPendingRequests: boolean; + theme: IChatTheme; +}; + +interface ShadowedProps { + setPosition: boolean; +} -const PendingMembers = ({ groupInfo, setShowPendingRequests, showPendingRequests, theme }: {groupInfo?: IGroup | null, setShowPendingRequests: React.Dispatch>, showPendingRequests: boolean, theme: IChatTheme }) => { - if(groupInfo){ +const PendingMembers = ({ + groupInfo, + setShowPendingRequests, + showPendingRequests, + theme, +}: PendingMembersProps) => { + if (groupInfo) { return ( - + setShowPendingRequests(!showPendingRequests)} > - setShowPendingRequests(!showPendingRequests)}> - Pending Requests - {groupInfo?.pendingMembers?.length} - - - {/* */} + + Pending Requests + + {groupInfo?.pendingMembers?.length} + + - {showPendingRequests && ( -
- {groupInfo?.pendingMembers && groupInfo?.pendingMembers?.length > 0 && groupInfo?.pendingMembers.map((item) => ( - - - - - {shortenText(item?.wallet?.split(':')[1] ?? '', 6, true)} - + {showPendingRequests && ( +
+ {groupInfo?.pendingMembers && + groupInfo?.pendingMembers?.length > 0 && + groupInfo?.pendingMembers.map((item) => ( + + + + ))} +
+ )} + + ); + } else { + return null; + } +}; + +const dummyConditonsData: ConditionArray[] = [ + [{ operator: 'any' }], + [ + { + type: 'PUSH', + category: 'ERC20', + subcategory: 'holder', + data: { + contract: 'eip155:1:0xf418588522d5dd018b425E472991E52EBBeEEEEE', + amount: 1, + decimals: 18, + }, + }, + ], + [ + { operator: 'all' }, + { + type: 'PUSH', + category: 'ERC20', + subcategory: 'holder', + data: { + contract: 'eip155:137:0x58001cC1A9E17A20935079aB40B1B8f4Fc19EFd1', + amount: 1, + decimals: 18, + }, + }, + { + type: 'PUSH', + category: 'ERC721', + subcategory: 'holder', + data: { + contract: 'eip155:137:0x58001cC1A9E17A20935079aB40B1B8f4Fc19EFd1', + amount: 1, + decimals: 18, + }, + }, + { + type: 'GUILD', + category: 'ROLES', + subcategory: 'DEFAULT', + data: { + id: '1', + role: '346243', + comparison: 'all', + }, + }, + ], + // [ + // { operator: 'any' }, + // { + // type: 'PUSH', + // category: 'INVITE', + // subcategory: 'DEFAULT', + // data: { + // inviterRoles: 'ADMIN', + // }, + // }, + // { + // type: 'PUSH', + // category: 'INVITE', + // subcategory: 'DEFAULT', + // data: { + // inviterRoles: 'OWNER', + // }, + // }, + // ], +]; + +const dummySingleCondtionData: ConditionArray[] = dummyConditonsData[2].map( + (criteria) => [criteria] +); + +interface ConditionsInformationProps { + theme: IChatTheme; + groupInfo?: IGroup | null; +} -
- ))} +export const ConditionsInformation = ({ + theme, + groupInfo, +}: ConditionsInformationProps) => { + return ( +
+ {(groupInfo?.rules?.chat?.conditions || + groupInfo?.rules?.entry?.conditions) && ( + + )} + {Object.keys(ACCESS_TYPE_TITLE).map((key) => ( + <> + + { ACCESS_TYPE_TITLE[key as keyof typeof ACCESS_TYPE_TITLE]?.heading} + + + + + + ))}
- )} - - ) - } else {return null } + ); +}; + +interface GroupTypeProps { + theme: IChatTheme; + icon: string; + header: string; + subheader: string; + cursor?: string; + handleNextInformation?: () => void; } -export const GroupInfoModal = ({ theme, modal, setModal, groupInfo, setGroupInfo }: { theme: IChatTheme, modal: boolean, setModal: React.Dispatch>, groupInfo: IGroup, setGroupInfo: React.Dispatch> }) => { - const { account, env, pgpPrivateKey } = useChatData(); - const [showAddMoreWalletModal, setShowAddMoreWalletModal] = useState(false); - const [showPendingRequests, setShowPendingRequests] = useState(false); - const [memberList, setMemberList] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [selectedMemberAddress, setSelectedMemberAddress] = useState(null); - - const handleClose = () => onClose(); - const dropdownRef = useRef(null); - useClickAway(dropdownRef, () => setSelectedMemberAddress(null)); - const groupInfoToast = useToast(); - - - const groupCreator = groupInfo?.groupCreator; - const membersExceptGroupCreator = groupInfo?.members?.filter((x) => x.wallet?.toLowerCase() !== groupCreator?.toLowerCase()); - const groupMembers = [...membersExceptGroupCreator, ...groupInfo.pendingMembers]; - - - const updateGroup = async (options:UpdateGroupType) => { - const { groupInfo, connectedUser,adminList,memberList } = options; - const updateResponse = await PushAPI.chat.updateGroup({ - chatId: groupInfo?.chatId, - groupName: groupInfo?.groupName, - groupDescription: groupInfo?.groupDescription ?? '', - groupImage: groupInfo?.groupImage, - members: memberList, - admins: adminList, - account: connectedUser?.wallets, - pgpPrivateKey: pgpPrivateKey, - env: env, +const GroupTypeBadge = ({ + theme, + icon, + header, + subheader, + handleNextInformation, + cursor, +}: GroupTypeProps) => { + return ( +
+ + + +
+ + {header} + + + {subheader} + +
+
+
+ ); +}; + +type GroupSectionProps = GroupInfoModalProps & { + handleNextInformation: () => void; + handlePreviousInformation?: () => void; +}; + +type GroupInfoModalProps = { + theme: IChatTheme; + setModal: React.Dispatch>; + groupInfo: IGroup; + setGroupInfo: React.Dispatch>; +}; + +export const GROUPINFO_STEPS = { + GROUP_INFO: 1, + CRITERIA: 2, +} as const; + +export type GROUP_INFO_TYPE = + typeof GROUPINFO_STEPS[keyof typeof GROUPINFO_STEPS]; + +const GroupInformation = ({ + theme, + setModal, + groupInfo, + setGroupInfo, + handleNextInformation, +}: GroupSectionProps) => { + const { account } = useChatData(); + const [showAddMoreWalletModal, setShowAddMoreWalletModal] = + useState(false); + const [showPendingRequests, setShowPendingRequests] = + useState(false); + const [memberList, setMemberList] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [checkedValue, setchecked] = useState(false); + const [selectedMemberAddress, setSelectedMemberAddress] = useState< + string | null + >(null); + const { updateGroup } = useUpdateGroup(); + const isMobile = useMediaQuery(device.mobileL); + + const handleClose = () => onClose(); + const dropdownRef = useRef(null); + useClickAway(dropdownRef, () => setSelectedMemberAddress(null)); + const groupInfoToast = useToast(); + + const groupCreator = groupInfo?.groupCreator; + const membersExceptGroupCreator = groupInfo?.members?.filter( + (x) => x.wallet?.toLowerCase() !== groupCreator?.toLowerCase() + ); + + const groupMembers = [ + ...membersExceptGroupCreator, + ...groupInfo.pendingMembers, + ]; + + type UpdateGroupType = { + adminList: Array; + memberList: Array; + }; + + const handleUpdateGroup = async (options: UpdateGroupType) => { + const { adminList, memberList } = options || {}; + const updateResponse = await updateGroup({ + groupInfo, + memberList, + adminList, + }); + return { updateResponse }; + }; + + const handleAddRemove = async ( + options: UpdateGroupType & { updateKey: UpdateKeys } + ) => { + const { adminList, memberList, updateKey } = options || {}; + + try { + setIsLoading(true); + const { updateResponse } = await handleUpdateGroup({ + adminList, + memberList, + }); + + if (typeof updateResponse !== 'string') { + setGroupInfo(updateResponse); + + groupInfoToast.showMessageToast({ + toastTitle: 'Success', + toastMessage: SUCCESS_MESSAGE[updateKey], + toastType: 'SUCCESS', + getToastIcon: (size) => , }); - let updatedCurrentChat = null; - if(typeof updateResponse !== 'string') - { - updatedCurrentChat = groupInfo; - updatedCurrentChat = updateResponse; - } - return {updateResponse,updatedCurrentChat}; + } else { + groupInfoToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: updateResponse, + toastType: 'ERROR', + getToastIcon: (size) => , + }); + } + } catch (error) { + console.error('Error', error); + groupInfoToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: 'Please, try again', + toastType: 'ERROR', + getToastIcon: (size) => , + }); + } finally { + if (updateKey === UPDATE_KEYS.ADD_MEMBER) handleClose(); + setIsLoading(false); + setSelectedMemberAddress(null); } - - const addMembers = async () => { - //Already Present Members and PendingMembers - const groupMemberList = convertToWalletAddressList([ - ...groupInfo.members, - ...groupInfo.pendingMembers, - ]); - - //Newly Added Members and alreadyPresent Members in the groupchat - const newMembersToAdd = memberList.map((member: any) => member.wallets); - const members = [...groupMemberList, ...newMembersToAdd]; - - //Admins wallet address from both members and pendingMembers - const adminList = getAdminList?.(groupInfo); - - - try { - setIsLoading(true); - const connectedUser = await PushAPI.user.get({ account: account as string, env }); - const { updateResponse, updatedCurrentChat } = await updateGroup({ - groupInfo, - connectedUser, - adminList, - memberList: members, - }); - - if (typeof updateResponse !== 'string') { - setSelectedMemberAddress(null); - setGroupInfo(updateResponse); - } else { - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: updateResponse, - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - setSelectedMemberAddress(null); - } - setIsLoading(false); - groupInfoToast.showMessageToast({ - toastTitle: 'Success', - toastMessage: 'Group Invitation sent', - toastType: 'SUCCESS', - getToastIcon: (size) => ( - - ), - }); - handleClose(); - } catch (error) { - setIsLoading(false); - console.log('Error', error); - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: 'Please, try again', - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - } - }; - - - const makeGroupAdmin = async () => { - const groupMemberList = convertToWalletAddressList([ - ...groupInfo.members, - ...groupInfo.pendingMembers, - ]); - const newAdminList = getUpdatedAdminList(groupInfo, selectedMemberAddress, false); - try { - const connectedUser = await PushAPI.user.get({ account: account as string, env }); - const { updateResponse, updatedCurrentChat } = await updateGroup({ - groupInfo, - connectedUser, - adminList: newAdminList, - memberList: groupMemberList, - }); - if (typeof updateResponse !== 'string') { - setSelectedMemberAddress(null); - setGroupInfo(updateResponse); - - groupInfoToast.showMessageToast({ - toastTitle: 'Success', - toastMessage: 'Admin added successfully', - toastType: 'SUCCESS', - getToastIcon: (size) => ( - ), - }); - } else { - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: updateResponse, - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - setSelectedMemberAddress(null); - } - } catch (e) { - console.error('Error while adding admin', e); - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: 'Error', - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - } - setSelectedMemberAddress(null); - }; - - const dismissGroupAdmin = async () => { - const groupMemberList = convertToWalletAddressList([ - ...groupInfo.members, - ...groupInfo.pendingMembers, - ]); - const newAdminList = getUpdatedAdminList(groupInfo, selectedMemberAddress, true); - try { - const connectedUser = await PushAPI.user.get({ account: account as string, env }); - const { updateResponse, updatedCurrentChat } = await updateGroup({ - groupInfo, - connectedUser, - adminList: newAdminList, - memberList: groupMemberList, - }); - if (typeof updateResponse !== 'string') { - setSelectedMemberAddress(null); - setGroupInfo(updateResponse); - - groupInfoToast.showMessageToast({ - toastTitle: 'Success', - toastMessage: 'Admin removed successfully', - toastType: 'SUCCESS', - getToastIcon: (size) => ( - ), - }); - - } else { - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: updateResponse, - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - setSelectedMemberAddress(null); - } - } catch (e) { - console.error('Error while dismissing admin', e); - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: 'Please, try again', - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - } - setSelectedMemberAddress(null); - }; - - const removeMember = async () => { - const updatedMemberList = getUpdatedMemberList(groupInfo, selectedMemberAddress!); - const adminList = getUpdatedAdminList(groupInfo, selectedMemberAddress, true); - try { - const connectedUser = await PushAPI.user.get({ account: account as string, env }); - const { updateResponse, updatedCurrentChat } = await updateGroup({ - groupInfo, - connectedUser, - adminList, - memberList: updatedMemberList, - }); - - if (typeof updateResponse !== 'string') { - setSelectedMemberAddress(null); - setGroupInfo(updateResponse); - - groupInfoToast.showMessageToast({ - toastTitle: 'Success', - toastMessage: 'Removed Member successfully', - toastType: 'SUCCESS', - getToastIcon: (size) => ( - - ), - }); - } else { - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: updateResponse, - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - setSelectedMemberAddress(null); - } - } catch (error) { - console.error('Error in removing member', error); - groupInfoToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: 'Please, try again', - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); + }; + const removeMember = async () => { + const updatedMemberList = getUpdatedMemberList( + groupInfo, + selectedMemberAddress! + ); + const adminList = getUpdatedAdminList( + groupInfo, + selectedMemberAddress, + true + ); + await handleAddRemove({ + memberList: updatedMemberList, + adminList, + updateKey: UPDATE_KEYS.REMOVE_MEMBER, + }); + }; + + const addMembers = async () => { + //Already Present Members and PendingMembers + const groupMemberList = convertToWalletAddressList([ + ...groupInfo.members, + ...groupInfo.pendingMembers, + ]); + + //Newly Added Members and alreadyPresent Members in the groupchat + const newMembersToAdd = memberList.map((member: any) => member.wallets); + const members = [...groupMemberList, ...newMembersToAdd]; + + //Admins wallet address from both members and pendingMembers + const adminList = getAdminList?.(groupInfo); + + await handleAddRemove({ + memberList: members, + adminList, + updateKey: UPDATE_KEYS.ADD_MEMBER, + }); + }; + + const updateGroupAdmin = async (updateKey: UpdateKeys) => { + const groupMemberList = convertToWalletAddressList([ + ...groupInfo.members, + ...groupInfo.pendingMembers, + ]); + const newAdminList = getUpdatedAdminList( + groupInfo, + selectedMemberAddress, + !(updateKey === UPDATE_KEYS.ADD_ADMIN) + ); + await handleAddRemove({ + memberList: groupMemberList, + adminList: newAdminList, + updateKey, + }); + }; + + // const messageUserDropdown: DropdownValueType = { + // id: 'message_user', + // title: 'Message user', + // icon: Message, + // function: () => messageUser(), + // }; + + const removeAdminDropdown: DropdownValueType = { + id: 'dismiss_admin', + title: 'Dismiss as admin', + icon: DismissAdmin, + function: () => updateGroupAdmin(UPDATE_KEYS.REMOVE_ADMIN), + }; + const addAdminDropdown: DropdownValueType = { + id: 'add_admin', + title: 'Make group admin', + icon: AddAdmin, + function: () => updateGroupAdmin(UPDATE_KEYS.ADD_ADMIN), + }; + const removeMemberDropdown: DropdownValueType = { + id: 'remove_member', + title: 'Remove', + icon: Remove, + function: () => removeMember(), + textColor: '#ED5858', + }; + + //remove all testing things + + const a1: DropdownValueType = { + id: 'dismiss_admin', + title: 'Dismiss as admin', + function: () => updateGroupAdmin(UPDATE_KEYS.REMOVE_ADMIN), + }; + const a2: DropdownValueType = { + id: 'add_admin', + title: 'Make group admin', + function: () => updateGroupAdmin(UPDATE_KEYS.ADD_ADMIN), + }; + + const handlePrevious = () => { + setShowAddMoreWalletModal(false); + }; + + const onClose = (): void => { + setModal(false); + }; + + return ( +
+ + + Group Description + + + {groupInfo?.groupDescription} + + + messageUser(), - // }; - const removeAdminDropdown: DropdownValueType = { - id: 'dismiss_admin', - title: 'Dismiss as admin', - icon: DismissAdmin, - function: () => dismissGroupAdmin(), - }; - const addAdminDropdown: DropdownValueType = { - id: 'add_admin', - title: 'Make group admin', - icon: AddAdmin, - function: () => makeGroupAdmin(), - }; - const removeMemberDropdown: DropdownValueType = { - id: 'remove_member', - title: 'Remove', - icon: Remove, - function: () => removeMember(), - textColor: '#ED5858', - }; - - - - const isAccountOwnerAdmin = groupInfo?.members?.some( - (member) => pCAIP10ToWallet(member?.wallet)?.toLowerCase() === account?.toLowerCase() && member?.isAdmin - ); - - const handlePrevious = () => { - setShowAddMoreWalletModal(false); - }; - - const onClose = () => { - setModal(false); - } - - const isMobile = useMediaQuery(device.mobileL); - if(groupInfo){ - return( - - {!showAddMoreWalletModal && (
-
- -
- - Group Info - - onClose()} cursor='pointer' /> -
- - - - -
- {groupInfo?.groupName} - {groupInfo?.members?.length} Members -
-
- - - Group Description - {groupInfo?.groupDescription} - - - - - -
- {groupInfo?.isPublic ? 'Public' : 'Private'} - {groupInfo?.isPublic ? 'Chats are not encrypted' : 'Chats are encrypted'} -
-
+ /> + {/* {(groupInfo.rules?.chat?.conditions || + groupInfo.rules?.entry?.conditions) && ( + + )} */} + + {isAccountOwnerAdmin(groupInfo, account!) && + groupInfo?.members && + groupInfo?.members?.length < 10 && ( + setShowAddMoreWalletModal(true)} + > + - {isAccountOwnerAdmin && groupInfo?.members && groupInfo?.members?.length < 10 && ( - setShowAddMoreWalletModal(true)} - > - - - - Add more wallets - - )} - -
+ + Add more wallets + + + )} + +
{groupInfo?.pendingMembers?.length > 0 && ( - + )} +
+ +
+ {groupInfo?.members && + groupInfo?.members?.length > 0 && + groupInfo?.members.map((item, index) => ( + - )} + ))} +
+ {showAddMoreWalletModal && ( + + )}
+ ); +}; + +export const GroupInfoModal = ({ + theme, + setModal, + groupInfo, + setGroupInfo, +}: GroupInfoModalProps) => { + const [activeComponent, setActiveComponent] = useState( + GROUPINFO_STEPS.GROUP_INFO + ); + const handleNextInfo = () => { + console.log('criteria'); + setActiveComponent((activeComponent + 1) as GROUP_INFO_TYPE); + console.log(activeComponent); + }; + + const handlePreviousInfo = () => { + setActiveComponent((activeComponent - 1) as GROUP_INFO_TYPE); + }; + + const renderComponent = () => { + switch (activeComponent) { + case GROUPINFO_STEPS.GROUP_INFO: + return ( + + ); + case GROUPINFO_STEPS.CRITERIA: + return ; + + default: + return ( + + ); + } + }; + const [showAddMoreWalletModal, setShowAddMoreWalletModal] = + useState(false); + useState(false); + const [memberList, setMemberList] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [selectedMemberAddress, setSelectedMemberAddress] = useState< + string | null + >(null); + const { updateGroup } = useUpdateGroup(); + const isMobile = useMediaQuery(device.mobileL); + + const handleClose = () => onClose(); + const dropdownRef = useRef(null); + useClickAway(dropdownRef, () => setSelectedMemberAddress(null)); + const groupInfoToast = useToast(); + + const groupCreator = groupInfo?.groupCreator; + const membersExceptGroupCreator = groupInfo?.members?.filter( + (x) => x.wallet?.toLowerCase() !== groupCreator?.toLowerCase() + ); + + type UpdateGroupType = { + adminList: Array; + memberList: Array; + }; + + const handleUpdateGroup = async (options: UpdateGroupType) => { + const { adminList, memberList } = options || {}; + const updateResponse = await updateGroup({ + groupInfo, + memberList, + adminList, + }); + return { updateResponse }; + }; + + const handleAddRemove = async ( + options: UpdateGroupType & { updateKey: UpdateKeys } + ) => { + const { adminList, memberList, updateKey } = options || {}; + + try { + setIsLoading(true); + const { updateResponse } = await handleUpdateGroup({ + adminList, + memberList, + }); + + if (typeof updateResponse !== 'string') { + setGroupInfo(updateResponse); + + groupInfoToast.showMessageToast({ + toastTitle: 'Success', + toastMessage: SUCCESS_MESSAGE[updateKey], + toastType: 'SUCCESS', + getToastIcon: (size) => , + }); + } else { + groupInfoToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: updateResponse, + toastType: 'ERROR', + getToastIcon: (size) => , + }); + } + } catch (error) { + console.error('Error', error); + groupInfoToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: 'Please, try again', + toastType: 'ERROR', + getToastIcon: (size) => , + }); + } finally { + if (updateKey === UPDATE_KEYS.ADD_MEMBER) handleClose(); + setIsLoading(false); + setSelectedMemberAddress(null); + } + }; + const removeMember = async () => { + const updatedMemberList = getUpdatedMemberList( + groupInfo, + selectedMemberAddress! + ); + const adminList = getUpdatedAdminList( + groupInfo, + selectedMemberAddress, + true + ); + await handleAddRemove({ + memberList: updatedMemberList, + adminList, + updateKey: UPDATE_KEYS.REMOVE_MEMBER, + }); + }; + + const addMembers = async () => { + //Already Present Members and PendingMembers + const groupMemberList = convertToWalletAddressList([ + ...groupInfo.members, + ...groupInfo.pendingMembers, + ]); + + //Newly Added Members and alreadyPresent Members in the groupchat + const newMembersToAdd = memberList.map((member: any) => member.wallets); + const members = [...groupMemberList, ...newMembersToAdd]; + + //Admins wallet address from both members and pendingMembers + const adminList = getAdminList?.(groupInfo); + + await handleAddRemove({ + memberList: members, + adminList, + updateKey: UPDATE_KEYS.ADD_MEMBER, + }); + }; + + const updateGroupAdmin = async (updateKey: UpdateKeys) => { + const groupMemberList = convertToWalletAddressList([ + ...groupInfo.members, + ...groupInfo.pendingMembers, + ]); + const newAdminList = getUpdatedAdminList( + groupInfo, + selectedMemberAddress, + !(updateKey === UPDATE_KEYS.ADD_ADMIN) + ); + await handleAddRemove({ + memberList: groupMemberList, + adminList: newAdminList, + updateKey, + }); + }; + + const onClose = (): void => { + setModal(false); + }; + + if (groupInfo) { + return ( + + {!showAddMoreWalletModal && ( +
+ -
- {groupInfo?.members && groupInfo?.members?.length > 0 && groupInfo?.members.map((item, index) => ( - - ))} -
- -
)} - - - - {showAddMoreWalletModal && ( - - )} -
- ) -} else { return null } - -} + + -const ProfileDiv = styled.div<{minHeight?: number}>` - display: flex; - flex-direction: column; - justify-content: flex-start; - padding-right: 3px; - align-items: center; - min-width: 445px; - min-height: 72px; - max-height: 216px; - min-height: ${(props) => `${props.minHeight}px`}; - overflow-y: auto; - overflow-x: hidden; - &&::-webkit-scrollbar { - width: 4px; +
+ + {groupInfo?.groupName} + + + {groupInfo?.members?.length} Members + +
+
+ {renderComponent()} +
+ )} +
+ ); + } else { + return null; } - &&::-webkit-scrollbar-thumb { - background: #cf1c84; - border-radius: 10px; - } - @media (max-width: 480px) { - min-width: 300px; - } -`; +}; +//styles const GroupHeader = styled.div` - margin-top: 34px; - display: flex; - flex-direction: row; - width: 100%; - gap: 19px; + margin-top: 34px; + display: flex; + flex-direction: row; + width: 100%; + gap: 19px; `; const GroupDescription = styled.div` - margin-top: 34px; - display: flex; - flex-direction: column; - width: 100%; - align-items: flex-start; - gap: 5px; + margin-top: 34px; + display: flex; + flex-direction: column; + width: 100%; + align-items: flex-start; + gap: 5px; `; - const PublicEncrypted = styled.div` - margin-top: 20px; - display: flex; - flex-direction: row; - width: 100%; - gap: 19px; - align-items: center; - border: ${(props) => `1px solid ${props.theme.defaultBorder}`}; - border-radius: 16px; - padding: 16px; - box-sizing: border-box; -`; - -const GroupMembers = styled.div` - margin-top: 20px; - display: flex; - flex-direction: row; - width: 100%; - align-items: center; -`; - -const AdminItem = styled.div` - background: rgb(244, 220, 234); - color: rgb(213, 58, 148); - margin-left: auto; - font-size: 10px; - padding: 6px; - border-radius: 8px; + margin-top: 20px; + display: flex; + flex-direction: row; + width: 100%; + gap: 19px; + align-items: center; + border: ${(props) => props.theme.border.modalInnerComponents}; + border-radius: ${(props) => props.theme.borderRadius.modalInnerComponents}; + padding: 16px; + box-sizing: border-box; + background: ${(props) => props.theme.backgroundColor.modalHoverBackground}; `; const AddWalletContainer = styled.div` - margin-top: 20px; - border: ${(props) => `1px solid ${props.theme.defaultBorder}`}; - border-radius: 16px; - width: 100%; - padding: 20px 16px; - box-sizing: border-box; - display: flex; - flex-direction: row; - justify-content: center; - cursor: pointer; - align-items: center; + margin-top: 20px; + border: ${(props) => props.theme.border.modalInnerComponents}; + border-radius: ${(props) => props.theme.borderRadius.modalInnerComponents}; + width: 100%; + padding: 20px 16px; + box-sizing: border-box; + display: flex; + flex-direction: row; + justify-content: center; + cursor: pointer; + align-items: center; `; const GroupPendingMembers = styled.div` - margin-top: 3px; - display: flex; - flex-direction: row; - width: 100%; - align-items: center; - background: ${(props) => props.theme.pendingCardBackground}; - padding: 10px 15px; - box-sizing: border-box; - - &:last-child { - border-radius: 0px 0px 16px 16px; - } -`; + margin-top: 3px; + display: flex; + flex-direction: row; + width: 100%; + align-items: center; + background: ${(props) => props.theme.backgroundColor.modalHoverBackground}; + padding: 10px 15px; + box-sizing: border-box; + &:last-child { + border-radius: 0px 0px 16px 16px; + } +`; const PendingRequestWrapper = styled.div` - width: 100%; - margin-top: 20px; - border: ${(props) => `1px solid ${props.theme.defaultBorder}`}; - border-radius: 16px; - padding: 0px 0px; - box-sizing: border-box; + width: 100%; + margin-top: 20px; + border: ${(props) => props.theme.border.modalInnerComponents}; + border-radius: ${(props) => props.theme.borderRadius.modalInnerComponents}; + padding: 0px 0px; + box-sizing: border-box; `; const PendingSection = styled.div` - width: 100%; - display: flex; - flex-direction: row; - align-items: center; - flex: 1; - cursor: pointer; - padding: 15px 20px; - box-sizing: border-box; + width: 100%; + display: flex; + flex-direction: row; + align-items: center; + flex: 1; + cursor: pointer; + padding: 15px 20px; + box-sizing: border-box; `; const ArrowImage = styled(Image)` - margin-left: auto; - transform: ${(props) => props?.setPosition ? 'rotate(0)' : 'rotate(180deg)'}; + margin-left: auto; + transform: ${(props) => + props?.setPosition ? 'rotate(0)' : 'rotate(180deg)'}; `; - const Badge = styled.div` - margin: 0 0 0 5px; - font-size: 13px; - background: rgb(207, 28, 132); - padding: 4px 8px; - border-radius: 7px; - color: white; - font-weight: 700; + margin: 0 0 0 5px; + font-size: 13px; + background: rgb(207, 28, 132); + padding: 4px 8px; + border-radius: 7px; + color: white; + font-weight: 700; `; + +const ConditionSection = styled(Section)<{ theme: IChatTheme }>` + &::-webkit-scrollbar-thumb { + background: ${(props) => props.theme.scrollbarColor}; + border-radius: 10px; + } + &::-webkit-scrollbar-button { + height: 20px; + } + &::-webkit-scrollbar { + width: 4px; + } +`; + +//auto update members when an user accepts not done diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/MemberListContainer.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/MemberListContainer.tsx index c92529fef..52885bec5 100644 --- a/packages/uiweb/src/lib/components/chat/ChatProfile/MemberListContainer.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatProfile/MemberListContainer.tsx @@ -1,183 +1,201 @@ -import { useContext, useRef, useState } from "react"; -import { ThemeContext } from "../theme/ThemeProvider"; -import { useClickAway } from "../../../hooks"; -import Dropdown, { DropdownValueType } from "./DropDown"; +import { useContext, useRef, useState } from 'react'; + +import styled from 'styled-components'; + +import { ThemeContext } from '../theme/ThemeProvider'; +import { useClickAway } from '../../../hooks'; +import Dropdown, { DropdownValueType } from '../reusables/DropDown'; +import { Section, Span } from '../../reusables/sharedStyling'; + import DismissAdmin from '../../../icons/dismissadmin.svg'; import AddAdmin from '../../../icons/addadmin.svg'; import Remove from '../../../icons/remove.svg'; -import styled from "styled-components"; -import { Section, Image, Span } from "../../reusables/sharedStyling"; -import { MemberListContainerType, WalletProfileContainerProps } from "../exportedTypes"; -import { findObject } from "../helpers/helper"; -import { device } from "../../../config"; -import { shortenText } from "../../../helpers"; - -export const MemberListContainer = ({ key, memberData, handleMembers, handleMemberList, lightIcon, darkIcon, memberList }: MemberListContainerType) => { - const theme = useContext(ThemeContext); - const [selectedWallet, setSelectedWallet] = useState(null); +import { IChatTheme, User } from '../exportedTypes'; +import { findObject } from '../helpers/helper'; +import { device } from '../../../config'; +import { shortenText } from '../../../helpers'; +import { ProfileContainer } from '../reusables'; + + +type MemberListContainerType = { + key?: number; + memberData: User; + handleMemberList: (member: User) => void; + handleMembers?: (value: User[]) => void; + darkIcon: any; + memberList?: any; +}; + +export interface WalletProfileContainerProps { + id?: any; + background?: any; + border?: string; +}; + +export const MemberListContainer = ({ + key, + memberData, + handleMembers, + handleMemberList, + darkIcon, + memberList, +}: MemberListContainerType) => { + const theme = useContext(ThemeContext); + const [selectedWallet, setSelectedWallet] = useState(null); const [dropdownHeight, setDropdownHeight] = useState(0); const dropdownRef = useRef(null); - useClickAway(dropdownRef, () => setSelectedWallet(null)); - const removeAdminDropdown: DropdownValueType = - { id: 'dismiss_admin', title: 'Dismiss as admin', icon: DismissAdmin, function: () => dismissGroupAdmin() } + const removeAdminDropdown: DropdownValueType = { + id: 'dismiss_admin', + title: 'Dismiss as admin', + icon: DismissAdmin, + function: () => dismissGroupAdmin(), + }; - const addAdminDropdown: DropdownValueType = - { id: 'dismiss_admin', title: 'Make group admin', icon: AddAdmin, function: () => makeGroupAdmin() } + const addAdminDropdown: DropdownValueType = { + id: 'dismiss_admin', + title: 'Make group admin', + icon: AddAdmin, + function: () => makeGroupAdmin(), + }; - const removeUserDropdown: DropdownValueType = - { id: 'remove_user', title: 'Remove', icon: Remove, function: () => removeUser() } + const removeUserDropdown: DropdownValueType = { + id: 'remove_user', + title: 'Remove', + icon: Remove, + function: () => removeUser(), + }; const dismissGroupAdmin = () => { - const updatedMembers = memberList.map((member:any) => member?.wallets?.toLowerCase() == memberData?.wallets?.toLowerCase() ? ({ ...member, isAdmin: false }) : member) - handleMembers?.(updatedMembers) - setSelectedWallet(null) - } + const updatedMembers = memberList.map((member: any) => + member?.wallets?.toLowerCase() == memberData?.wallets?.toLowerCase() + ? { ...member, isAdmin: false } + : member + ); + handleMembers?.(updatedMembers); + setSelectedWallet(null); + }; const makeGroupAdmin = () => { - const updatedMembers = memberList.map((member: any) => member?.wallets?.toLowerCase() == memberData?.wallets?.toLowerCase() ? ({ ...member, isAdmin: true }) : member) - handleMembers?.(updatedMembers) - setSelectedWallet(null) - } + const updatedMembers = memberList.map((member: any) => + member?.wallets?.toLowerCase() == memberData?.wallets?.toLowerCase() + ? { ...member, isAdmin: true } + : member + ); + handleMembers?.(updatedMembers); + setSelectedWallet(null); + }; const removeUser = () => { - handleMemberList(memberData) - setSelectedWallet(null) - } - + handleMemberList(memberData); + setSelectedWallet(null); + }; const handleHeight = (id: any) => { - const containerHeight = document.getElementById(id)?.getBoundingClientRect(); + const containerHeight = document + .getElementById(id) + ?.getBoundingClientRect(); setDropdownHeight(containerHeight?.top); }; - - - return ( - - -
- -
- - {shortenText(memberData?.wallets?.split(':')[1], 8, true)} -
+ -
+
{memberData?.isAdmin && ( - + Admin - + )} -
{ - handleHeight(memberData?.wallets); - setSelectedWallet(null) - memberList - ? findObject(memberData, memberList, 'wallets') - ? setSelectedWallet(memberData?.wallets) - : handleMemberList(memberData) - : handleMemberList(memberData) - }} - > - {/* {theme === 'light' ? lightIcon : darkIcon} */} - {darkIcon} -
+
{ + handleHeight(memberData?.wallets); + setSelectedWallet(null); + memberList + ? findObject(memberData, memberList, 'wallets') + ? setSelectedWallet(memberData?.wallets) + : handleMemberList(memberData) + : handleMemberList(memberData); + }} + > + {darkIcon} +
{selectedWallet?.toLowerCase() == memberData?.wallets?.toLowerCase() && ( - 500 ? '30%' : "45%" }} ref={dropdownRef} theme={theme}> + 500 ? '30%' : '45%' }} + ref={dropdownRef} + theme={theme} + > )} - - - ) -} - + + ); +}; const WalletProfileContainer = styled(Section)` - // position: relative; - // padding: 5px 16px; - // margin: 8px 0px; - // justify-content: space-between; - // // min-width: 450px; - // min-width: 100%; - // box-sizing: border-box; - // align-items: center; - // border-radius: 16px; - - // @media (max-width: 480px) { - // // min-width: 300px; - // } - - justify-content: space-between; - padding: 8px 16px; - border-radius: 16px; - position: relative; - box-sizing: border-box; - width: 100%; - // background-color: ${(props) => props.theme.snapFocusBg}; - max-height: 64px; - align-self: stretch; - display: flex; - height: auto; - z-index: auto; - flex: 1; - @media (max-width: 480px) { - max-width: 100%; - } - -`; - -const WalletProfile = styled(Section)` - justify-content: flex-start; + justify-content: space-between; + padding: 8px 16px; + border: ${(props) => props.border}; + position: relative; + box-sizing: border-box; + width: 100%; + max-height: 64px; + align-self: stretch; + display: flex; + height: auto; + z-index: auto; + flex: 1; + @media (max-width: 480px) { + max-width: 100%; + } `; const DropdownContainer = styled.div` - // position: absolute; - // left: 48%; - // border-radius: 16px; - // padding: 14px 8px; - // background: ${(props) => props.theme.modalContentBackground}; - // border: 1px solid ${(props) => props.theme.modalBorderColor}; - // z-index: 400; - // @media ${device.mobileL} { - // left: 27%; - // } - // @media (min-width: 426px) and (max-width: 1150px) { - // left: 47%; - // } position: absolute; left: 48%; top: 69%; - border-radius: 16px; + border-radius: ${(props) => props.theme.borderRadius.modalInnerComponents}; padding: 14px 8px; z-index: 999999999999 !important; display: flex; flex-direction: column !important; - background: ${(props) => props.theme.modalContentBackground}; - border: 1px solid ${(props) => props.theme.modalBorderColor}; + background: ${(props) => props.theme.backgroundColor.modalBackground}; + border: ${(props) => props.theme.border.modalInnerComponents}; @media ${device.mobileL} { left: 27%; @@ -185,7 +203,7 @@ const DropdownContainer = styled.div` @media (min-width: 426px) and (max-width: 1150px) { left: 48%; } - @media (max-width: 480px){ + @media (max-width: 480px) { left: 25%; } -`; \ No newline at end of file +`; diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/MemberProfileCard.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/MemberProfileCard.tsx new file mode 100644 index 000000000..80b6bfa9f --- /dev/null +++ b/packages/uiweb/src/lib/components/chat/ChatProfile/MemberProfileCard.tsx @@ -0,0 +1,154 @@ +// React + Web3 Essentials +import React, { useContext, useState } from 'react'; + +// External Packages +import styled from 'styled-components'; + +// Internal Components +import { MoreLightIcon } from '../../../icons/MoreLight'; +import { shortenText } from '../../../helpers'; +import { ThemeContext } from '../theme/ThemeProvider'; +import { useChatData } from '../../../hooks'; +import { Section, Span } from '../../reusables'; +import Dropdown from '../reusables/DropDown'; +import { pCAIP10ToWallet } from '../../../helpers'; +import { device } from '../../../config'; +import { IChatTheme } from '../theme'; +import { ProfileContainer } from '../reusables'; + +type MemberProfileCardProps = { + key?: number | string; + member?: any; + dropdownValues?: any; + selectedMemberAddress?: any; + setSelectedMemberAddress?: any; + dropdownRef?: any; +}; + + +export const MemberProfileCard = ({ + key, + member, + dropdownValues, + selectedMemberAddress, + setSelectedMemberAddress, + dropdownRef, +}: MemberProfileCardProps) => { + const theme = useContext(ThemeContext); + const { account } = useChatData(); + + const [dropdownHeight, setDropdownHeight] = useState(0); + + const handleHeight = (id: any) => { + const containerHeight = document + .getElementById(id) + ?.getBoundingClientRect(); + setDropdownHeight(containerHeight?.top); + }; + + + return ( + + +
+ {member?.isAdmin && ( + + Admin + + )} + {pCAIP10ToWallet(member?.wallet)?.toLowerCase() !== + account?.toLowerCase() && + dropdownValues.length > 0 && ( +
{ + handleHeight(member.wallet); + setSelectedMemberAddress(member?.wallet); + }} + style={{ cursor: 'pointer' }} + > + +
+ )} +
+ {selectedMemberAddress?.toLowerCase() == + member?.wallet?.toLowerCase() && ( + 570 ? '30%' : '40%' }} + theme={theme} + ref={dropdownRef} + > + + + )} +
+ ); +}; + +//styles +const ProfileCardItem = styled(Section)<{ id: any; key: any; background: any }>` + justify-content: space-between; + padding: 8px 8px; + border-radius: 16px; + position: relative; + box-sizing: border-box; + width: 100%; + max-height: 64px; + align-self: stretch; + display: flex; + height: auto; + z-index: auto; + flex: 1; + @media (max-width: 480px) { + max-width: 100%; + } +`; + +const DropdownContainer = styled(Section)` + position: absolute; + left: 48%; + top: 69%; + border-radius: ${(props) => props.theme.borderRadius.modalInnerComponents}; + padding: 14px 8px; + z-index: 999999999999 !important; + display: flex; + flex-direction: column !important; + background: ${(props) => props.theme.backgroundColor.modalBackground}; + border: ${(props) => props.theme.border.modal}; + + @media ${device.mobileL} { + left: 27%; + } + @media (min-width: 426px) and (max-width: 1150px) { + left: 48%; + } + @media (max-width: 480px) { + left: 25%; + } +`; diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/ProfileCard.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/ProfileCard.tsx deleted file mode 100644 index 8244a3b24..000000000 --- a/packages/uiweb/src/lib/components/chat/ChatProfile/ProfileCard.tsx +++ /dev/null @@ -1,158 +0,0 @@ -// React + Web3 Essentials -import React, { useContext ,useState } from 'react'; - -// External Packages -import styled from 'styled-components'; -import { ethers } from 'ethers'; - -// Internal Components -import { MoreLightIcon } from '../../../icons/MoreLight'; -import { MoreDarkIcon } from '../../../icons/MoreDark'; -import { shortenText } from "../../../helpers"; -import { ThemeContext } from "../theme/ThemeProvider"; -import { useChatData, useClickAway} from "../../../hooks"; -import { Image, Section, Span } from "../../reusables"; -import Dropdown from './DropDown'; -import { pCAIP10ToWallet } from '../../../helpers'; -import { device } from "../../../config"; - - -type ProfileCardProps = { - key?: number | string, - member?: any, - dropdownValues?: any; - selectedMemberAddress?: any; - setSelectedMemberAddress?: any; - dropdownRef?: any; -} - -export const ProfileCard = ({ - key, - member, - dropdownValues, - selectedMemberAddress, - setSelectedMemberAddress, - dropdownRef, -}: ProfileCardProps) => { - const theme = useContext(ThemeContext); - const { account } = useChatData(); - - const [dropdownHeight, setDropdownHeight] = useState(0); - - const handleHeight = (id: any) => { - const containerHeight = document.getElementById(id)?.getBoundingClientRect(); - setDropdownHeight(containerHeight?.top); - }; - - return ( - -
-
- -
- - {shortenText(member?.wallet?.split(':')[1], 6, true)} - -
-
- {member?.isAdmin && ( - - Admin - - )} - {pCAIP10ToWallet(member?.wallet)?.toLowerCase() !== account?.toLowerCase() && dropdownValues.length > 0 && ( -
{ - handleHeight(member.wallet); - setSelectedMemberAddress(member?.wallet) - }} - style={{ cursor: 'pointer' }} - > - -
- )} -
- {selectedMemberAddress?.toLowerCase() == member?.wallet?.toLowerCase() && ( - 570 ? '30%' : '40%' }} - theme={theme} - ref={dropdownRef}> - - - )} -
- ); -}; - -const ProfileCardItem = styled(Section)<{id: any, key: any, background: any}>` - justify-content: space-between; - padding: 8px 16px; - border-radius: 16px; - position: relative; - box-sizing: border-box; - width: 100%; - // background-color: ${(props) => props.theme.snapFocusBg}; - max-height: 64px; - align-self: stretch; - display: flex; - height: auto; - z-index: auto; - flex: 1; - @media (max-width: 480px) { - max-width: 100%; - } -`; - -const DropdownContainer = styled(Section)` - position: absolute; - left: 48%; - top: 69%; - border-radius: 16px; - padding: 14px 8px; - z-index: 999999999999 !important; - display: flex; - flex-direction: column !important; - background: ${(props) => props.theme.modalContentBackground}; - border: 1px solid ${(props) => props.theme.modalBorderColor}; - - @media ${device.mobileL} { - left: 27%; - } - @media (min-width: 426px) and (max-width: 1150px) { - left: 48%; - } - @media (max-width: 480px){ - left: 25%; - } -`; diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx index f527c883b..1b388c4ed 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx @@ -1,8 +1,23 @@ -import { useContext, useEffect, useState } from 'react'; -import { Section, Span, Image } from '../../reusables'; +import { + ReactElement, + ReactNode, + useContext, + useEffect, + useState, +} from 'react'; + import moment from 'moment'; import styled from 'styled-components'; +import { TwitterTweetEmbed } from 'react-twitter-embed'; + +import { Section, Span, Image } from '../../reusables'; +import { checkTwitterUrl } from '../helpers/twitter'; +import { ChatDataContext } from '../../../context'; +import { useChatData } from '../../../hooks'; +import { ThemeContext } from '../theme/ThemeProvider'; + import { FileMessageContent } from '../../../types'; +import { IMessagePayload, TwitterFeedReturnType } from '../exportedTypes'; import { FILE_ICON } from '../../../config'; import { formatFileSize, @@ -10,12 +25,6 @@ import { pCAIP10ToWallet, shortenText, } from '../../../helpers'; -import { checkTwitterUrl } from '../helpers/twitter'; -import { IMessagePayload, TwitterFeedReturnType } from '../exportedTypes'; -import { TwitterTweetEmbed } from 'react-twitter-embed'; -import { ChatDataContext } from '../../../context'; -import { useChatData } from '../../../hooks'; -import { ThemeContext } from '../theme/ThemeProvider'; const SenderMessageAddress = ({ chat }: { chat: IMessagePayload }) => { const { account } = useContext(ChatDataContext); @@ -73,19 +82,18 @@ const SenderMessageProfilePicture = ({ chat }: { chat: IMessagePayload }) => { ); }; - -//can create a wrapper for till the senderMessageAddress and use it for all cards(types of messages) -const MessageCard = ({ +const MessageWrapper = ({ chat, - position, + children, isGroup, + maxWidth, }: { chat: IMessagePayload; - position: number; + children: ReactNode; isGroup: boolean; + maxWidth?: string; }) => { const theme = useContext(ThemeContext); - const time = moment(chat.timestamp).format('hh:mm a'); return (
{isGroup && }
{isGroup && } -
+ +
+ ); +}; + +const MessageCard = ({ + chat, + position, + isGroup, +}: { + chat: IMessagePayload; + position: number; + isGroup: boolean; +}) => { + const theme = useContext(ThemeContext); + const time = moment(chat.timestamp).format('hh:mm a'); + return ( + +
+ {' '} +
+ {chat.messageContent.split('\n').map((str) => ( + + {str} + + ))} +
+ - {' '} -
- {chat.messageContent.split('\n').map((str) => ( - - {str} - - ))} -
- - {time} - -
+ {time} +
-
+ ); }; @@ -189,51 +216,42 @@ const FileCard = ({ const size = fileContent.size; return ( -
- {isGroup && } -
- {isGroup && } -
- extension icon -
- - {shortenText(name, 11)} - - - {formatFileSize(size)} - -
- - + +
+ extension icon +
+ + {shortenText(name, 11)} + + + {formatFileSize(size)} +
+ +
-
+ ); }; @@ -247,27 +265,21 @@ const ImageCard = ({ isGroup: boolean; }) => { return ( -
- {isGroup && } -
- {isGroup && } -
- -
+ +
+
-
+ ); }; @@ -281,32 +293,21 @@ const GIFCard = ({ isGroup: boolean; }) => { return ( -
- {isGroup && } -
- {isGroup && } -
- -
+ +
+
-
+ ); }; @@ -322,31 +323,21 @@ const TwitterCard = ({ position: number; }) => { return ( -
- {isGroup && } -
- {isGroup && } -
- -
+ +
+
-
+ ); }; export const ChatViewBubble = ({ chat }: { chat: IMessagePayload }) => { - const { account} = - useChatData(); + const { account } = useChatData(); const position = pCAIP10ToWallet(chat.fromDID).toLowerCase() !== account?.toLowerCase() ? 0 @@ -367,8 +358,6 @@ export const ChatViewBubble = ({ chat }: { chat: IMessagePayload }) => { } }, [chat.toDID, isGroup]); - - if (messageType === 'TwitterFeedLink') { chat.messageType = 'TwitterFeedLink'; } diff --git a/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx b/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx index a7011cd74..b63b1ee00 100644 --- a/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx @@ -76,7 +76,6 @@ export const ChatViewList: React.FC = ( setChatStatusText(''); }, [chatId, account, env]); - //need to do something about fetching connectedUser in every component useEffect(() => { (async () => { if (!connectedProfile && account) { @@ -129,6 +128,7 @@ export const ChatViewList: React.FC = ( setLoading(false); })(); }, [chatId, pgpPrivateKey, account, env]); + //moniters socket changes useEffect(() => { if (checkIfSameChat(messagesSinceLastConnection, account!, chatId)) { @@ -166,22 +166,6 @@ export const ChatViewList: React.FC = ( } } }, [groupInformationSinceLastConnection]); - // useEffect(() => { - // if ( - // Object.keys(messagesSinceLastConnection || {}).length && - // Object.keys(chatFeed || {}).length && - // checkIfSameChat(messagesSinceLastConnection, account!, chatId) - // ) { - - // } - // }, [messagesSinceLastConnection]); - // useEffect(() => { - // (async function () { - // if (!account && !env && !chatId) return; - // const hash = await fetchConversationHash({ conversationId: chatId }); - // setConversationHash(hash?.threadHash); - // })(); - // }, [chatId, account, env, pgpPrivateKey]); useEffect(() => { if (conversationHash) { diff --git a/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx b/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx index f739f6e0a..b0090d41a 100644 --- a/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx +++ b/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx @@ -1,15 +1,17 @@ -import styled from 'styled-components'; -import { IChatTheme } from '../theme'; -import { useChatData } from '../../../hooks'; -import * as PushAPI from '@pushprotocol/restapi'; import { useContext, useEffect, useState } from 'react'; -import { init, useConnectWallet } from "@web3-onboard/react"; -import injectedModule from "@web3-onboard/injected-wallets"; + +import styled from 'styled-components'; import { Signer, ethers } from 'ethers'; +import { useAccount, useChatData } from '../../../hooks'; import { ThemeContext } from '../theme/ThemeProvider'; -import { device } from '../../../config'; +import useGetChatProfile from '../../../hooks/useGetChatProfile'; +import useCreateChatProfile from '../../../hooks/useCreateChatProfile'; +import useDecryptPGPKey from '../../../hooks/useDecryptPGPKey'; + import { getAddressFromSigner } from '../../../helpers'; +import { IChatTheme } from '../theme'; +import { device } from '../../../config'; /** * @interface IThemeProps @@ -18,12 +20,12 @@ import { getAddressFromSigner } from '../../../helpers'; interface IThemeProps { theme?: IChatTheme; } - interface IConnectButtonProps { autoConnect?: boolean; } -export const ConnectButtonSub: React.FC = ({autoConnect = false}) => { - const [{ wallet, connecting }, connect, disconnect] = useConnectWallet(); + +export const ConnectButtonSub = ({autoConnect = false}) => { + const {wallet, connecting , connect, disconnect} = useAccount(); const { signer, @@ -35,11 +37,14 @@ export const ConnectButtonSub: React.FC = ({autoConnect = f setSigner, } = useChatData(); const theme = useContext(ThemeContext); + const {fetchChatProfile} = useGetChatProfile(); + const {creteChatProfile} = useCreateChatProfile(); + const {decryptPGPKey} = useDecryptPGPKey(); - const newFunc = () => { + + const setUserData = () => { if (wallet) { (async () => { - const ethersProvider = new ethers.providers.Web3Provider(wallet.provider, 'any') const signer = ethersProvider.getSigner() const newAdd = await getAddressFromSigner(signer) @@ -52,14 +57,12 @@ export const ConnectButtonSub: React.FC = ({autoConnect = f setPgpPrivateKey(null) } } - useEffect(() => { if(wallet && !autoConnect) disconnect(wallet); - newFunc() + setUserData() }, [wallet]) - useEffect(() => { (async () => { if (account && signer) { @@ -68,24 +71,23 @@ export const ConnectButtonSub: React.FC = ({autoConnect = f })(); }, [account, signer]); + const handleUserCreation = async () => { if (!account && !env) return; try { - let user = await PushAPI.user.get({ account: account!, env: env }); + let user = await fetchChatProfile({ profileId: account! ,env}); if (!user) { if (!signer) return; - user = await PushAPI.user.create({ - signer: signer, - env: env, - }); + user = await creteChatProfile({ signer: signer ,env}); } if (user?.encryptedPrivateKey && !pgpPrivateKey) { - const decryptPgpKey = await PushAPI.chat.decryptPGPKey({ - encryptedPGPPrivateKey: user.encryptedPrivateKey, + const decryptPgpKey = await decryptPGPKey({ + encryptedPrivateKey: user.encryptedPrivateKey, account: account!, signer: signer, env: env, }); + if(decryptPgpKey) setPgpPrivateKey(decryptPgpKey); } } catch (e: any) { @@ -107,10 +109,10 @@ const ConnectButtonDiv = styled.div` button{ background: ${(props) => `${props.theme.backgroundColor.buttonBackground}!important`}; - // color: ${(props) => `${props.theme.backgroundColor.buttonText}!important`}; - color: #fff; + color: ${(props) => `${props.theme.textColor.buttonText}!important`}; text-align:center; font-size: 1em; + cursor:pointer; border-radius: 10px; padding: 10px 20px; outline: none; @@ -126,4 +128,7 @@ const ConnectButtonDiv = styled.div` @media ${device.mobileL} { font-size: 12px; } -`; + body.modal-open { + overflow-y: hidden; + } +`; \ No newline at end of file diff --git a/packages/uiweb/src/lib/components/chat/ConnectButton/index.tsx b/packages/uiweb/src/lib/components/chat/ConnectButton/index.tsx index 6994cbb3c..b6b603fdf 100644 --- a/packages/uiweb/src/lib/components/chat/ConnectButton/index.tsx +++ b/packages/uiweb/src/lib/components/chat/ConnectButton/index.tsx @@ -2,30 +2,30 @@ import { IChatTheme } from '../theme'; import coinbaseWalletModule from '@web3-onboard/coinbase' import { ConnectButtonSub } from './ConnectButton'; -import { InfuraAPIKey } from '../../../config'; +import { BLOCKNATIVE_PROJECT_ID, InfuraAPIKey } from '../../../config'; import { Web3OnboardProvider } from '@web3-onboard/react'; import injectedModule, { ProviderLabel } from '@web3-onboard/injected-wallets'; import walletConnectModule from '@web3-onboard/walletconnect' import init from '@web3-onboard/core'; -import { ethers } from 'ethers'; +const APP_META_DATA = { + name: 'Push Protocol', + icon: 'https://files.slack.com/files-pri/T011WQBLH39-F05QWQA0MSR/pushlogoblocknative.png', + description: 'Example showcasing how to connect a wallet.', -/** - * @interface IThemeProps - * this interface is used for defining the props for styled components - */ -interface IThemeProps { - theme?: IChatTheme; + recommendedInjectedWallets: [ + { name: 'MetaMask', url: 'https://metamask.io' }, + ] } const wcv2InitOptions = { - projectId: '64a44a0fb537407bfe97d24330e4109c', + projectId: BLOCKNATIVE_PROJECT_ID, requiredChains: [1, 56] } const walletConnect = walletConnectModule(wcv2InitOptions) const coinbaseWalletSdk = coinbaseWalletModule({ darkMode: true }) -const chains = [ +const CHAINS = [ { id: '0x1', token: 'ETH', @@ -69,20 +69,11 @@ const wallets = [injectedModule(), walletConnect, coinbaseWalletSdk] -const appMetadata = { - name: 'Push Protocol', - icon: 'https://push.org/static/media/PushLogoTextBlack.fa01629c2ebd2149bab979861756591d.svg', - description: 'Example showcasing how to connect a wallet.', - - recommendedInjectedWallets: [ - { name: 'MetaMask', url: 'https://metamask.io' }, - ] -} const web3OnBoard = init({ wallets, - chains, - appMetadata, + chains:CHAINS, + appMetadata:APP_META_DATA, accountCenter: { desktop: { enabled: false diff --git a/packages/uiweb/src/lib/components/chat/CreateGroup/AddButtons.tsx b/packages/uiweb/src/lib/components/chat/CreateGroup/AddButtons.tsx new file mode 100644 index 000000000..73b56e0c5 --- /dev/null +++ b/packages/uiweb/src/lib/components/chat/CreateGroup/AddButtons.tsx @@ -0,0 +1,27 @@ +import React, { useContext } from 'react' + +import { Button } from '../reusables' +import { ThemeContext } from '../theme/ThemeProvider'; + +interface AddButtonsProps { + title: string; + handleNext?: () => void; +} + +export const AddButtons = ({title, handleNext}: AddButtonsProps) => { + const theme = useContext(ThemeContext); + return ( + + ) +} diff --git a/packages/uiweb/src/lib/components/chat/CreateGroup/AddCriteria.tsx b/packages/uiweb/src/lib/components/chat/CreateGroup/AddCriteria.tsx new file mode 100644 index 000000000..8f9bbd0c2 --- /dev/null +++ b/packages/uiweb/src/lib/components/chat/CreateGroup/AddCriteria.tsx @@ -0,0 +1,557 @@ +import { useContext, useEffect, useState } from 'react'; + +import { + Button, + DropDownInput, + DropdownValueType, + ModalHeader, + TextInput, +} from '../reusables'; +import { Section, } from '../../reusables'; +import useMediaQuery from '../../../hooks/useMediaQuery'; +import { GatingRulesInformation, ModalHeaderProps } from './CreateGroupModal'; +import { useChatData } from '../../../hooks'; +import { QuantityInput } from '../reusables/QuantityInput'; +import { ThemeContext } from '../theme/ThemeProvider'; +import { Checkbox } from '../reusables/Checkbox'; +import OptionButtons from '../reusables/OptionButtons'; + +import EthereumSvg from '../../../icons/ethereum.svg'; +import PolygonSvg from '../../../icons/polygon.svg'; +import BSCSvg from '../../../icons/bsc.svg'; +import OptimismSvg from '../../../icons/optimisim.svg'; +import { BLOCKCHAIN_NETWORK, device } from '../../../config'; +import { GUILD_COMPARISON_OPTIONS, INVITE_CHECKBOX_LABEL } from '../constants'; +import { + CATEGORY, + DropdownCategoryValuesType, + DropdownSubCategoryValuesType, + SUBCATEGORY, + TYPE, + SubCategoryKeys, + TypeKeys, + ReadonlyInputType, +} from '../types'; +import { Data, GuildData, PushData, Rule } from '../types/tokenGatedGroupCreationType'; + + + +const AddCriteria = ({ + handlePrevious, + handleNext, + onClose, + criteriaStateManager +}: ModalHeaderProps) => { + const [selectedTypeValue, setSelectedTypeValue] = useState(0); + const [selectedCategoryValue, setSelectedCategoryValue] = useState(0); + const [selectedSubCategoryValue, setSelectedSubCategoryValue] = + useState(0); + const [guildComparison, setGuildComparison] = useState('') + const [selectedChainValue, setSelectedChainValue] = useState(0); + const [contract, setContract] = useState(''); + const [inviteCheckboxes, setInviteCheckboxes] = useState<{ + admin: boolean; + owner: boolean; + }>({ admin: true, owner: true }); + const [url, setUrl] = useState(''); + const [guildId, setGuildId] = useState(''); + const [specificRoleId, setSpecificRoleId] = useState(''); + + const [quantity, setQuantity] = useState<{ value: number; range: number }>({ + value: 0, + range: 0, + }); + const { env } = useChatData(); + const theme = useContext(ThemeContext); + + const isMobile = useMediaQuery(device.mobileL); + + const dropdownQuantityRangeValues: Array = [ + { + id: 0, + title: 'Greater than', + value: '>', + function: () => setQuantity({ ...quantity, range: 0 }), + }, + { + id: 1, + title: 'Greater or equal to', + value: '>=', + function: () => setQuantity({ ...quantity, range: 1 }), + }, + { + id: 2, + title: 'Less than', + value: '<', + function: () => setQuantity({ ...quantity, range: 2 }), + }, + { + id: 3, + title: 'Less or equal to', + value: '<=', + function: () => setQuantity({ ...quantity, range: 3 }), + }, + { + id: 4, + title: 'Equal to', + value: '==', + function: () => setQuantity({ ...quantity, range: 4 }), + }, + { + id: 5, + title: 'Not equal to', + value: '!=', + function: () => setQuantity({ ...quantity, range: 5 }), + }, + ]; + const dropdownTypeValues: Array = [ + { + id: 0, + title: 'Push protocol', + value: TYPE.PUSH, + function: () => setSelectedTypeValue(0), + }, + { + id: 1, + title: 'Guild', + value: TYPE.GUILD, + function: () => setSelectedTypeValue(1), + }, + ]; + const dropdownCategoryValues: DropdownCategoryValuesType = { + PUSH: [ + { + id: 0, + value: CATEGORY.ERC20, + title: 'Token ERC20', + function: () => setSelectedCategoryValue(0), + }, + { + id: 1, + value: CATEGORY.ERC721, + title: 'NFT ERC721', + function: () => setSelectedCategoryValue(1), + }, + { + id: 2, + value: CATEGORY.INVITE, + title: 'Invite', + function: () => setSelectedCategoryValue(2), + }, + { + id: 3, + value: CATEGORY.CustomEndpoint, + title: 'Custom Endpoint', + function: () => setSelectedCategoryValue(3), + }, + ], + GUILD: { + value: CATEGORY.ROLES, + title: 'Roles', + }, + }; + + const tokenCategoryValues = [ + { + id: 0, + value: SUBCATEGORY.HOLDER, + title: 'Holder', + function: () => setSelectedSubCategoryValue(0), + }, + { + id: 1, + value: SUBCATEGORY.OWENER, + title: 'Owner', + function: () => setSelectedSubCategoryValue(1), + }, + ]; + const dropdownSubCategoryValues: DropdownSubCategoryValuesType = { + ERC20: tokenCategoryValues, + ERC721: tokenCategoryValues, + INVITE: { + value: SUBCATEGORY.DEFAULT, + title: 'Default', + }, + CustomEndpoint: [ + { + id: 0, + value: SUBCATEGORY.GET, + title: 'Get', + function: () => setSelectedSubCategoryValue(0), + }, + ], + ROLES: { + value: SUBCATEGORY.DEFAULT, + title: 'Default', + }, + }; + + const dropdownChainsValues: Array = [ + { + id: 0, + value: BLOCKCHAIN_NETWORK[env].ETHEREUM, + title: 'Ethereum', + icon: EthereumSvg, + function: () => setSelectedChainValue(0), + }, + { + id: 1, + value: BLOCKCHAIN_NETWORK[env].POLYGON, + title: 'Polygon', + icon: PolygonSvg, + function: () => setSelectedChainValue(1), + }, + { + id: 2, + value: BLOCKCHAIN_NETWORK[env].BSC, + title: 'BSC', + icon: BSCSvg, + function: () => setSelectedChainValue(2), + }, + { + id: 3, + value: BLOCKCHAIN_NETWORK[env].OPTIMISM, + title: 'Optimism', + icon: OptimismSvg, + function: () => setSelectedChainValue(3), + }, + ]; + + const getCategoryDropdownValues = () => { + return dropdownCategoryValues[ + dropdownTypeValues[selectedTypeValue].value as TypeKeys + ]; + }; + + const getSelectedCategoryValue = () => { + const category = getCategoryDropdownValues(); + if (Array.isArray(category)) + return (category as DropdownValueType[])[selectedCategoryValue].value!; + else return category.value! as SubCategoryKeys; + }; + + const getSelectedSubCategoryValue = () => { + const subCategory = getSubCategoryDropdownValues(); + if (Array.isArray(subCategory)) + return (subCategory as DropdownValueType[])[selectedCategoryValue].value!; + else return subCategory.value! as SubCategoryKeys; + }; + + const checkIfTokenNFT = () => { + const category = getSelectedCategoryValue(); + if (category === CATEGORY.ERC20 || category === CATEGORY.ERC721) + return true; + + return false; + }; + + const checkIfCustomEndpoint = () => { + const category = getSelectedCategoryValue(); + if (category === CATEGORY.CustomEndpoint) return true; + return false; + }; + + const checkIfPushInvite = () => { + const accessType = dropdownTypeValues[selectedTypeValue].value; + if (accessType === TYPE.PUSH) { + const category = getSelectedCategoryValue(); + if (category === CATEGORY.INVITE) return true; + } + + return false; + }; + + const checkIfGuild = () => { + const accessType = dropdownTypeValues[selectedTypeValue].value; + if (accessType === TYPE.GUILD) { + return true; + } + + return false; + }; + + const getSubCategoryDropdownValues = () => { + const category = getCategoryDropdownValues(); + if (Array.isArray(category)) + return dropdownSubCategoryValues[ + (category as DropdownValueType[])[selectedCategoryValue] + .value as SubCategoryKeys + ]; + else return dropdownSubCategoryValues[category.value as SubCategoryKeys]; + }; + + const onQuantityChange = (e: any) => { + setQuantity({ ...quantity, value: e.target.value }); + }; + + const verifyAndDoNext = ()=>{ + const _type = dropdownTypeValues[selectedTypeValue].value as 'PUSH' | 'GUILD' + const category:string = _type === "PUSH" ? (dropdownCategoryValues[_type] as DropdownValueType[])[ + selectedCategoryValue + ].value || CATEGORY.ERC20 : "ROLES" + + let subCategory = "DEFAULT" + if(_type === "PUSH"){ + if(category === CATEGORY.ERC20 || category === CATEGORY.ERC721){ + subCategory = tokenCategoryValues[selectedSubCategoryValue].value + }else if(category === CATEGORY.CustomEndpoint){ + subCategory = "GET" + } + } + + const getData = (type:string, category:string):Data=>{ + if(type === "PUSH"){ + if(category === CATEGORY.ERC20 || category === CATEGORY.ERC721){ + const selectedChain = dropdownChainsValues[selectedChainValue].value || 'eip155:1'; + return { + contract: `${selectedChain}:${contract}`, + amount: quantity.value, + comparison:dropdownQuantityRangeValues[quantity.range].value, + decimals: 18, + } + }else if(category === CATEGORY.INVITE){ + const _inviteRoles = [] + if(inviteCheckboxes.admin){ + _inviteRoles.push("ADMIN") + } + if(inviteCheckboxes.owner){ + _inviteRoles.push("OWNER") + } + + return{ + inviterRoles: _inviteRoles as ['OWNER' | 'ADMIN'] + } + }else{ + // CATEGORY.CustomEndpoint + // TODO: validate url + return{ + url:url + } + } + }else{ + // GUILD type + return { + id:guildId, + comparison:guildComparison, + role:guildComparison === 'specific' ? specificRoleId : "*", + } + } + } + + const rule:Rule = { + type: _type, + category: category, + subcategory: subCategory, + data: getData(_type, category), + } + + criteriaState.addNewRule(rule) + + if(handlePrevious){ + handlePrevious() + } + + } + + const criteriaState = criteriaStateManager.getSelectedCriteria() + + + // Autofill the form for the update + useEffect(()=>{ + if(criteriaState.isUpdateCriteriaEnabled()){ + //Load the states + const oldValue = criteriaState.selectedRules[criteriaState.updateCriteriaIdx] + + if(oldValue.type === 'PUSH'){ + + // category + setSelectedCategoryValue( + (dropdownCategoryValues.PUSH as DropdownValueType[]).findIndex(obj => obj.value === oldValue.category) + ) + + const pushData = oldValue.data as PushData + + // sub category + if(oldValue.category === CATEGORY.ERC20 || oldValue.category === CATEGORY.ERC721){ + setSelectedSubCategoryValue( + tokenCategoryValues.findIndex(obj => obj.value === oldValue.subcategory) + ) + + const contractAndChain:string[] = (pushData.contract || "eip155:1:0x").split(':') + setSelectedChainValue( + dropdownChainsValues.findIndex( + obj => obj.value === contractAndChain[0]+":"+contractAndChain[1] + ) + ) + setContract(contractAndChain.length === 3 ? contractAndChain[2]: "") + setQuantity({ + value:pushData.amount || 0, + range:dropdownQuantityRangeValues.findIndex( + obj => obj.value === pushData.comparison + ) + }) + }else if(oldValue.category === CATEGORY.INVITE){ + setInviteCheckboxes({ + admin:true, + owner:true, + }) + }else{ + // invite + setUrl(pushData.url || "") + } + }else{ + // guild condition + setGuildId((oldValue.data as GuildData).id) + setSpecificRoleId((oldValue.data as GuildData).role) + setGuildComparison((oldValue.data as GuildData).comparison) + } + + setSelectedTypeValue( + dropdownTypeValues.findIndex(obj => obj.value === oldValue.type) + ) + } + },[]) + + return ( +
+
+ +
+ + {Array.isArray(getCategoryDropdownValues()) ? ( + + ) : ( + + )} + + {Array.isArray(getSubCategoryDropdownValues()) ? ( + + ) : ( + + )} + {/* shift to minor components leave for now*/} + {checkIfTokenNFT() && ( + <> + + setContract(e.target.value)} + placeholder="e.g. 0x123..." + /> + + + )} + + {checkIfCustomEndpoint() && ( + setUrl(e.target.value)} + placeholder="e.g. abc.com" + /> + )} + {checkIfPushInvite() && ( +
+ {Object.keys(INVITE_CHECKBOX_LABEL).map((key) => ( + + setInviteCheckboxes({ + admin:true, + owner:true + }) + } + checked={ + inviteCheckboxes[key as keyof typeof INVITE_CHECKBOX_LABEL] + } + /> + ))} +
+ )} + + {checkIfGuild() && ( + <> + setGuildId(e.target.value)} + placeholder="e.g. 4687" + /> + { + setGuildComparison(newEl)}} + /> + + {guildComparison === "specific" && + setSpecificRoleId(e.target.value)} + placeholder="e.g. 4687" + /> + } + + + + )} + + +
+ ); +}; + +export default AddCriteria; + diff --git a/packages/uiweb/src/lib/components/chat/CreateGroup/ConditionsComponent.tsx b/packages/uiweb/src/lib/components/chat/CreateGroup/ConditionsComponent.tsx new file mode 100644 index 000000000..4efb0d594 --- /dev/null +++ b/packages/uiweb/src/lib/components/chat/CreateGroup/ConditionsComponent.tsx @@ -0,0 +1,322 @@ +import React, { useContext, useRef, useState } from 'react'; + +import styled from 'styled-components'; + +import { Section, Span } from '../../reusables'; +import { MoreDarkIcon } from '../../../icons/MoreDark'; +import { ThemeContext } from '../theme/ThemeProvider'; +import Dropdown, { DropdownValueType } from '../reusables/DropDown'; +import { ConditionArray, ConditionData, IChatTheme } from '../exportedTypes'; +import { useClickAway } from '../../../hooks'; +import { CATEGORY, CRITERIA_TYPE, CriteriaType, TOKEN_NFT_COMPARISION, TokenNftComparision } from '../types'; + +import EditSvg from '../../../icons/EditSvg.svg'; +import RemoveSvg from '../../../icons/RemoveSvg.svg'; +import { shortenText } from '../../../helpers'; +import { GUILD_COMPARISON_OPTIONS } from '../constants'; + +export type CriteraValueType = { + invertedIcon?: any; + id: number; + type?: string; + title?: string; + icon?: string; + function: () => void; +}; + +interface CriteriaProps { + conditionData: ConditionArray[]; + moreOptions?: boolean; + deleteFunction?: (idx: number) => void; + updateFunction?: (idx: number) => void; +} + +interface MoreOptionsContainerProps { + handleMoreOptionsClick: (row: number, col: number) => void; + setSelectedIndex: any; + selectedIndex: Array | null; + row: number; + col: number; + dropDownValues: DropdownValueType[]; +} + +// fix dropdown position mobile view and z index +const MoreOptionsContainer = ({ + handleMoreOptionsClick, + setSelectedIndex, + selectedIndex, + row, + col, + dropDownValues, +}: MoreOptionsContainerProps) => { + const theme = useContext(ThemeContext); + const dropdownRef = useRef(null); + + useClickAway(dropdownRef, () => setSelectedIndex(null)); + return ( +
handleMoreOptionsClick(row, col)} position='static'> + + {selectedIndex?.length && selectedIndex[0] === row && ( + + + + )} +
+ ); +}; + +const CriteriaSection = ({ criteria }: { criteria: ConditionData }) => { + const theme = useContext(ThemeContext); + + const getTokenNftComparisionLabel = () => { + return TOKEN_NFT_COMPARISION[ + criteria?.data?.['comparison'] as TokenNftComparision + ]; + }; + const checkIfNftToken = () => { + if ( + criteria?.category === CATEGORY.ERC721 || + criteria?.category === CATEGORY.ERC20 + ) + return true; + return false; + }; + + const getGuildRole = () =>{ + return (GUILD_COMPARISON_OPTIONS.find(option => option.value === criteria?.data?.['comparison']))?.heading; + + } + return ( +
+ + {CRITERIA_TYPE[criteria.category as CriteriaType]} + + {checkIfNftToken() && ( + + + {getTokenNftComparisionLabel()}{' '} + + {/* need to fetch token symbol */} + {criteria?.data?.['amount']} {criteria.category} + + )} + {criteria.category === CATEGORY.INVITE && ( + + Owner and Admin can invite + + )} + {criteria.category === CATEGORY.CustomEndpoint && ( + + {shortenText(criteria.data?.['url'],30)} + + )} + {criteria.category === CATEGORY.ROLES && ( + + {criteria?.data?.['id']} {' '} + + with {' '} + + {getGuildRole()} role + + )} +
+ ); +}; +// fix dropdown ui +const ConditionsComponent = ({ + conditionData, + deleteFunction, + updateFunction, + moreOptions = true, +}: CriteriaProps) => { + const [selectedIndex, setSelectedIndex] = useState | null>( + null + ); + + const dropdownRef = useRef(null); + + const dropDownValues: DropdownValueType[] = [ + { + id: 0, + value: 'Edit', + title: 'Edit', + icon: EditSvg, + function: () => { + if (updateFunction) { + if (selectedIndex) { + updateFunction(selectedIndex[0]); + setSelectedIndex(null); + } + } + }, + }, + { + id: 1, + value: 'Remove', + title: 'Remove', + icon: RemoveSvg, + function: () => { + if (deleteFunction) { + if (selectedIndex) { + deleteFunction(selectedIndex[0]); + setSelectedIndex(null); + } + } + }, + }, + ]; + const theme = useContext(ThemeContext); + + useClickAway(dropdownRef, () => setSelectedIndex(null)); + + const handleMoreOptionsClick = (row: number, col: number) => { + setSelectedIndex([row, col]); + }; + + return ( +
+ {conditionData && + conditionData.slice(1).map((criteria, row) => ( +
+ {criteria.length <= 2 && + criteria.length >= 1 && + criteria.map((singleCriteria, col) => ( + <> + {singleCriteria.type && ( +
+ + {moreOptions && ( + + )} +
+ )} + + ))} + + {criteria[0]?.operator && criteria.length > 2 && ( + +
+ {criteria.map((singleCriteria, col) => ( + <> + {singleCriteria.type && ( + <> +
+ +
+ + )} + + ))} +
+
+ {criteria.map((singleCriteria) => ( + <> + {criteria.length > 2 && + singleCriteria.operator && + !singleCriteria.type && ( + + {singleCriteria.operator} + + )} + + ))} + {moreOptions && ( + + )} +
+
+ )} + {conditionData && + row < conditionData.length - 2 && + conditionData[0][0]?.operator && ( + // this can be reused + + {conditionData[0][0].operator} + + )} +
+ ))} +
+ ); +}; + +export default ConditionsComponent; + +const DropdownContainer = styled.div` + position: absolute; + // left: 48%; + top: 0; + right: 0; + border-radius: ${(props) => props.theme.borderRadius.modalInnerComponents}; + + padding: 6px 32px 6px 12px; + z-index: 999999999999 !important; + display: flex; + flex-direction: column !important; + background: ${(props) => props.theme.backgroundColor.modalBackground}; + border: ${(props) => props.theme.border.modalInnerComponents}; +`; + +const OperatorSpan = styled(Span)<{ theme: IChatTheme }>` + padding: 4px 8px; + margin: 8px 0; + border-radius: ${(props) => props.theme.borderRadius.modalInnerComponents}; + background: ${(props) => props.theme.backgroundColor.modalHoverBackground}; + color: ${(props) => props.theme.textColor?.modalSubHeadingText}; +`; + +const CriteriaGroup = styled(Section)<{ theme: IChatTheme }>` + border: ${(props) => props.theme.border?.modalInnerComponents}; +`; diff --git a/packages/uiweb/src/lib/components/chat/CreateGroup/CreateGroupModal.tsx b/packages/uiweb/src/lib/components/chat/CreateGroup/CreateGroupModal.tsx new file mode 100644 index 000000000..9b2ad7045 --- /dev/null +++ b/packages/uiweb/src/lib/components/chat/CreateGroup/CreateGroupModal.tsx @@ -0,0 +1,341 @@ +import React, { useContext, useEffect, useRef, useState } from 'react'; + +import styled from 'styled-components'; +import { ToastContainer } from 'react-toastify'; +import { AiTwotoneCamera } from 'react-icons/ai'; +import { MdError } from 'react-icons/md'; + +import { ModalHeader } from '../reusables/Modal'; +import { Modal } from '../reusables/Modal'; +import { TextInput } from '../reusables/TextInput'; +import { TextArea } from '../reusables/TextArea'; +import { Section, Span } from '../../reusables'; +import { Button } from '../reusables'; +import { CreateGroupType } from './CreateGroupType'; +import useToast from '../reusables/NewToast'; +import { CreateGroupModalProps, IChatTheme } from '../exportedTypes'; +import useMediaQuery from '../../../hooks/useMediaQuery'; +import { DefineCondtion } from './DefineCondition'; +import AddCriteria from './AddCriteria'; +import { SpamIcon } from '../../../icons/SpamIcon'; +import { ThemeContext } from '../theme/ThemeProvider'; +import { + CriteriaStateManagerType, + useCriteriaStateManager, +} from '../../../hooks/chat/useCriteriaState'; + +import { Image } from '../../../config/styles'; +import { ProfilePicture, device } from '../../../config'; + +export const CREATE_GROUP_STEP_KEYS = { + INPUT_DETAILS: 1, + GROUP_TYPE: 2, + DEFINITE_CONDITION: 3, + ADD_CRITERIA: 4, +} as const; + +export type CreateGroupStepKeys = + typeof CREATE_GROUP_STEP_KEYS[keyof typeof CREATE_GROUP_STEP_KEYS]; + +interface GroupInputDetailsType { + groupName: string; + groupDescription: string; + groupImage: string; +} + +export const CreateGroupModal: React.FC = ({ + onClose, +}) => { + const [activeComponent, setActiveComponent] = useState( + // replace it with info one + CREATE_GROUP_STEP_KEYS.INPUT_DETAILS + ); + + const handleNext = () => { + setActiveComponent((activeComponent + 1) as CreateGroupStepKeys); + }; + const handlePrevious = () => { + setActiveComponent((activeComponent - 1) as CreateGroupStepKeys); + }; + + const criteriaStateManager = useCriteriaStateManager(); + + useEffect(() => { + // reset update rules + if (activeComponent === 2) { + criteriaStateManager.resetRules(); + } else if (activeComponent === 3) { + criteriaStateManager.resetCriteriaIdx(); + } + }, [activeComponent]); + + const [groupInputDetails, setGroupInputDetails] = + useState({ + groupName: '', + groupDescription: '', + groupImage: '', + }); + + const renderComponent = () => { + switch (activeComponent) { + case CREATE_GROUP_STEP_KEYS.INPUT_DETAILS: + return ( + + ); + case CREATE_GROUP_STEP_KEYS.GROUP_TYPE: + return ( + + ); + case CREATE_GROUP_STEP_KEYS.DEFINITE_CONDITION: + return ( + + ); + case CREATE_GROUP_STEP_KEYS.ADD_CRITERIA: + return ( + + ); + default: + return ( + + ); + } + }; + + return ( + + {renderComponent()} + + ); +}; + +export interface ModalHeaderProps { + handleNext?: () => void; + handlePrevious?: () => void; + onClose: () => void; + criteriaStateManager: CriteriaStateManagerType; +} + +interface GroupDetailState { + groupInputDetails: GroupInputDetailsType; + setGroupInputDetails: React.Dispatch< + React.SetStateAction + >; +} + +export interface GroupTypeState { + groupInputDetails: GroupInputDetailsType; +} + +const CreateGroupDetail = ({ + handleNext, + onClose, + groupInputDetails, + setGroupInputDetails, +}: ModalHeaderProps & GroupDetailState) => { + const groupInfoToast = useToast(); + const { groupName, groupDescription, groupImage } = groupInputDetails; + const theme = useContext(ThemeContext); + + const fileUploadInputRef = useRef(null); + const isMobile = useMediaQuery(device.mobileL); + + const handleChange = (e: Event) => { + if (!(e.target instanceof HTMLInputElement)) { + return; + } + if (!e.target.files) { + return; + } + if ( + (e.target as HTMLInputElement).files && + ((e.target as HTMLInputElement).files as FileList).length + ) { + const reader = new FileReader(); + reader.readAsDataURL(e.target.files[0]); + + reader.onloadend = function () { + setGroupInputDetails({ + groupDescription, + groupName, + groupImage: reader.result as string, + }); + }; + } + }; + + const showError = (errorMessage: string) => { + groupInfoToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: errorMessage, + toastType: 'ERROR', + getToastIcon: (size) => , + }); + }; + + const verifyAndHandelNext = () => { + const skipVerify = false; + + if (!skipVerify) { + // verify name + if (groupName.trim().length === 0) { + showError('Group Name is empty'); + return; + } + + // verify description + if (groupDescription.trim().length === 0) { + showError('Group Description is empty'); + return; + } + + // verify description + // if (!groupImage) { + // showError("Group image can't be empty"); + // return; + // } + } + + if (handleNext) { + handleNext(); + } + }; + + const handleUpload = () => { + if (fileUploadInputRef.current) { + fileUploadInputRef.current.click(); + } + }; + + //groupImage and desccription is optional + return ( +
+ + + + {!groupImage && ( + + + + )} + {groupImage && ( + + group image + + )} + handleChange(e as unknown as Event)} + /> + + + setGroupInputDetails({ + groupDescription, + groupName: e.target.value, + groupImage, + }) + } + /> + +