Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
at main 236 lines 7.4 kB view raw
1import { 2 ArrowsRightLeftIcon, 3 PlusIcon, 4 UsersIcon, 5 XCircleIcon 6} from "@heroicons/react/24/outline"; 7import { ADDRESS_PLACEHOLDER } from "@hey/data/constants"; 8import type { CollectActionType } from "@hey/types/hey"; 9import { motion } from "motion/react"; 10import { useState } from "react"; 11import { isAddress } from "viem"; 12import SearchAccounts from "@/components/Shared/Account/SearchAccounts"; 13import ToggleWithHelper from "@/components/Shared/ToggleWithHelper"; 14import { Button, H6, Input } from "@/components/Shared/UI"; 15import splitNumber from "@/helpers/splitNumber"; 16import { useCollectActionStore } from "@/store/non-persisted/post/useCollectActionStore"; 17import { useAccountStore } from "@/store/persisted/useAccountStore"; 18import { EXPANSION_EASE } from "@/variants"; 19 20interface SplitConfigProps { 21 isRecipientsDuplicated: boolean; 22 setCollectType: (data: CollectActionType) => void; 23} 24 25const SplitConfig = ({ 26 isRecipientsDuplicated, 27 setCollectType 28}: SplitConfigProps) => { 29 const { currentAccount } = useAccountStore(); 30 const { collectAction } = useCollectActionStore((state) => state); 31 32 const currentAddress = currentAccount?.address || ""; 33 const recipients = collectAction.payToCollect?.recipients || []; 34 const [isToggleOn, setIsToggleOn] = useState( 35 recipients.length > 1 || 36 (recipients.length === 1 && recipients[0].address !== currentAddress) 37 ); 38 const splitTotal = recipients.reduce((acc, curr) => acc + curr.percent, 0); 39 40 const handleSplitEvenly = () => { 41 const equalSplits = splitNumber(100, recipients.length); 42 const splits = recipients.map((recipient, i) => { 43 return { 44 address: recipient.address, 45 percent: equalSplits[i] 46 }; 47 }); 48 if (!collectAction.payToCollect) return; 49 setCollectType({ 50 payToCollect: { 51 ...collectAction.payToCollect, 52 recipients: [...splits] 53 } 54 }); 55 }; 56 57 const onChangeRecipientOrPercent = ( 58 index: number, 59 value: string, 60 type: "address" | "percent" 61 ) => { 62 const getRecipients = (value: string) => { 63 return recipients.map((recipient, i) => { 64 if (i === index) { 65 return { 66 ...recipient, 67 [type]: type === "address" ? value : Number.parseInt(value, 10) 68 }; 69 } 70 return recipient; 71 }); 72 }; 73 74 if (!collectAction.payToCollect) return; 75 setCollectType({ 76 payToCollect: { 77 ...collectAction.payToCollect, 78 recipients: getRecipients(value) 79 } 80 }); 81 }; 82 83 const updateRecipient = (index: number, value: string) => { 84 onChangeRecipientOrPercent(index, value, "address"); 85 }; 86 87 const handleRemoveRecipient = (index: number) => { 88 const updatedRecipients = recipients.filter((_, i) => i !== index); 89 if (updatedRecipients.length) { 90 if (!collectAction.payToCollect) return; 91 setCollectType({ 92 payToCollect: { 93 ...collectAction.payToCollect, 94 recipients: updatedRecipients 95 } 96 }); 97 } else { 98 if (!collectAction.payToCollect) return; 99 setCollectType({ 100 payToCollect: { 101 ...collectAction.payToCollect, 102 recipients: [{ address: currentAddress, percent: 100 }] 103 } 104 }); 105 setIsToggleOn(false); 106 } 107 }; 108 109 const toggleSplit = () => { 110 if (!collectAction.payToCollect) return; 111 setCollectType({ 112 payToCollect: { 113 ...collectAction.payToCollect, 114 recipients: [{ address: currentAddress, percent: 100 }] 115 } 116 }); 117 setIsToggleOn(!isToggleOn); 118 }; 119 120 return ( 121 <div className="mt-5"> 122 <ToggleWithHelper 123 description="Set multiple recipients for the collect fee" 124 heading="Split revenue" 125 icon={<UsersIcon className="size-5" />} 126 on={isToggleOn} 127 setOn={toggleSplit} 128 /> 129 {isToggleOn ? ( 130 <motion.div 131 animate="visible" 132 className="mt-4 ml-8 space-y-3" 133 initial="hidden" 134 transition={{ duration: 0.2, ease: EXPANSION_EASE }} 135 variants={{ 136 hidden: { height: 0, opacity: 0, y: -20 }, 137 visible: { height: "auto", opacity: 1, y: 0 } 138 }} 139 > 140 <div className="space-y-2"> 141 {recipients.map((recipient, index) => ( 142 <H6 143 className="flex items-center space-x-2 font-normal" 144 key={index} 145 > 146 <SearchAccounts 147 error={ 148 recipient.address.length > 0 && 149 !isAddress(recipient.address) 150 } 151 hideDropdown={isAddress(recipient.address)} 152 onAccountSelected={(account) => 153 updateRecipient(index, account.owner) 154 } 155 onChange={(event) => 156 updateRecipient(index, event.target.value) 157 } 158 placeholder={`${ADDRESS_PLACEHOLDER} or wagmi`} 159 value={recipient.address} 160 /> 161 <div className="w-1/3"> 162 <Input 163 iconRight="%" 164 max="100" 165 min="1" 166 onChange={(event) => 167 onChangeRecipientOrPercent( 168 index, 169 event.target.value, 170 "percent" 171 ) 172 } 173 placeholder="5" 174 type="number" 175 value={recipient.percent} 176 /> 177 </div> 178 <button 179 onClick={() => handleRemoveRecipient(index)} 180 type="button" 181 > 182 <XCircleIcon className="size-5" /> 183 </button> 184 </H6> 185 ))} 186 </div> 187 <div className="flex items-center justify-between"> 188 {recipients.length >= 4 ? ( 189 <div /> 190 ) : ( 191 <Button 192 icon={<PlusIcon className="size-3" />} 193 onClick={() => { 194 if (!collectAction.payToCollect) return; 195 setCollectType({ 196 payToCollect: { 197 ...collectAction.payToCollect, 198 recipients: [...recipients, { address: "", percent: 0 }] 199 } 200 }); 201 }} 202 outline 203 size="sm" 204 > 205 Add recipient 206 </Button> 207 )} 208 <Button 209 icon={<ArrowsRightLeftIcon className="size-3" />} 210 onClick={handleSplitEvenly} 211 outline 212 size="sm" 213 > 214 Split evenly 215 </Button> 216 </div> 217 {splitTotal > 100 ? ( 218 <H6 className="text-red-500"> 219 Splits cannot exceed 100%. Total: <span>{splitTotal}</span>% 220 </H6> 221 ) : null} 222 {splitTotal < 100 ? ( 223 <H6 className="text-red-500"> 224 Splits cannot be less than 100%. Total: <span>{splitTotal}</span>% 225 </H6> 226 ) : null} 227 {isRecipientsDuplicated ? ( 228 <H6 className="text-red-500">Duplicate recipient address found</H6> 229 ) : null} 230 </motion.div> 231 ) : null} 232 </div> 233 ); 234}; 235 236export default SplitConfig;