-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(component): implemented a copy link button for copying invited c… #645
base: main
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @pheobeayo! Thank you very much for this PR.
It looks great, but it would be nice to move the new invite code box to the modal that opens after clicking the Add member
button. The issue description can help but feel free to ask any questions you may have.
update required changes
update and fix CI issues
Kindly review the changes made please |
Hey @pheobeayo! I will review the PR as soon as possible. Thank you! |
Hey @pheobeayo! Could you update your branch with the latest changes of the main branch and fix the branch conflict? Please, let me know if you need any help to do it. |
update PR
bug fix for issue
bug fix
fixing bugs
Fixed @vplasencia |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @pheobeayo! Thank you very much for the updates.
The idea of this implementation is correct but this feature should be implemented inside the add-member-modal.tsx
file.
The new additions would be something like:
const {
hasCopied: hasCopiedInviteCode,
value: inviteCode,
setValue: setInviteCode,
onCopy: onCopyInviteCode
} = useClipboard("")
const generateInviteLink = useCallback(async () => {
const inviteLink = await bandadaAPI.generateMagicLink(group.id)
if (inviteLink === null) {
return
}
setClientLink(inviteLink)
const index = inviteLink.lastIndexOf("=")
const inviteCodeLink = inviteLink.substring(index + 1)
setInviteCode(inviteCodeLink)
}, [group, setClientLink, setInviteCode])
UI code updates:
{!group.credentials && (
<>
<Text mb="10px" color="balticSea.800">
Share invite code
</Text>
<InputGroup size="lg">
<Input
pr="50px"
placeholder="Invite code"
value={inviteCode}
isDisabled
/>
<InputRightElement mr="5px">
<Tooltip
label={
hasCopiedInviteCode
? "Copied!"
: "Copy"
}
closeOnClick={false}
hasArrow
>
<IconButton
variant="link"
aria-label="Copy invite link"
onClick={onCopyInviteCode}
onMouseDown={(e) =>
e.preventDefault()
}
icon={
<Icon
color="sunsetOrange.600"
boxSize="5"
as={FiCopy}
/>
}
/>
</Tooltip>
</InputRightElement>
</InputGroup>
</>
)}
The final file would be something like:
import { getSemaphoreContract } from "@bandada/utils"
import {
AbsoluteCenter,
Box,
Button,
Divider,
Heading,
Icon,
IconButton,
Input,
InputGroup,
InputRightElement,
Modal,
ModalBody,
ModalContent,
ModalOverlay,
Text,
Textarea,
Tooltip,
useClipboard
} from "@chakra-ui/react"
import { useCallback, useEffect, useState } from "react"
import { FiCopy } from "react-icons/fi"
import { useSigner } from "wagmi"
import * as bandadaAPI from "../api/bandadaAPI"
import { Group } from "../types"
import parseMemberIds from "../utils/parseMemberIds"
export type AddMemberModalProps = {
isOpen: boolean
onClose: (value?: string[]) => void
group: Group
}
export default function AddMemberModal({
isOpen,
onClose,
group
}: AddMemberModalProps): JSX.Element {
const [_memberIds, setMemberIds] = useState<string>("")
const [_isLoading, setIsLoading] = useState(false)
const {
hasCopied,
value: _clientLink,
setValue: setClientLink,
onCopy
} = useClipboard("")
const {
hasCopied: hasCopiedInviteCode,
value: inviteCode,
setValue: setInviteCode,
onCopy: onCopyInviteCode
} = useClipboard("")
const { data: signer } = useSigner()
useEffect(() => {
setMemberIds("")
if (group.credentials) {
setClientLink(
`${import.meta.env.VITE_CLIENT_URL}?credentialGroupId=${
group.id
}`
)
}
}, [group, setClientLink])
const addMember = useCallback(async () => {
const memberIds = parseMemberIds(_memberIds)
if (memberIds.length === 0) {
alert("Please enter at least one member id!")
return
}
const uniqueMemberIds = new Set(memberIds)
if (uniqueMemberIds.size !== memberIds.length) {
alert("Please ensure there are no repeated member IDs!")
return
}
if (group.type === "on-chain" && group.members) {
const existingMembers = new Set(
group.members.map((memberId) => BigInt(memberId))
)
const conflictingMembers = []
for (const memberId of memberIds) {
const parsedMemberId = BigInt(memberId)
if (existingMembers.has(parsedMemberId)) {
conflictingMembers.push(parsedMemberId)
}
}
if (conflictingMembers.length > 0) {
if (conflictingMembers.length === 1) {
alert(
`Member ID ${conflictingMembers[0]} already exists in the group.`
)
} else {
alert(
`Member IDs ${conflictingMembers.join(
", "
)} already exist in the group.`
)
}
return
}
}
const confirmMessage = `
Are you sure you want to add the following members?
${memberIds.join("\n")}
`
if (!window.confirm(confirmMessage)) {
return
}
setIsLoading(true)
if (group.type === "off-chain") {
if ((await bandadaAPI.addMembers(group.id, memberIds)) === null) {
setIsLoading(false)
return
}
setIsLoading(false)
onClose(memberIds)
} else {
if (!signer) {
alert("No valid signer for your transaction!")
setIsLoading(false)
return
}
try {
const semaphore = getSemaphoreContract("sepolia", signer as any)
await semaphore.addMembers(group.id, memberIds)
setIsLoading(false)
onClose(memberIds)
} catch (error) {
alert(
"Some error occurred! Check if you're on Sepolia network and the transaction is signed and completed"
)
setIsLoading(false)
}
}
}, [onClose, _memberIds, group, signer])
const generateInviteLink = useCallback(async () => {
const inviteLink = await bandadaAPI.generateMagicLink(group.id)
if (inviteLink === null) {
return
}
setClientLink(inviteLink)
const index = inviteLink.lastIndexOf("=")
const inviteCodeLink = inviteLink.substring(index + 1)
setInviteCode(inviteCodeLink)
}, [group, setClientLink, setInviteCode])
return (
<Modal
isOpen={isOpen}
onClose={onClose}
closeOnOverlayClick={false}
isCentered
>
<ModalOverlay />
<ModalContent bgColor="balticSea.50" maxW="450px">
<ModalBody p="25px 30px">
<Heading fontSize="25px" fontWeight="500" mb="25px" as="h1">
New member
</Heading>
{!group.credentials && (
<Box mb="5px">
<Text my="10px" color="balticSea.800">
Add member IDs
</Text>
<Textarea
placeholder="Paste one or more member IDs separated by commas, spaces, or newlines"
size="lg"
value={_memberIds}
onChange={(event) =>
setMemberIds(event.target.value)
}
rows={5}
/>
<Button
my="10px"
width="100%"
variant="solid"
colorScheme="tertiary"
onClick={addMember}
isLoading={_isLoading}
>
Add members
</Button>
</Box>
)}
{group.type === "off-chain" && !group.credentials && (
<Box position="relative" py="8">
<Divider borderColor="balticSea.300" />
<AbsoluteCenter
fontSize="13px"
px="4"
bgColor="balticSea.50"
>
OR
</AbsoluteCenter>
</Box>
)}
{group.type === "off-chain" && (
<Box mb="30px">
{!group.credentials && (
<>
<Text mb="10px" color="balticSea.800">
Share invite code
</Text>
<InputGroup size="lg">
<Input
pr="50px"
placeholder="Invite code"
value={inviteCode}
isDisabled
/>
<InputRightElement mr="5px">
<Tooltip
label={
hasCopiedInviteCode
? "Copied!"
: "Copy"
}
closeOnClick={false}
hasArrow
>
<IconButton
variant="link"
aria-label="Copy invite link"
onClick={onCopyInviteCode}
onMouseDown={(e) =>
e.preventDefault()
}
icon={
<Icon
color="sunsetOrange.600"
boxSize="5"
as={FiCopy}
/>
}
/>
</Tooltip>
</InputRightElement>
</InputGroup>
</>
)}
<Text mb="10px" color="balticSea.800">
{!group.credentials
? "Share invite link"
: "Share access link"}
</Text>
<InputGroup size="lg">
<Input
pr="50px"
placeholder={
!group.credentials
? "Invite link"
: "Access link"
}
value={_clientLink}
isDisabled
/>
<InputRightElement mr="5px">
<Tooltip
label={hasCopied ? "Copied!" : "Copy"}
closeOnClick={false}
hasArrow
>
<IconButton
variant="link"
aria-label="Copy invite link"
onClick={onCopy}
onMouseDown={(e) =>
e.preventDefault()
}
icon={
<Icon
color="sunsetOrange.600"
boxSize="5"
as={FiCopy}
/>
}
/>
</Tooltip>
</InputRightElement>
</InputGroup>
{!group.credentials && (
<Button
mt="10px"
variant="link"
color="balticSea.600"
textDecoration="underline"
onClick={generateInviteLink}
>
Generate new link
</Button>
)}
</Box>
)}
<Button
width="100%"
variant="solid"
colorScheme="tertiary"
onClick={() => onClose()}
>
Close
</Button>
</ModalBody>
</ModalContent>
</Modal>
)
}
This should be the final result:
Please let me know if you have any questions.
@vplasencia , thank you!, I will work on the update. |
…odes
Description
implemented invited code links in button
implemented invited code links in button
Related Issue
Issue #635
#635 (comment)
Does this introduce a breaking change?
Other information