Search
Search

Transaction: GZMCdiZ...qBCk

Signed by
Receiver
Status
Succeeded
Transaction Fee
0.01621 
Deposit Value
0.06034 
Gas Used
162 Tgas
Attached Gas
300 Tgas
Created
May 03, 2024 at 11:46:50am
Hash
GZMCdiZVfuGE3g5Q9P9XCb5Yi99TCjJQjJAYZq7NqBCk

Actions

Called method: 'set' in contract: social.near
Arguments:
{ "data": { "potlock.near": { "widget": { "Index": { "": "\n /** Bundle generated by Além Library v1.0.0 - See more here: https://github.com/wpdas/alem */\n /** Project repository: https://github.com/PotLock/bos-app-alem */\n const updateAlemState = (updatedState) => { State.update({ alem: { ...state.alem, ...updatedState, }, });};const alemState = () => state.alem ;const AlemStateInitialBody = { alem: { ready: false, rootProps: props, alemEnvironment: \"production\", keepRoute: false, previousRoute: null, previousRouteParams: null, alemExternalStylesLoaded: false, alemExternalStylesBody: \"\", },};State.init(AlemStateInitialBody);State.update({ alem: { ...state.alem, rootProps: props } });const props = { ...props, alem: { ...state.alem, createRoute: (path, component) => ({ path, component }) , useParams: () => { let params = alemState().rootProps; return params; }, loadExternalStyles: (URLs) => { if (!URLs && !alemState().alemExternalStylesLoaded) { return; } let stylesBody = \"\"; const totalItems = URLs.length; let loadedCounter = 0; const loadStyle = (styleURL) => { asyncFetch(styleURL).then((response) => { Storage.set(styleURL, response.body); stylesBody += response.body; loadedCounter += 1; if (loadedCounter === totalItems) { updateAlemState({ alemExternalStylesLoaded: true, alemExternalStylesBody: stylesBody, }); } }); }; URLs.forEach((styleURL) => { props.alem.promisify( () => Storage.get(styleURL), (response) => { stylesBody += response; loadedCounter += 1; if (loadedCounter === totalItems) { updateAlemState({ alemExternalStylesLoaded: true, alemExternalStylesBody: stylesBody, }); } }, () => { loadStyle(styleURL); }, 100, ); }); return alemState().alemExternalStylesLoaded; }, promisify: ( caller, resolve, reject, _timeout, ) => { const timer = 100; const timeout = _timeout || 10000; let timeoutCheck = 0; const find = () => { const response = caller(); if (response !== undefined && response !== null) { resolve(response); } else { if (timeoutCheck < timeout) { setTimeout(find, timer); timeoutCheck += timer; } else { if (reject) { reject(); } } } }; find(); }, isDevelopment: alemState().alemEnvironment === \"development\", getAlemEnvironment: () => alemState().alemEnvironment, componentsCode: { ModalAddFundingSource: ` const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_53 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_54 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const Error = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const InputContainer = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_55 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const Input = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`; const DateInput = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_53> {label && <A_54>{label}</A_54>} <InputContainer> {__props__.preInputChildren && __props__.preInputChildren} <Input type={__props__.selectTime ? \"datetime-local\" : \"date\"} placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? (() => {})} style={__props__.inputStyles || {}} /> {__props__.postInputChildren && __props__.postInputChildren} </InputContainer> <Error className={error ? \"show\" : \"\"}>{error}</Error> </A_53>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const ModalHeader = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; background: #f6f5f3; padding: 10px 20px; border-top-left-radius: 6px; border-top-right-radius: 6px;\\`;const ModalHeaderText = styled.div\\` font-size: 16px; font-weight: 600; color: #292929; line-height: 24px; word-wrap: break-word; margin-left: 8px;\\`;const ModalBody = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 16px 20px 32px 20px; gap: 24px;\\`;const Icon = styled.svg\\` width: 20px; height: 20px;\\`;const CloseIcon = styled.svg\\` width: 20px; height: 20px; cursor: pointer; transition: rotate 300ms ease-in-out; :hover { rotate: 180deg; }\\`;const Row = styled.div\\` display: flex; flex-direction: row; align-items: center;\\`; const props = props; const { onClose, handleAddFundingSource, fundingSources, fundingSourceIndex } = props; const initalState = { investorName: fundingSources[fundingSourceIndex]?.investorName || \"\", investorNameError: \"\", date: fundingSources[fundingSourceIndex]?.date || \"\", dateError: \"\", description: fundingSources[fundingSourceIndex]?.description || \"\", descriptionError: \"\", denomination: fundingSources[fundingSourceIndex]?.denomination || \"\", denominationError: \"\", amountReceived: fundingSources[fundingSourceIndex]?.amountReceived || \"\", amountReceivedError: \"\" }; State.init(initalState); useEffect(() => { State.update(initalState); }, [fundingSources, fundingSourceIndex]); return <ModalOverlay overlayStyle={onClose}> <ModalHeader> <div></div> <Row> <Icon width=\"12\" height=\"18\" viewBox=\"0 0 12 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6.39016 7.9C4.12016 7.31 3.39016 6.7 3.39016 5.75C3.39016 4.66 4.40016 3.9 6.09016 3.9C7.87016 3.9 8.53016 4.75 8.59016 6H10.8002C10.7302 4.28 9.68016 2.7 7.59016 2.19V0H4.59016V2.16C2.65016 2.58 1.09016 3.84 1.09016 5.77C1.09016 8.08 3.00016 9.23 5.79016 9.9C8.29016 10.5 8.79016 11.38 8.79016 12.31C8.79016 13 8.30016 14.1 6.09016 14.1C4.03016 14.1 3.22016 13.18 3.11016 12H0.910156C1.03016 14.19 2.67016 15.42 4.59016 15.83V18H7.59016V15.85C9.54016 15.48 11.0902 14.35 11.0902 12.3C11.0902 9.46 8.66016 8.49 6.39016 7.9Z\" fill=\"#151A23\" /> </Icon> <ModalHeaderText>Add Past Funding Source</ModalHeaderText> </Row> <CloseIcon viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" onClick={onClose}> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#7B7B7B\" /> </CloseIcon> </ModalHeader> <ModalBody> <A_62 {...{ label: \"Name of investor\", placeholder: \"Enter investor name\", value: state.investorName, onChange: val => State.update({ investorName: val }), validate: () => { if (state.investorName.length < 3) { State.update({ investorNameError: \"Must be at least 3 characters\" }); return; } if (state.investorName.length > 50) { State.update({ investorNameError: \"Must be less than 50 characters\" }); return; } State.update({ investorNameError: \"\" }); }, error: state.investorNameError }} /> <DateInput {...{ label: <> Date <span>(optional)</span> </>, selectTime: false, value: state.date, onChange: date => State.update({ date: date }), error: state.dateError }} /> <TextArea {...{ label: \"Description\", placeholder: \"Type description\", value: state.description, onChange: description => State.update({ description }), validate: () => { if (state.description.length > 500) { State.update({ descriptionError: \"Must be less than 500 characters\" }); return; } State.update({ descriptionError: \"\" }); }, error: state.descriptionError }} /> <A_62 {...{ label: \"Denomination of investment\", placeholder: \"e.g. NEAR, USD, USDC, etc.\", value: state.denomination, onChange: val => State.update({ denomination: val.toUpperCase() }), validate: () => { if (state.denomination.length < 3) { State.update({ denominationError: \"Must be at least 3 characters\" }); return; } if (state.denomination.length > 10) { State.update({ denominationError: \"Must be less than 10 characters\" }); return; } State.update({ denominationError: \"\" }); }, error: state.denominationError }} /> <A_62 {...{ label: \"Investment amount\", placeholder: \"e.g. 1000\", value: state.amountReceived, onChange: val => State.update({ amountReceived: val }), validate: () => { if (isNaN(state.amountReceived)) { State.update({ amountReceivedError: \"Must be a number\" }); return; } State.update({ amountReceivedError: \"\" }); }, error: state.amountReceivedError }} /> <Row style={{ width: \"100%\", justifyContent: \"flex-end\" }}> <Button {...{ type: \"primary\", text: \"Add Funding Source\", disabled: !state.investorName || !!state.investorNameError || !!state.dateError || !state.description || !!state.descriptionError || !state.denomination || !!state.denominationError || !state.amountReceived || !!state.amountReceivedError, onClick: () => { const fundingSource = { investorName: state.investorName, date: state.date, description: state.description, denomination: state.denomination, amountReceived: state.amountReceived }; State.update({ investorName: \"\", investorNameError: \"\", date: \"\", dateError: \"\", description: \"\", descriptionError: \"\", denomination: \"\", denominationError: \"\", amountReceived: \"\", amountReceivedError: \"\" }); handleAddFundingSource(fundingSource); } }} /> </Row> </ModalBody> </ModalOverlay>; `, AccountsStack: ` const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const StackContainer = styled.div\\` width: 200px; height: 30px; margin-bottom: 16px; display: flex; flex-direction: row; @media screen and (max-width: 768px) { margin-left: 36px; }\\`;const MoreAccountsContainer = styled.div\\` width: 28px; height: 28px; border: 2px solid white; border-radius: 50%; background: #dd3345; position: relative; display: flex; justify-content: center; align-items: center; margin-right: -8px;\\`;const MoreAccountsText = styled.div\\` color: white; font-size: 12px; font-weight: 600; text-align: center;\\`; const {accountIds: accountIds, maxDisplayCount: maxDisplayCount, sendToBack: sendToBack} = props; const MAX_DISPLAY_COUNT = maxDisplayCount || 5; const accounts = useMemo(() => accountIds.slice(0, MAX_DISPLAY_COUNT), [accountIds]); return <StackContainer> {accountIds.length > MAX_DISPLAY_COUNT && <MoreAccountsContainer style={{ zIndex: accountIds.length + 1 }}> <MoreAccountsText>{MAX_DISPLAY_COUNT}+</MoreAccountsText> </MoreAccountsContainer>} {accounts.map((accountId, idx) => { return <ProfileImage {...{ accountId, style: { width: \"28px\", height: \"28px\", zIndex: sendToBack ? 0 : accountIds.length - idx, margin: \"0 -8px 0 0\", border: \"2px solid white\", borderRadius: \"50%\", background: \"white\" }, className: \"mb-2\", imageClassName: \"rounded-circle w-100 h-100 d-block\", thumbnail: false, tooltip: true }} />; })} </StackContainer>; `, CreateForm: ` function validateGithubRepoUrl(url) { const githubRepoUrlPattern = /^(https?:\\\\/\\\\/)?(www\\\\.)?github\\\\.com\\\\/[a-zA-Z0-9-]+\\\\/[a-zA-Z0-9_.-]+\\\\/?$/; return githubRepoUrlPattern.test(url);} const MembersListItem = styled.div\\` padding: 16px 0px; border-top: 1px #f0f0f0 solid; display: flex; flex-direction: row; align-items: center; justify-content: space-between;\\`;const MembersListItemLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 16px;\\`;const MembersListItemText = styled.div\\` font-size: 16px; font-weight: 400; color: #2e2e2e;\\`;const RemoveMember = styled.a\\` color: #2e2e2e; font-size: 14px; font-weight: 600; visibility: hidden; cursor: pointer; opacity: 0; transition: opacity 0.2s ease-in-out; &:hover { text-decoration: none; } \\${MembersListItem}:hover & { visibility: visible; opacity: 1; }\\`; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const AccountsList = __props__ => { const { accountIds, allowRemove, handleRemoveAccount } = __props__; return <> {accountIds.map(accountId => { return <MembersListItem> <MembersListItemLeft> <ProfileImage {...{ accountId, style: { width: \"40px\", height: \"40px\", margin: \"0 -8px 0 0\", borderRadius: \"50%\", background: \"white\" }, imageClassName: \"rounded-circle w-100 h-100 d-block\", thumbnail: false, tooltip: true }} /> <MembersListItemText>@{accountId}</MembersListItemText> </MembersListItemLeft> {allowRemove && <RemoveMember onClick={() => handleRemoveAccount(accountId)}>Remove</RemoveMember>} </MembersListItem>; })} </>;}; const A_49 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; margin-bottom: 24px;\\`;const ModalHeaderLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const IconContainer = styled.div\\` width: 40px; height: 40px; background: #f0f0f0; border-radius: 50%; display: flex; justify-content: center; align-items: center; margin-right: 16px;\\`;const A_50 = styled.svg\\` width: 20px; height: 20px; cursor: pointer; transition: 300ms ease-in-out; :hover { rotate: 180deg; }\\`;const A_51 = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 600;\\`;const ModalDescription = styled.p\\` color: #2e2e2e; font-size: 16px; font-weight: 400;\\`;const A_52 = styled.div\\` height: 24px;\\`;const MembersCount = styled.span\\` color: #2e2e2e; font-weight: 600;\\`;const MembersText = styled.div\\` color: #7b7b7b; font-size: 12px; font-weight: 400;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const ModalMultiAccount = (__props__) => { const { onClose, titleText, descriptionText, unitText, inputValue, onInputChange, handleAddAccount, handleRemoveAccount, accountError, accountIds } = __props__; return <ModalOverlay onOverlayClick={onClose}> <A_49> <ModalHeaderLeft> <IconContainer> <A_50 viewBox=\"0 0 24 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M16.24 7.65C15.07 7.13 13.63 6.75 12 6.75C10.37 6.75 8.93 7.14 7.76 7.65C6.68 8.13 6 9.21 6 10.39V12H18V10.39C18 9.21 17.32 8.13 16.24 7.65ZM8.07 10C8.16 9.77 8.34 9.58 8.56 9.48C9.66 8.99 10.82 8.75 11.99 8.75C13.17 8.75 14.32 9 15.42 9.48C15.65 9.58 15.82 9.77 15.91 10H8.07Z\" fill=\"#151A23\" /> <path d=\"M1.22 8.58C0.48 8.9 0 9.62 0 10.43V12H4.5V10.39C4.5 9.56 4.73 8.78 5.13 8.1C4.76 8.04 4.39 8 4 8C3.01 8 2.07 8.21 1.22 8.58Z\" fill=\"#151A23\" /> <path d=\"M22.78 8.58C21.93 8.21 20.99 8 20 8C19.61 8 19.24 8.04 18.87 8.1C19.27 8.78 19.5 9.56 19.5 10.39V12H24V10.43C24 9.62 23.52 8.9 22.78 8.58Z\" fill=\"#151A23\" /> <path d=\"M12 6C13.66 6 15 4.66 15 3C15 1.34 13.66 0 12 0C10.34 0 9 1.34 9 3C9 4.66 10.34 6 12 6ZM12 2C12.55 2 13 2.45 13 3C13 3.55 12.55 4 12 4C11.45 4 11 3.55 11 3C11 2.45 11.45 2 12 2Z\" fill=\"#151A23\" /> <path d=\"M3.9999 2.49687L1.49677 5L3.9999 7.50313L6.50303 5L3.9999 2.49687Z\" fill=\"#151A23\" /> <path d=\"M20 3L17.5 7H22.5L20 3Z\" fill=\"#151A23\" /> </A_50> </IconContainer> <A_51>{titleText}</A_51> </ModalHeaderLeft> <A_50 viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" onClick={onClose}> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#7B7B7B\" /> </A_50> </A_49> <ModalDescription>{descriptionText}</ModalDescription> <A_62 {...{ placeholder: \"NEAR account ID\", value: inputValue, onChange: onInputChange, postInputChildren: <Button {...{ type: \"primary\", text: \"Add\", onClick: handleAddAccount, style: { borderRadius: \\`0px 4px 4px 0px\\` }, submit: true }} />, handleKeyPress: (e) => { if (e.key === \"Enter\") { handleAddAccount(); } }, error: accountError }} /> <A_52 /> <MembersText> <MembersCount>{accountIds.length} </MembersCount> {accountIds.length == 1 ? unitText : \\`\\${unitText}s\\`} </MembersText> <AccountsList {...{ accountIds, allowRemove: true, handleRemoveAccount }} /> </ModalOverlay>;}; function validateEVMAddress(address) { if (!address || address.length !== 42) { return false; } const re = /^0x[a-fA-F0-9]{40}$/; return re.test(address);} const A_233 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_234 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_235 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 10px; background: #ffffff; border: 1px solid #d0d5dd; /* box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); */ box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset; border-radius: 4px; color: #101828; width: 100%;\\`;const Placeholder = styled.span\\` color: #a0a3a8;\\`;const scaleOut = styled.keyframes\\` from { transform: scaleY(0); } to { transform: scaleY(1); }\\`;const SelectContent = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; gap: 0.5em; width: 100%; border: 1px solid #d0d5dd; border-radius: 4px; background: #ffffff; z-index: 3 !important;\\`;const Viewport = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; width: 100%;\\`;const Item = styled.button\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 0.5em; width: 100%; cursor: pointer; background: transparent; border: none; transition: background 0.2s ease-in-out; &:nth-child(n + 1) { border-top: 1px solid #d0d5dd; } &:hover { background: #d0d5dd; boder: none; } &:focus { outline: none; }\\`; const Select = (componentProps) => { const label = componentProps.label ?? \"Label\"; const noLabel = componentProps.noLabel ?? false; const placeholder = componentProps.placeholder ?? \"Select an option\"; const value = componentProps.value ?? \"\"; const options = componentProps.options ?? []; const onChange = componentProps.onChange ?? (() => {}); const validate = componentProps.validate ?? (() => {}); const error = componentProps.error ?? \"\"; return <A_233 style={componentProps.containerStyles || {}}> {noLabel ? <></> : <A_234>{label}</A_234>} <Select.Root value={value?.value} onValueChange={(value) => onChange(options.find((option) => option.value === value))}> <Select.Trigger asChild={true}> <A_235 style={componentProps.inputStyles || {}}> {componentProps.iconLeft && componentProps.iconLeft} <Select.Value aria-label={value.value} placeholder={<Placeholder>{placeholder}</Placeholder>} /> {} <Select.Icon> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1 1.5L6 6.5L11 1.5\" stroke=\"currentColor\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg> </Select.Icon> {} </A_235> </Select.Trigger> <Select.Content asChild={true}> <SelectContent> <Select.Viewport asChild={true}> <Viewport> {options.map(({ text, value }) => <Select.Item value={value} asChild={true}> <Item> <Select.ItemText>{text}</Select.ItemText> <Select.ItemIndicator> <svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" /> </svg> </Select.ItemIndicator> </Item> </Select.Item>)} </Viewport> </Select.Viewport> </SelectContent> </Select.Content> </Select.Root> </A_233>;}; const Container = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const Label = styled.label\\` font-style: normal; /* font-weight: 600; */ font-size: 0.95em; line-height: 1.25em; color: #344054;\\`; const SelectMultiple = __props__ => { const { label, options, onChange, placeholder, selected } = __props__; return <Container> {label && <Label>{label}</Label>} <Typeahead options={options} multiple onChange={onChange} placeholder={placeholder} /> </Container>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_166 = styled.div\\` padding-top: 0; position: relative;\\`;const ProfileWraper = styled.div\\` display: flex; padding-left: 4rem; align-items: end; transform: translateY(-50%); position: relative; z-index: 6; @media screen and (max-width: 768px) { padding-left: 8px; }\\`;const ProfileStats = styled.div\\` position: relative; z-index: 1; display: flex; align-items: center; gap: 1.5rem; transform: translate(-25px, -20px); @media screen and (max-width: 768px) { transform: translate(-25px, 0px); gap: 10px; }\\`;const Verified = styled.div\\` opacity: 1; display: flex; align-items: center; font-size: 11px; letter-spacing: 0.88px; gap: 4px; padding: 3px; border-radius: 20px; background: #fff; text-transform: uppercase; overflow: hidden; &.not-verified { width: 10px; opacity: 0; } div { font-weight: 600; } svg { background: white; border-radius: 50%; } @media screen and (max-width: 768px) { div { display: none; } }\\`;const ProfileImageContainer = styled.div\\` background: white; border-radius: 50%; padding: 6px; position: relative; width: 120px; height: 120px; &.editable { &:after { content: \"\"; position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%; border-radius: 50%; background-color: rgba(45.9, 45.9, 45.9, 0); transition: background-color 0.3s; pointer-events: none; @media screen and (max-width: 768px) { height: 64px; } } &:hover { cursor: pointer; &:after { background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; } } } .profile-image { height: 100%; width: 100%; display: flex; border-radius: 50%; img { object-fit: cover; width: 100%; height: 100%; } } > svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0; /* Start with the image invisible */ transition: opacity 0.3s; z-index: 2; /* Ensure the image is on top */ pointer-events: none; } @media screen and (max-width: 768px) { width: 100px; height: 100px; }\\`;const BackgroundImageContainer = styled.div\\` position: relative; width: 100%; height: 318px; display: flex; &.editable { &:after { content: \"\"; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(45.9, 45.9, 45.9, 0); transition: background-color 0.3s; pointer-events: none; } &:hover { cursor: pointer; &:after { background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; } } } img { object-fit: cover; height: 100%; width: 100%; } svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0; transition: opacity 0.3s; z-index: 2; pointer-events: none; } @media screen and (max-width: 768px) { height: 264px; }\\`; const statuses = { Approved: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M10.7835 0.943599C10.3834 0.454917 9.6361 0.454917 9.236 0.943599L8.44604 1.90847C8.17411 2.24062 7.72052 2.36216 7.31895 2.21048L6.15238 1.76985C5.56155 1.54669 4.91436 1.92035 4.8122 2.5436L4.61051 3.77419C4.54108 4.19781 4.20904 4.52985 3.78542 4.59928L2.55484 4.80097C1.93158 4.90313 1.55792 5.55032 1.78108 6.14115L2.22171 7.30772C2.37339 7.70929 2.25185 8.16288 1.9197 8.43481L0.95483 9.22477C0.466148 9.62487 0.466147 10.3722 0.954829 10.7723L1.9197 11.5622C2.25185 11.8342 2.37339 12.2878 2.22171 12.6893L1.78108 13.8559C1.55792 14.4467 1.93158 15.0939 2.55484 15.1961L3.78542 15.3978C4.20904 15.4672 4.54108 15.7992 4.61051 16.2229L4.8122 17.4534C4.91436 18.0767 5.56155 18.4504 6.15238 18.2272L7.31895 17.7866C7.72052 17.6349 8.17411 17.7564 8.44604 18.0886L9.236 19.0535C9.6361 19.5421 10.3834 19.5421 10.7835 19.0535L11.5735 18.0886C11.8454 17.7564 12.299 17.6349 12.7006 17.7866L13.8671 18.2272C14.458 18.4504 15.1052 18.0767 15.2073 17.4534L15.409 16.2229C15.4784 15.7992 15.8105 15.4672 16.2341 15.3978L17.4647 15.1961C18.0879 15.0939 18.4616 14.4467 18.2384 13.8559L17.7978 12.6893C17.6461 12.2878 17.7677 11.8342 18.0998 11.5622L19.0647 10.7723C19.5534 10.3722 19.5534 9.62487 19.0647 9.22478L18.0998 8.43481C17.7677 8.16288 17.6461 7.70929 17.7978 7.30772L18.2384 6.14115C18.4616 5.55032 18.0879 4.90313 17.4647 4.80097L16.2341 4.59928C15.8105 4.52985 15.4784 4.19781 15.409 3.77419L15.2073 2.54361C15.1052 1.92035 14.458 1.54669 13.8671 1.76985L12.7006 2.21048C12.299 2.36216 11.8454 2.24062 11.5735 1.90847L10.7835 0.943599ZM13.5029 7.49591C13.3065 7.30179 12.9899 7.3036 12.7957 7.49996L8.92649 11.4119L7.25946 9.53742C7.07595 9.33107 6.75991 9.31256 6.55356 9.49607C6.34722 9.67958 6.3287 9.99562 6.51221 10.202L8.8858 12.8709L9.90713 11.8496L13.5073 8.20316C13.7013 8.00663 13.6994 7.69003 13.5029 7.49591Z\" fill=\"#0DBFAF\" /> </svg>, color: \"#0E615E\" }, Rejected: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM13.59 5L10 8.59L6.41 5L5 6.41L8.59 10L5 13.59L6.41 15L10 11.41L13.59 15L15 13.59L11.41 10L15 6.41L13.59 5Z\" fill=\"#F6767A\" /> </svg>, color: \"#ED464F\" }, Pending: { icon: <svg width=\"21\" height=\"20\" viewBox=\"0 0 21 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.1523 0C4.63234 0 0.152344 4.48 0.152344 10C0.152344 15.52 4.63234 20 10.1523 20C15.6723 20 20.1523 15.52 20.1523 10C20.1523 4.48 15.6723 0 10.1523 0ZM10.1523 18C5.73234 18 2.15234 14.42 2.15234 10C2.15234 5.58 5.73234 2 10.1523 2C14.5723 2 18.1523 5.58 18.1523 10C18.1523 14.42 14.5723 18 10.1523 18Z\" fill=\"#F4B37D\" /> <path d=\"M5.15234 11.5C5.98077 11.5 6.65234 10.8284 6.65234 10C6.65234 9.17157 5.98077 8.5 5.15234 8.5C4.32392 8.5 3.65234 9.17157 3.65234 10C3.65234 10.8284 4.32392 11.5 5.15234 11.5Z\" fill=\"#F4B37D\" /> <path d=\"M10.1523 11.5C10.9808 11.5 11.6523 10.8284 11.6523 10C11.6523 9.17157 10.9808 8.5 10.1523 8.5C9.32392 8.5 8.65234 9.17157 8.65234 10C8.65234 10.8284 9.32392 11.5 10.1523 11.5Z\" fill=\"#F4B37D\" /> <path d=\"M15.1523 11.5C15.9808 11.5 16.6523 10.8284 16.6523 10C16.6523 9.17157 15.9808 8.5 15.1523 8.5C14.3239 8.5 13.6523 9.17157 13.6523 10C13.6523 10.8284 14.3239 11.5 15.1523 11.5Z\" fill=\"#F4B37D\" /> </svg>, color: \"#EA6A25\" }, Graylisted: { icon: <svg width=\"21\" height=\"20\" viewBox=\"0 0 21 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9.15234 16H11.1523V14H9.15234V16ZM10.1523 0C4.63234 0 0.152344 4.48 0.152344 10C0.152344 15.52 4.63234 20 10.1523 20C15.6723 20 20.1523 15.52 20.1523 10C20.1523 4.48 15.6723 0 10.1523 0ZM10.1523 18C5.74234 18 2.15234 14.41 2.15234 10C2.15234 5.59 5.74234 2 10.1523 2C14.5623 2 18.1523 5.59 18.1523 10C18.1523 14.41 14.5623 18 10.1523 18ZM10.1523 4C7.94234 4 6.15234 5.79 6.15234 8H8.15234C8.15234 6.9 9.05234 6 10.1523 6C11.2523 6 12.1523 6.9 12.1523 8C12.1523 10 9.15234 9.75 9.15234 13H11.1523C11.1523 10.75 14.1523 10.5 14.1523 8C14.1523 5.79 12.3623 4 10.1523 4Z\" fill=\"#7B7B7B\" /> </svg>, color: \"#7B7B7B\" }, Blacklisted: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM2 10C2 5.58 5.58 2 10 2C11.85 2 13.55 2.63 14.9 3.69L3.69 14.9C2.63 13.55 2 11.85 2 10ZM10 18C8.15 18 6.45 17.37 5.1 16.31L16.31 5.1C17.37 6.45 18 8.15 18 10C18 14.42 14.42 18 10 18Z\" fill=\"#292929\" /> </svg>, color: \"#292929\" }}; const FollowStats = ({ projectId: _projectId, accountId: _accountId}) => { const projectId = _projectId; const accountId = projectId || _accountId; if (!accountId) { return \"\"; } const following = Social.keys(\\`\\${accountId}/graph/follow/*\\`, \"final\", { return_type: \"BlockHeight\", values_only: true }); const followers = Social.keys(\\`*/graph/follow/\\${accountId}\\`, \"final\", { return_type: \"BlockHeight\", values_only: true }); const numFollowing = following ? Object.keys(following[accountId].graph.follow || {}).length : 0; const numFollowers = followers ? Object.keys(followers || {}).length : 0; const profileLink = hrefWithParams(\\`?tab=\\${projectId ? \"project\" : \"profile\"}&\\${projectId ? \"projectId\" : \"accountId\"}=\\${projectId || accountId}\\`); const Container = styled.div\\` display: flex; align-items: center; font-size: 14px; gap: 2rem; a { gap: 8px; display: flex; } @media screen and (max-width: 768px) { gap: 1rem; } \\`; return <Container> <div> <a href={\\`\\${profileLink}&nav=followers\\`} className=\"text-dark\"> {numFollowers !== null ? <span style={{ fontWeight: 600 }}>{numFollowers}</span> : \"-\"} <span>Follower{numFollowers !== 1 && \"s\"}</span> </a> </div> <div> <a href={\\`\\${profileLink}&nav=following\\`} className=\"text-dark\"> {numFollowing !== null ? <span style={{ fontWeight: 600 }}>{numFollowing}</span> : \"-\"} <span>Following</span> </a> </div> </Container>;}; const CameraSvg = ({ height}) => <svg width={height || 48} height={height || 48} viewBox={\\`0 0 48 48\\`} fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <mask id=\"mask0_3178_2528\" style={{ maskType: \"alpha\" }} maskUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width={height || 48} height={height || 48}> <rect width={height || 48} height={height || 48} fill=\"#D9D9D9\" /> </mask> <g mask=\"url(#mask0_3178_2528)\"> <path d=\"M5 40.35C4.2 40.35 3.5 40.05 2.9 39.45C2.3 38.85 2 38.15 2 37.35V11.7C2 10.9333 2.3 10.2417 2.9 9.625C3.5 9.00833 4.2 8.7 5 8.7H12.35L16 4.35H29.7V7.35H17.4L13.75 11.7H5V37.35H39V16.65H42V37.35C42 38.15 41.6917 38.85 41.075 39.45C40.4583 40.05 39.7667 40.35 39 40.35H5ZM39 11.65V7.35H34.7V4.35H39V0H42V4.35H46.35V7.35H42V11.65H39ZM21.975 33C24.3917 33 26.4167 32.1833 28.05 30.55C29.6833 28.9167 30.5 26.8917 30.5 24.475C30.5 22.0583 29.6833 20.0417 28.05 18.425C26.4167 16.8083 24.3917 16 21.975 16C19.5583 16 17.5417 16.8083 15.925 18.425C14.3083 20.0417 13.5 22.0583 13.5 24.475C13.5 26.8917 14.3083 28.9167 15.925 30.55C17.5417 32.1833 19.5583 33 21.975 33ZM21.975 30C20.3917 30 19.0833 29.475 18.05 28.425C17.0167 27.375 16.5 26.0583 16.5 24.475C16.5 22.8917 17.0167 21.5833 18.05 20.55C19.0833 19.5167 20.3917 19 21.975 19C23.5583 19 24.875 19.5167 25.925 20.55C26.975 21.5833 27.5 22.8917 27.5 24.475C27.5 26.0583 26.975 27.375 25.925 28.425C24.875 29.475 23.5583 30 21.975 30Z\" fill=\"white\" /> </g> </svg>; const BannerHeader = (__props__) => { const { showFollowers, registration, projectId, profileImageOnChange, bgImageOnChange } = __props__; const accountId = __props__.accountId || projectId || context.accountId; if (!accountId) { return \"No account ID\"; } const editable = bgImageOnChange && profileImageOnChange; const profile = __props__.profile ?? Social.getr(\\`\\${accountId}/profile\\`); const image = profile.image; const backgroundImage = __props__.backgroundImage || profile.backgroundImage; const profileImage = __props__.profileImage || image; const imageStyle = __props__.imageStyle ?? {}; const backgroundStyle = __props__.backgroundStyle ?? {}; const containerStyle = __props__.containerStyle ?? {}; const nadaBotVerified = Near.view(\"v1.nadabot.near\", \"is_human\", { account_id: accountId }); return <A_166 style={{ ...containerStyle }}> <BackgroundImageContainer className={editable ? \"editable\" : \"\"} style={{ height: backgroundStyle.height ? backgroundStyle.height : \"\" }}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: backgroundImage, alt: \"profile background\", style: { ...backgroundStyle, pointerEvents: \"none\" }, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\" }, ...props } }} /> <CameraSvg height={48} /> {editable && <Files multiple={false} accepts={[\"image/*\"]} minFileSize={1} style={{ zIndex: 4, top: 0, width: \"100%\", height: backgroundStyle.height ?? \"100%\", position: \"absolute\" }} clickable onChange={bgImageOnChange} />} </BackgroundImageContainer> <ProfileWraper> <ProfileImageContainer className={editable ? \"editable\" : \"\"}> <CameraSvg height={24} /> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ style: { ...imageStyle }, className: \"profile-image\", image: profileImage, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\", ...props } }} /> {editable && <Files multiple={false} accepts={[\"image/*\"]} minFileSize={1} style={{ position: \"absolute\", width: \"100%\", height: \"100%\", left: 0, top: 0 }} clickable onChange={profileImageOnChange}> </Files>} </ProfileImageContainer> {showFollowers && <ProfileStats> {registration ? <Verified> {statuses[registration.status].icon} <div style={{ color: statuses[registration.status].color }}> {registration.status}</div> </Verified> : nadaBotVerified ? <Verified> {statuses.Approved.icon} <div style={{ color: statuses.Approved.color }}> Verified</div> </Verified> : <div style={{ width: \"10px\" }} />} <FollowStats accountId={projectId || accountId} projectId={projectId} /> </ProfileStats>} </ProfileWraper> {__props__.children && __props__.children} </A_166>; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const validateNearAddress = address => { const NEAR_ACCOUNT_ID_REGEX = /^(?=.{2,64}$)(?!.*\\\\.\\\\.)(?!.*-$)(?!.*_$)[a-z\\\\d._-]+$/i; let isValid = NEAR_ACCOUNT_ID_REGEX.test(address); if (address.length < 64 && !address.endsWith(\".near\")) { isValid = false; } return isValid;}; const getTeamMembersFromSocialProfileData = profileData => { if (!profileData) return []; const team = profileData.plTeam ? JSON.parse(profileData.plTeam) : profileData.team ? Object.entries(profileData.team).filter(([_, v]) => v !== null).map(([k, _]) => k) : []; return team;}; function doesUserHaveDaoFunctionCallProposalPermissions(accountId, policy) { const userRoles = policy.roles.filter(role => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); return allowed;} const A_13 = styled.div\\` display: flex; flex-direction: column; width: 100%; padding: 72px 64px 72px 64px; @media screen and (max-width: 768px) { padding: 0px; }\\`;const LowerBannerContainer = styled.div\\` position: absolute; top: 340px; left: 0px; display: flex; align-items: stretch; /* Ensuring child elements stretch to full height */ justify-content: space-between; width: 100%; z-index: 10; @media screen and (max-width: 768px) { top: 310px; position: initial; align-items: flex-start; gap: 10px; flex-direction: column; }\\`;const LowerBannerContainerLeft = styled.div\\` display: flex; flex-direction: row; align-items: flex-end; margin-left: 190px; @media screen and (max-width: 768px) { margin-left: 0px; }\\`;const LowerBannerContainerRight = styled.div\\` display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; /* Pushes TeamContainer to the bottom */ flex: 1;\\`;const AddTeamMembers = styled.a\\` margin: 0px 0px 16px 36px; cursor: pointer; color: #dd3345; font-size: 14px; font-weight: 600; &:hover { text-decoration: none; } @media screen and (max-width: 768px) { margin-bottom: 0; }\\`;const FormBody = styled.div\\` display: flex; flex-direction: column; padding: 0px 68px 32px 68px; width: 100%; @media screen and (max-width: 768px) { padding: 0px 32px 32px 32px; }\\`;const FormDivider = styled.div\\` height: 2px; width: 100%; background-color: #ebebeb;\\`;const FormSectionContainer = styled.div\\` display: flex; flex-direction: row; gap: 160px; margin: 48px 0 48px 0; @media screen and (max-width: 768px) { flex-direction: column; gap: 32px; }\\`;const FormSectionLeftDiv = styled.div\\` flex: 1; display: flex; flex-direction: column; /* background-color: yellow; */ gap: 16px;\\`;const FormSectionRightDiv = styled.div\\` flex: 1; display: flex; flex-direction: column; /* background-color: lightblue; */\\`;const FormSectionTitle = styled.div\\` color: #2e2e2e; font-size: 16; font-weight: 600; word-wrap: break-word;\\`;const FormSectionDescription = styled.div\\` color: #2e2e2e; font-size: 16; font-weight: 400; word-wrap: break-word;\\`;const FormSectionIsRequired = styled.div\\` font-size: 16px; font-weight: 400; word-wrap: break-word; position: relative;\\`;const SvgContainer = styled.div\\` position: absolute; top: -6; left: -26;\\`;const ButtonsContainer = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 32px; margin-top: 32px;\\`;const Space = styled.div\\`\\`;const InputPrefix = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400; box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset;\\`;const A_14 = styled.div\\` display: flex; flex-direction: row; gap: 16px; align-items: flex-start; justify-content: center;\\`;const A_15 = styled.svg\\` width: 20px; height: 20px; cursor: pointer; path { transition: 300ms; } :hover path { fill: #dd3345; }\\`;const FundingHeader = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; background: #f6f5f3; width: 100%;\\`;const FundingHeaderItem = styled.div\\` display: flex; flex-direction: row; align-items: space-between; justify-content: flex-start; padding: 10px 20px;\\`;const FundingHeaderItemText = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const Table = styled.div\\` display: flex; flex-direction: column; border-radius: 6px; border: 1px solid #7b7b7b; background: #fff; .header, .fudning-row { display: flex; justify-content: space-between; } .header { border-bottom: 0.5px solid #7b7b7b; } .fudning-row:not(:last-of-type) { border-bottom: 0.5px solid #7b7b7b; } .item { width: 140px; display: flex; align-items: center; &:nth-of-type(1) { width: 190px; } &:nth-of-type(2) { flex: 1; } } .source { width: 190px; flex-direction: column; align-items: flex-start; gap: 4px; div { font-weight: 600; } div:last-of-type { color: #7b7b7b; font-weight: 400; } } .amount { display: flex; gap: 0.5rem; justify-content: flex-end; div:last-child { font-weight: 600; } } .btns { width: 95px; gap: 2rem; justify-content: space-between; svg { cursor: pointer; path { transition: 300ms ease-in-out; } &:hover { path { fill: black; } } } } .header .item { padding: 10px 1rem; color: #7b7b7b; font-weight: 600; } .fudning-row .item { padding: 1rem 1rem; } @media only screen and (max-width: 769px) { .header { display: none; } .fudning-row { flex-direction: column; } .item { width: 100%; justify-content: flex-start; } }\\`; const InfoIcon = __props__ => { return <svg {...__props__} viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.0001 13.3327V9.99935M10.0001 6.66602H10.0084M18.3334 9.99935C18.3334 14.6017 14.6025 18.3327 10.0001 18.3327C5.39771 18.3327 1.66675 14.6017 1.66675 9.99935C1.66675 5.39698 5.39771 1.66602 10.0001 1.66602C14.6025 1.66602 18.3334 5.39698 18.3334 9.99935Z\" stroke=\"#475467\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg>;}; const A_16 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: flex-start; padding: 1em; gap: 0.75em; background: #fcfcfd; border: 1px solid #d0d5dd; border-radius: 4px; width: 100%; svg { width: 20px; }\\`;const Text = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; padding: 0px; gap: 0.75em;\\`;const Heading = styled.div\\` font-style: normal; font-weight: 600; font-size: 0.95em; line-height: 1.25em; color: #344054;\\`;const Description = styled.p\\` font-style: normal; font-weight: 400; font-size: 0.95em; line-height: 1.25em; color: #475467; white-space: wrap; margin: 0px;\\`; const InfoSegment = ({ title, description}) => { const icon = <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.0001 13.3327V9.99935M10.0001 6.66602H10.0084M18.3334 9.99935C18.3334 14.6017 14.6025 18.3327 10.0001 18.3327C5.39771 18.3327 1.66675 14.6017 1.66675 9.99935C1.66675 5.39698 5.39771 1.66602 10.0001 1.66602C14.6025 1.66602 18.3334 5.39698 18.3334 9.99935Z\" stroke=\"#475467\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg>; return <A_16> <InfoIcon /> <Text> <Heading>{title}</Heading> <Description>{description}</Description> </Text> </A_16>;}; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const props = props; const { projectId } = props.alem.useParams(); const HORIZON_CONTRACT_ID = \"nearhorizon.near\"; const SOCIAL_CONTRACT_ID = \"social.near\"; const ownerId = \"potlock.near\"; Big.PE = 100; const FIFTY_TGAS = \"50000000000000\"; const THREE_HUNDRED_TGAS = \"300000000000000\"; const MIN_PROPOSAL_DEPOSIT_FALLBACK = \"100000000000000000000000\"; const DEFAULT_BANNER_IMAGE_CID = \"bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\"; if (!context.accountId) { return <InfoSegment {...{ title: \"Not logged in!\", description: \"You must log in to create a new project!\" }} />; } const existingHorizonProject = Near.view(HORIZON_CONTRACT_ID, \"get_project\", { account_id: context.accountId }); const registrations = ListsSDK.getRegistrations() || []; State.init({ isDao: false, daoAddressTemp: \"\", daoAddress: \"\", daoAddressError: \"\", existingSocialData: {}, backgroundImage: { ipfs_cid: DEFAULT_BANNER_IMAGE_CID }, profileImage: \"\", name: \"\", nameError: \"\", originalCategories: [], categories: [], categoriesError: \"\", description: \"\", descriptionError: \"\", publicGoodReason: \"\", publicGoodReasonError: \"\", hasSmartContracts: false, originalSmartContracts: [], smartContracts: [[\"\", \"\"]], originalGithubRepos: [], githubRepos: [[\"\"]], hasReceivedFunding: false, fundingSourceIndex: null, originalFundingSources: [], fundingSources: [], website: \"\", websiteError: \"\", twitter: \"\", twitterError: \"\", telegram: \"\", telegramError: \"\", github: \"\", githubError: \"\", socialDataFetched: false, socialDataIsFetching: false, isMultiAccountModalOpen: false, teamMember: \"\", teamMembers: [], nearAccountIdError: \"\", registrationSuccess: false, showAlert: false, alertMessage: \"\" }); const CATEGORY_MAPPINGS = { SOCIAL_IMPACT: \"Social Impact\", NON_PROFIT: \"NonProfit\", CLIMATE: \"Climate\", PUBLIC_GOOD: \"Public Good\", DE_SCI: \"DeSci\", OPEN_SOURCE: \"Open Source\", COMMUNITY: \"Community\", EDUCATION: \"Education\", _deprecated: { \"social-impact\": \"SOCIAL_IMPACT\", \"non-profit\": \"NON_PROFIT\", climate: \"CLIMATE\", \"public-good\": \"PUBLIC_GOOD\", \"de-sci\": \"DE_SCI\", \"open-source\": \"OPEN_SOURCE\", community: \"COMMUNITY\", education: \"EDUCATION\" } }; const CHAIN_OPTIONS = { NEAR: { isEVM: false }, Solana: { isEVM: false }, Ethereum: { isEVM: true }, Polygon: { isEVM: true }, Avalanche: { isEVM: true }, Optimism: { isEVM: true }, Arbitrum: { isEVM: true }, BNB: { isEVM: true }, Sui: { isEVM: false }, Aptos: { isEVM: false }, Polkadot: { isEVM: false }, Stellar: { isEVM: false }, ZkSync: { isEVM: false }, Celo: { isEVM: true }, Aurora: { isEVM: true }, Injective: { isEVM: true }, Base: { isEVM: false }, Manta: { isEVM: false }, Fantom: { isEVM: true }, ZkEVM: { isEVM: true }, Flow: { isEVM: false }, Tron: { isEVM: true }, MultiverseX: { isEVM: false }, Scroll: { isEVM: true }, Linea: { isEVM: true }, Metis: { isEVM: true } }; const accountId = projectId ? projectId : state.isDao ? state.daoAddress : context.accountId; const policy = Near.view(accountId, \"get_policy\", {}); const userHasPermissions = policy == null ? false : policy ? doesUserHaveDaoFunctionCallProposalPermissions(context.accountId, policy) : projectId === context.accountId; const setSocialData = (accountId, shouldSetTeamMembers) => { Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${accountId}/**\\`] }).then((socialData) => { if (!socialData || !socialData[accountId].profile) { State.update({ socialDataFetched: true, name: \"\", originalCategories: [], categories: [], description: \"\", website: \"\", twitter: \"\", telegram: \"\", github: \"\", teamMembers: [] }); return; } const profileData = socialData[accountId].profile; const backgroundImage = profileData.backgroundImage; const profileImage = profileData.image || \"\"; const description = profileData.description || \"\"; const publicGoodReason = profileData.plPublicGoodReason || \"\"; let categories = []; if (profileData.plCategories) { categories = JSON.parse(profileData.plCategories); } else if (profileData.category) { if (typeof profileData.category == \"string\") { const availableCategory = CATEGORY_MAPPINGS[CATEGORY_MAPPINGS._deprecated[profileData.category]]; if (availableCategory) { categories.push(availableCategory); } } } const smartContracts = profileData.plSmartContracts ? Object.entries(JSON.parse(profileData.plSmartContracts)).reduce((accumulator, [chain, contracts]) => { const contractsForChain = Object.keys(contracts).map((contractAddress) => { return [chain, contractAddress]; }); return accumulator.concat(contractsForChain); }, []) : []; const hasSmartContracts = smartContracts.length > 0; smartContracts.push([\"\", \"\"]); const githubRepos = profileData.plGithubRepos ? JSON.parse(profileData.plGithubRepos).map((repo) => [repo]) : []; const originalGithubRepos = githubRepos; githubRepos.push([\"\"]); const fundingSources = profileData.plFundingSources ? JSON.parse(profileData.plFundingSources) : []; const hasReceivedFunding = fundingSources.length > 0; const linktree = profileData.linktree || {}; const twitter = linktree.twitter || \"\"; const telegram = linktree.telegram || \"\"; const github = linktree.github || \"\"; const website = linktree.website || \"\"; const team = getTeamMembersFromSocialProfileData(profileData); const stateUpdates = { existingSocialData: socialData[accountId], backgroundImage, profileImage, name: profileData?.name || \"\", description, publicGoodReason, originalCategories: categories, categories, hasSmartContracts, originalSmartContracts: smartContracts, smartContracts, originalGithubRepos, githubRepos, hasReceivedFunding, originalFundingSources: fundingSources, fundingSources, twitter, telegram, github, website, socialDataFetched: true }; if (backgroundImage) { stateUpdates.backgroundImage = backgroundImage; } if (shouldSetTeamMembers) { stateUpdates.teamMembers = team; } State.update(stateUpdates); }).catch((e) => { console.log(\"error getting social data: \", e); State.update({ socialDataFetched: true }); }); }; useEffect(() => { if (state.isDao && state.daoAddress) { setSocialData(state.daoAddress, true); } else if (!state.isDao && context.accountId && !state.socialDataFetched) { setSocialData(context.accountId, true); } }, [state.socialDataFetched, state.isDao, state.daoAddress, context.accountId]); const isCreateProjectDisabled = !state.profileImage || !state.backgroundImage || state.daoAddressError || !state.name || state.nameError || !state.description || state.descriptionError || !state.publicGoodReason || state.publicGoodReasonError || state.categories.includes(CATEGORY_MAPPINGS.OPEN_SOURCE) && !state.githubRepos.filter((val) => val[0]).length || state.hasSmartContracts && !state.smartContracts.filter((val) => val[0]).length || state.hasReceivedFunding && !state.fundingSources.length || !state.categories.length || state.categoriesError; const deepObjectDiff = (objOriginal, objUpdated) => { if (!objUpdated) objUpdated = {}; let diff = {}; function findDiff(original, updated, diffObj) { Object.keys(updated).forEach((key) => { const updatedValue = updated[key]; const originalValue = original ? original[key] : undefined; if (typeof updatedValue === \"object\" && updatedValue !== null && (originalValue === undefined || typeof originalValue === \"object\" && originalValue !== null)) { const nestedDiff = originalValue ? findDiff(originalValue, updatedValue, {}) : updatedValue; if (Object.keys(nestedDiff).length > 0) { diffObj[key] = nestedDiff; } } else if (updatedValue !== originalValue) { diffObj[key] = updatedValue; } }); return diffObj; } return findDiff(objOriginal, objUpdated, diff); }; const handleCreateOrUpdateProject = (e) => { if (isCreateProjectDisabled) return; const daoAddressValid = state.isDao ? validateNearAddress(state.daoAddress) : true; if (!daoAddressValid) { State.update({ daoAddressError: \"Invalid NEAR account ID\" }); return; } const formattedSmartContracts = state.smartContracts.reduce((accumulator, [chain, contractAddress]) => { if (!chain || !contractAddress) return accumulator; if (!accumulator[chain]) { accumulator[chain] = {}; } accumulator[chain][contractAddress] = \"\"; return accumulator; }, {}); const socialData = { profile: { name: state.name, plCategories: JSON.stringify(state.categories), description: state.description, plPublicGoodReason: state.publicGoodReason, plSmartContracts: state.hasSmartContracts ? JSON.stringify(formattedSmartContracts) : null, plGithubRepos: JSON.stringify(state.githubRepos.map((repo) => repo[0]).filter((val) => val)), plFundingSources: JSON.stringify(state.fundingSources), linktree: { website: state.website, twitter: state.twitter, telegram: state.telegram, github: state.github }, plTeam: JSON.stringify(state.teamMembers) }, index: { star: { key: { type: \"social\", path: \\`\\${ownerId}/widget/Index\\` }, value: { type: \"star\" } }, notify: { key: ownerId, value: { type: \"star\", item: { type: \"social\", path: \\`\\${ownerId}/widget/Index\\` } } } }, graph: { star: { [ownerId]: { widget: { Index: \"\" } } }, follow: { [ownerId]: \"\" } } }; if (state.backgroundImage) { socialData.profile.backgroundImage = state.backgroundImage; } if (state.profileImage) { socialData.profile.image = state.profileImage; } const diff = deepObjectDiff(state.existingSocialData, socialData); const socialArgs = { data: { [accountId]: diff } }; const potlockRegistryArgs = { list_id: 1 }; const horizonArgs = { account_id: state.isDao ? state.daoAddress : context.accountId }; Near.asyncView(SOCIAL_CONTRACT_ID, \"get_account\", { account_id: state.isDao ? state.daoAddress : context.accountId }).then((account) => { const socialTransaction = { contractName: SOCIAL_CONTRACT_ID, methodName: \"set\", args: socialArgs }; let depositFloat = JSON.stringify(socialArgs).length * 0.00015; if (!account) { depositFloat += 0.1; } socialTransaction.deposit = Big(depositFloat).mul(Big(10).pow(24)); let transactions = [socialTransaction]; if (!props.edit) { transactions.push({ contractName: ListsSDK.getContractId(), methodName: \"register_batch\", deposit: Big(0.05).mul(Big(10).pow(24)), args: potlockRegistryArgs }); if (!existingHorizonProject) { transactions.push({ contractName: HORIZON_CONTRACT_ID, methodName: \"add_project\", args: horizonArgs }); } } if (state.isDao) { const clonedTransactions = JSON.parse(JSON.stringify(transactions)); transactions = clonedTransactions.map((tx) => { const action = { method_name: tx.methodName, gas: FIFTY_TGAS, deposit: tx.deposit ? tx.deposit.toString() : \"0\", args: Buffer.from(JSON.stringify(tx.args), \"utf-8\").toString(\"base64\") }; return { ...tx, contractName: state.daoAddress, methodName: \"add_proposal\", args: { proposal: { description: props.edit ? \"Update project on Potlock (via NEAR Social)\" : \"Create project on Potlock (3 steps: Register information on NEAR Social, register on Potlock, and register on NEAR Horizon)\", kind: { FunctionCall: { receiver_id: tx.contractName, actions: [action] } } } }, deposit: policy.proposal_bond || MIN_PROPOSAL_DEPOSIT_FALLBACK, gas: THREE_HUNDRED_TGAS }; }); } Near.call(transactions); const pollIntervalMs = 1000; const pollId = setInterval(() => { ListsSDK.asyncGetRegistration(null, context.accountId).then((_project) => { if (_project) { clearInterval(pollId); State.update({ registrationSuccess: true }); } }); }, pollIntervalMs); }); }; if (projectId) { Near.asyncView(projectId, \"get_policy\", {}).then((policy) => { if (policy) { State.update({ isDao: true, daoAddress: projectId, daoAddressTemp: projectId }); } }); } const registeredProject = useMemo(() => { return ListsSDK.getRegistration(null, state.isDao ? state.daoAddress : context.accountId); }, [state.isDao, state.daoAddress]); console.log(\"registeredProject: \", registeredProject); const proposals = Near.view(state.daoAddress, \"get_proposals\", { from_index: 0, limit: 1000 }); const proposalInProgress = useMemo(() => { if (!state.isDao || !state.daoAddress || !proposals) return false; return proposals?.find((proposal) => { return proposal.status == \"InProgress\" && proposal.kind.FunctionCall?.receiver_id == ListsSDK.getContractId() && proposal.kind.FunctionCall?.actions[0]?.method_name == \"register\"; }); }, [state, proposals]); const handleAddTeamMember = () => { let isValid = validateNearAddress(state.teamMember); if (!isValid) { State.update({ nearAccountIdError: \"Invalid NEAR account ID\" }); return; } if (!state.teamMembers.find((tm) => tm == state.teamMember)) { State.update({ teamMembers: [...state.teamMembers, state.teamMember], teamMember: \"\", nearAccountIdError: \"\" }); } }; const FormSectionLeft = (title, description, isRequired) => { return <FormSectionLeftDiv> <FormSectionTitle>{title}</FormSectionTitle> <FormSectionDescription>{description}</FormSectionDescription> <FormSectionIsRequired style={{ color: isRequired ? \"#DD5633\" : \"#7B7B7B\" }}> {isRequired ? \"Required\" : \"Optional\"} {isRequired && <SvgContainer style={{ top: -6, left: -26 }}> <svg width=\"117\" height=\"31\" viewBox=\"0 0 117 31\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M81.8 3.40116C82.247 3.1908 83.0709 3.13488 82.6 2.60116C81.0461 0.840105 83.0819 0.798833 78.6667 1.22338C65.6302 2.47689 52.5192 4.47997 39.6667 6.95672C31.3106 8.56697 19.0395 10.1936 12.7333 17.09C3.95785 26.6869 29.2286 29.1656 32.9333 29.3567C53.953 30.4413 75.9765 28.9386 96.5111 24.1789C99.8286 23.41 122.546 18.5335 112.733 11.5345C107.621 7.88815 100.796 6.47335 94.7333 5.75672C77.7504 3.74928 60.1141 5.22649 43.2222 7.35671C28.8721 9.16641 14.4138 11.8506 1 17.4012\" stroke=\"#2E2E2E\" stroke-width=\"1.8\" stroke-linecap=\"round\" /> </svg> </SvgContainer>} </FormSectionIsRequired> </FormSectionLeftDiv>; }; const DeleteIcon = (props) => <A_15 {...props} viewBox=\"0 0 12 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M2.5 14C2.0875 14 1.73437 13.8531 1.44062 13.5594C1.14687 13.2656 1 12.9125 1 12.5V2.5H0V1H4V0H8V1H12V2.5H11V12.491C11 12.9137 10.8531 13.2708 10.5594 13.5625C10.2656 13.8542 9.9125 14 9.5 14H2.5ZM9.5 2.5H2.5V12.5H9.5V2.5ZM4 11H5.5V4H4V11ZM6.5 11H8V4H6.5V11Z\" fill=\"#7B7B7B\" /> </A_15>; if (props.edit && !userHasPermissions) { return <h3 style={{ textAlign: \"center\", paddingTop: \"32px\" }}>Unauthorized</h3>; } const uploadFileUpdateState = (body, callback) => { asyncFetch(\"https://ipfs.near.social/add\", { method: \"POST\", headers: { Accept: \"application/json\" }, body }).then(callback); }; console.log(state.fundingSources); return <A_13> {!state.socialDataFetched || !registrations ? <div className=\"spinner-border text-secondary\" role=\"status\" /> : proposalInProgress ? <A_13 style={{ padding: \"32px 16px\", justifyContent: \"center\", alignItems: \"center\", wordWrap: \"break-word\" }}> <h1 style={{ textAlign: \"center\" }}>You have a DAO proposal in progress.</h1> <h5 style={{ wordWrap: \"break-word\", textAlign: \"center\" }}> Please come back once voting on your proposal has been completed. </h5> <div style={{ fontStyle: \"italic\", fontFamily: \"sans-serif\", wordWrap: \"break-word\", textAlign: \"center\" }}> NB: This proposal consists of 3 steps (individual proposals): Register information on NEAR Social, register on Potlock, and register on NEAR Horizon. </div> <a target=\"_blank\" href={\\`https://near.org/sking.near/widget/DAO.Page?daoId=\\${state.daoAddress}&tab=proposal&proposalId=\\${proposalInProgress.id}\\`} style={{ marginTop: \"16px\" }}> View DAO Proposal </a> </A_13> : !props.edit && (registeredProject || state.registrationSuccess) ? <> <h1 style={{ textAlign: \"center\" }}>You've successfully registered!</h1> <ButtonsContainer> <Button {...{ type: \"primary\", text: \"View your project\", disabled: false, href: hrefWithParams(\\`?tab=project&projectId=\\${registeredProject?.id || context.accountId}\\`) }} /> <Button {...{ type: \"secondary\", text: \"View all projects\", disabled: false, href: hrefWithParams(\\`?tab=projects\\`) }} /> </ButtonsContainer> </> : <> <BannerHeader {...{ projectId: state.isDao && state.daoAddress ? state.daoAddress : context.accountId, backgroundImage: state.backgroundImage, profileImage: state.profileImage, bgImageOnChange: (files) => { if (files) { uploadFileUpdateState(files[0], (res) => { const ipfs_cid = res.body.cid; State.update({ backgroundImage: { ipfs_cid } }); }); } }, profileImageOnChange: (files) => { if (files) { uploadFileUpdateState(files[0], (res) => { const ipfs_cid = res.body.cid; State.update({ profileImage: { ipfs_cid } }); }); } }, children: <LowerBannerContainer> <LowerBannerContainerLeft> <AddTeamMembers onClick={() => State.update({ isMultiAccountModalOpen: true })}> {state.teamMembers.length > 0 ? \"Add or remove team members\" : \"Add team members\"} </AddTeamMembers> </LowerBannerContainerLeft> <LowerBannerContainerRight> <Widget loading=\" \" code={props.alem.componentsCode.AccountsStack} props={{ ...{ ...{ accountIds: state.teamMembers, sendToBack: state.isMultiAccountModalOpen }, ...props } }} /> </LowerBannerContainerRight> </LowerBannerContainer> }} /> <FormBody> <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Project details\", \"Give an overview of your project including background details and your mission.\", true)} <FormSectionRightDiv> <CheckBox {...{ id: \"masterSelector\", checked: state.isDao, onClick: (e) => { State.update({ isDao: e.target.checked }); if (!e.target.checked && context.accountId) { setSocialData(context.accountId); } else { if (state.daoAddress) { setSocialData(state.daoAddress); } } }, label: \"Register as DAO\", disabled: props.edit, containerStyle: { marginBottom: \"24px\" } }} /> <A_62 {...{ label: state.isDao ? \"DAO address *\" : \"Project ID *\", value: state.isDao ? state.daoAddressTemp : context.accountId, disabled: !state.isDao, onChange: (daoAddress) => State.update({ daoAddressTemp: daoAddress.toLowerCase(), daoAddressError: \"\" }), validate: () => { if (state.isDao) { const isValid = validateNearAddress(state.daoAddressTemp); if (!isValid) { State.update({ daoAddressError: \"Invalid NEAR account ID\" }); return; } const NO_PERMISSIONS_ERROR = \"You do not have required roles for this DAO\"; Near.asyncView(state.daoAddressTemp, \"get_policy\", {}).then((policy) => { const userRoles = policy.roles.filter((role) => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(context.accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); if (!allowed) { State.update({ daoAddressError: NO_PERMISSIONS_ERROR }); } else { const councilRole = policy.roles.find((role) => role.name === \"council\"); const councilTeamMembers = councilRole?.kind?.Group || []; State.update({ daoAddress: state.daoAddressTemp, teamMembers: councilTeamMembers }); } }).catch((e) => { console.log(\"error getting DAO policy: \", e); State.update({ daoAddressError: NO_PERMISSIONS_ERROR }); }); setSocialData(state.daoAddressTemp, false); } State.update({ daoAddressError: \"\" }); }, error: state.isDao ? state.daoAddressError : \"\" }} /> <Space style={{ height: \"24px\" }} /> <A_62 {...{ label: \"Project name *\", placeholder: \"Enter project name\", value: state.name, onChange: (name) => State.update({ name }), validate: () => { if (state.name.length < 3) { State.update({ nameError: \"Name must be at least 3 characters\" }); return; } if (state.name.length > 100) { State.update({ nameError: \"Name must be less than 100 characters\" }); return; } State.update({ nameError: \"\" }); }, error: state.nameError }} /> <Space style={{ height: \"24px\" }} /> <TextArea {...{ label: \"Overview *\", placeholder: \"Give a short description of your project\", value: state.description, onChange: (description) => State.update({ description }), validate: () => { if (state.description.length > 500) { State.update({ descriptionError: \"Description must be less than 500 characters\" }); return; } State.update({ descriptionError: \"\" }); }, error: state.descriptionError }} /> <Space style={{ height: \"24px\" }} /> <TextArea {...{ label: \"Reason for considering yourself a public good *\", placeholder: \"Type response\", value: state.publicGoodReason, onChange: (publicGoodReason) => State.update({ publicGoodReason }), validate: () => { if (state.publicGoodReason.length > 500) { State.update({ publicGoodReasonError: \"Response must be less than 500 characters\" }); return; } State.update({ publicGoodReasonError: \"\" }); }, error: state.publicGoodReasonError }} /> <Space style={{ height: \"24px\" }} /> <SelectMultiple {...{ label: \"Select category (select multiple) *\", placeholder: \"Choose category\", options: Object.values(CATEGORY_MAPPINGS).filter((el) => typeof el === \"string\"), onChange: (categories) => { State.update({ categories }); }, selected: state.categories }} /> <Space style={{ height: \"24px\" }} /> <CheckBox {...{ id: \"hasSmartContractsSelector\", checked: state.hasSmartContracts, onClick: (e) => { State.update({ hasSmartContracts: e.target.checked }); }, label: \"Yes, my project has smart contracts\", containerStyle: { marginBottom: \"16px\" } }} /> <CheckBox {...{ id: \"hasReceivedFundingSelector\", checked: state.hasReceivedFunding, onClick: (e) => { State.update({ hasReceivedFunding: e.target.checked }); }, label: \"Yes, my project has received funding\" }} /> </FormSectionRightDiv> </FormSectionContainer> {state.categories.includes(CATEGORY_MAPPINGS.OPEN_SOURCE) && <> <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Add Your Repositories\", \"Add full URLs for specific github repositories so we can track their popularity.\", true)} <FormSectionRightDiv> {state.githubRepos.map((repo, index) => { return <A_14 style={{ marginBottom: \"12px\" }} key={index}> <A_62 {...{ label: \"GitHub Repo URL #\" + (index + 1), inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.githubRepos[index][0], onChange: (repo) => State.update({ githubRepos: state.githubRepos.map((r, i) => i == index ? [repo] : [r[0]]) }), validate: () => { const isValid = validateGithubRepoUrl(repo); if (!isValid) { State.update({ githubRepos: state.githubRepos.map((r, i) => i == index ? [r[0], \"Invalid GitHub Repo URL\"] : [r[0]]) }); return; } }, error: state.githubRepos[index][1] || \"\" }} /> {state.githubRepos.length > 1 && <div style={{ height: \"100%\", display: \"flex\", alignItems: \"center\" }}> <DeleteIcon onClick={() => { const updatedRepos = state.githubRepos.filter((r, i) => i != index); State.update({ githubRepos: updatedRepos }); }} /> </div>} </A_14>; })} <Button {...{ type: \"tertiary\", text: \"Add another repository\", disabled: !state.githubRepos[state.githubRepos.length - 1][0], onClick: () => { State.update({ githubRepos: [...state.githubRepos, [\"\"]] }); } }} /> </FormSectionRightDiv> </FormSectionContainer> </>} {state.hasSmartContracts && <> <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Smart contracts\", \"Add smart contracts from different chains that belong to your application.\", true)} <FormSectionRightDiv> {state.smartContracts.map(([chain, contractAddress], index) => { return <A_14 style={{ marginBottom: \"12px\" }} key={index}> <Select {...{ label: \"Add chain\", noLabel: false, placeholder: \"Select chain\", options: Object.keys(CHAIN_OPTIONS).map((chain) => ({ text: chain, value: chain })), value: { text: chain, value: chain }, onChange: (chain) => { const updatedSmartContracts = state.smartContracts.map((sc, i) => { if (i == index) { return [chain.value, sc[1]]; } return sc; }); State.update({ smartContracts: updatedSmartContracts }); } }} /> <A_62 {...{ label: \"Contract address\", placeholder: \"Enter address\", value: contractAddress, onChange: (contractAddress) => { const updatedSmartContracts = state.smartContracts.map((sc, i) => { if (i == index) { return [sc[0], contractAddress]; } return sc; }); State.update({ smartContracts: updatedSmartContracts }); }, validate: () => { const chain = state.smartContracts[index][0]; const isEvm = CHAIN_OPTIONS[chain].isEVM; const isValid = chain == \"NEAR\" ? validateNearAddress(contractAddress) : isEvm ? validateEVMAddress(contractAddress) : true; if (!isValid) { State.update({ smartContracts: state.smartContracts.map((sc, i) => { if (i == index) { return [sc[0], sc[1], \"Invalid address\"]; } return sc; }) }); return; } }, error: state.smartContracts[index][2] || \"\" }} /> {state.smartContracts.length > 1 && <div style={{ height: \"100%\", display: \"flex\", alignItems: \"center\" }}> <DeleteIcon onClick={() => { const updatedSmartContracts = state.smartContracts.filter((sc, i) => i != index); State.update({ smartContracts: updatedSmartContracts }); }} /> </div>} </A_14>; })} <Button {...{ type: \"tertiary\", text: \"Add another contract\", disabled: !state.smartContracts[state.smartContracts.length - 1][0] && !state.smartContracts[state.smartContracts.length - 1][1], onClick: () => { State.update({ smartContracts: [...state.smartContracts, [\"\", \"\"]] }); } }} /> </FormSectionRightDiv> </FormSectionContainer> </>} {state.hasReceivedFunding && <> <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Funding sources\", \"Add any previous funding you have received.\", true)} {} </FormSectionContainer> {state.fundingSources.length > 0 && <Table> <div className=\"header\"> <div className=\"item\">Funding source</div> <div className=\"item\">Description</div> <div className=\"item amount\">Amount</div> <div className=\"btns\" /> </div> {state.fundingSources.map((funding, idx) => <div className=\"fudning-row\" key={funding.investorName}> <div className=\"item source\"> <div>{funding.investorName}</div> {funding.date && <div> {new Date(funding.date).toLocaleDateString(\"en-US\", { month: \"numeric\", day: \"numeric\", year: \"numeric\" })} </div>} </div> <div className=\"item\">{funding.description}</div> <div className=\"item amount\"> <div>{funding.denomination}</div> <div>{funding.amountReceived}</div> </div> <div className=\"btns item\"> {} <svg onClick={() => State.update({ fundingSourceIndex: idx })} width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_446_76)\"> <path d=\"M15.369 0.290547C14.979 -0.0994531 14.349 -0.0994531 13.959 0.290547L12.129 2.12055L15.879 5.87055L17.709 4.04055C18.099 3.65055 18.099 3.02055 17.709 2.63055L15.369 0.290547Z\" fill=\"#7B7B7B\" /> <path d=\"M-0.000976562 18.0005H3.74902L14.809 6.94055L11.059 3.19055L-0.000976562 14.2505V18.0005ZM1.99902 15.0805L11.059 6.02055L11.979 6.94055L2.91902 16.0005H1.99902V15.0805Z\" fill=\"#7B7B7B\" /> </g> <defs> <clipPath id=\"clip0_446_76\"> <rect width=\"18\" height=\"18\" fill=\"white\" /> </clipPath> </defs> </svg> {} <svg onClick={() => { const updatedFundingSources = state.fundingSources.filter((fudning, i) => i !== idx); State.update({ fundingSources: updatedFundingSources }); }} width=\"14\" height=\"18\" viewBox=\"0 0 14 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11 6V16H3V6H11ZM9.5 0H4.5L3.5 1H0V3H14V1H10.5L9.5 0ZM13 4H1V16C1 17.1 1.9 18 3 18H11C12.1 18 13 17.1 13 16V4Z\" fill=\"#7B7B7B\" /> </svg> </div> </div>)} </Table>} <Button {...{ type: \"tertiary\", text: \"Add funding source\", style: { width: \"fit-content\", marginTop: \"1rem\", marginBottom: \"3rem\" }, disabled: state.fundingSources.some((fs) => !fs.investorName || !fs.amountReceived || !fs.denomination || !fs.description), onClick: () => { const updatedFundingSources = [...state.fundingSources, { investorName: \"\", description: \"\", amountReceived: \"\", denomination: \"\" }]; State.update({ fundingSources: updatedFundingSources, fundingSourceIndex: updatedFundingSources.length - 1 }); } }} /> </>} <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Social links\", \"Add your project social links to so supporters can connect with you directly.\", false)} <FormSectionRightDiv> <A_62 {...{ label: \"Twitter\", preInputChildren: <InputPrefix>twitter.com/</InputPrefix>, inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.twitter, onChange: (twitter) => State.update({ twitter: twitter.trim() }), validate: () => { if (state.twitter.length > 15) { State.update({ twitterError: \"Invalid Twitter handle\" }); return; } State.update({ twitterError: \"\" }); }, error: state.twitterError }} /> <Space style={{ height: \"24px\" }} /> <A_62 {...{ label: \"Telegram\", preInputChildren: <InputPrefix>t.me/</InputPrefix>, inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.telegram, onChange: (telegram) => State.update({ telegram: telegram.trim() }), validate: () => {}, error: state.telegramError }} /> <Space style={{ height: \"24px\" }} /> <A_62 {...{ label: \"GitHub\", preInputChildren: <InputPrefix>github.com/</InputPrefix>, inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.github, onChange: (github) => State.update({ github: github.trim() }), validate: () => {}, error: state.githubError }} /> <Space style={{ height: \"24px\" }} /> <A_62 {...{ label: \"Website\", preInputChildren: <InputPrefix>https://</InputPrefix>, inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.website, onChange: (website) => State.update({ website: website.trim() }), validate: () => {}, error: state.websiteError }} /> <Space style={{ height: \"24px\" }} /> <Button {...{ type: \"primary\", prefix: \"https://\", text: props.edit ? state.isDao ? \"Add proposal to update project\" : \"Update your project\" : state.isDao ? \"Add proposal to create project\" : \"Create new project\", disabled: isCreateProjectDisabled, onClick: handleCreateOrUpdateProject }} /> <Space style={{ height: \"24px\" }} /> </FormSectionRightDiv> </FormSectionContainer> </FormBody> {state.isMultiAccountModalOpen && <ModalMultiAccount {...{ onClose: () => State.update({ isMultiAccountModalOpen: false }), titleText: \"Add team members\", descriptionText: \"Add NEAR account IDs for your team members.\", inputValue: state.teamMember, onInputChange: (teamMember) => { State.update({ teamMember, nearAccountIdError: \"\" }); }, handleAddAccount: handleAddTeamMember, handleRemoveAccount: (accountId) => { State.update({ teamMembers: state.teamMembers.filter((tm) => tm != accountId) }); }, accountError: state.nearAccountIdError, accountIds: state.teamMembers, unitText: \"member\" }} />} {state.fundingSourceIndex !== null && <Widget loading=\" \" code={props.alem.componentsCode.ModalAddFundingSource} props={{ ...{ ...{ onClose: () => {const updatedFundingSources = state.fundingSources.filter((fs) => fs.investorName && fs.amountReceived && fs.denomination && fs.description);State.update({ fundingSources: updatedFundingSources, fundingSourceIndex: null });}, fundingSources: state.fundingSources, fundingSourceIndex: state.fundingSourceIndex, handleAddFundingSource: ({ investorName, date, description, amountReceived, denomination }) => {const updatedFundingSources = state.fundingSources.map((fs, i) => {if (i == state.fundingSourceIndex) {return { investorName, date, description, amountReceived, denomination };}return fs;});State.update({ fundingSources: updatedFundingSources, fundingSourceIndex: null });} }, ...props } }} />} </>} </A_13>; `, CheckoutBreakdown: ` const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useCart = () => useContext(\"cart-context\"); const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const A_18 = styled.div\\` display: flex; flex-direction: column; gap: 24px; margin-top: 20px; width: 380px; /* background: white; */ @media screen and (max-width: 768px) { width: 100%; margin-bottom: 50px; }\\`;const Title = styled.div\\` color: #2e2e2e; font-size: 24px; font-weight: 600; line-height: 32px; word-wrap: break-word;\\`;const CurrencyHeader = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px; border-radius: 5px; background: #f0f0f0;\\`;const CurrencyHeaderText = styled.div\\` color: #7b7b7b; font-size: 12px; font-weight: 400; line-height: 14px; word-wrap: break-word;\\`;const BreakdownItemContainer = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px;\\`;const BreakdownItemLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; width: 50%; gap: 8px;\\`;const BreakdownItemRight = styled.div\\` display: flex; flex: 1; flex-direction: row; align-items: center; justify-content: flex-end;\\`;const BreakdownItemText = styled.div\\` color: #2e2e2e; font-size: 14px; font-weight: 400; line-height: 16px; word-wrap: break-word;\\`;const CurrencyIcon = styled.img\\` width: 20px; height: 20px;\\`;const TotalContainer = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px; border-top: 1px #7b7b7b solid;\\`;const TotalText = styled.div\\` color: #2e2e2e; font-size: 14px; font-weight: 600; line-height: 20px; word-wrap: break-word;\\`;const ErrorText = styled.div\\` color: #dd3345; font-size: 14px; font-weight: 400; line-height: 20px; word-wrap: break-word; width: 100%; text-align: center;\\`; const props = props; const { cart, clearCart } = useCart(); const { SUPPORTED_FTS } = constants; const DONATION_CONTRACT_ID = DonateSDK.getContractId(); Big.PE = 100; const MIN_REQUIRED_DONATION_AMOUNT_PER_PROJECT = 0.1; const [tokens, amountsByFt, totalAmount, donationTooSmall] = useMemo(() => { const tokens = {}; const amountsByFt = {}; let donationTooSmall = false; Object.entries(cart || {}).forEach(([projectId, { token, amount }]) => { const ft = token.text; if (!amountsByFt[ft]) amountsByFt[ft] = 0; amountsByFt[ft] += parseFloat(amount || 0); if (amountsByFt[ft] < MIN_REQUIRED_DONATION_AMOUNT_PER_PROJECT) donationTooSmall = true; tokens[ft] = token; }); const totalAmount = Object.values(amountsByFt).reduce((acc, amount) => acc + amount, 0); return [tokens, amountsByFt, totalAmount, donationTooSmall]; }, [props]); const handleDonate = () => { const transactions = []; let potIdContained; Object.entries(cart).forEach(([projectId, { token, amount, referrerId, note, potId }]) => { const isFtDonation = token.text != \"NEAR\"; const amountIndivisible = Big(parseFloat(amount)).mul(Big(10).pow(isFtDonation ? token.decimals : 24)); const args = {}; if (isFtDonation) { args.receiver_id = DONATION_CONTRACT_ID; args.amount = amountIndivisible.toString(); args.memo = JSON.stringify({ recipient_id: projectId, referrer_id: referrerId || null, bypass_protocol_fee: false, message: note || null }); } else { if (potId) args.project_id = projectId;else args.recipient_id = projectId; args.referrer_id = referrerId; args.message = note; potIdContained = potId; } transactions.push({ contractName: isFtDonation ? token.id : potId ?? DONATION_CONTRACT_ID, methodName: isFtDonation ? \"ft_transfer_call\" : \"donate\", args, deposit: isFtDonation ? \"1\" : amountIndivisible.toString(), gas: \"300000000000000\" }); }); if (Object.keys(amountsByFt).some(ft => ft !== \"NEAR\")) { const requiredDepositFloat = transactions.reduce((acc, { methodName, args }) => { if (methodName === \"donate\") return acc; const baseAmount = 0.008; const argsAmount = (args.message.length || 0) * 0.0001; return acc + baseAmount + argsAmount; }, 0); transactions.unshift({ contractName: DONATION_CONTRACT_ID, methodName: \"storage_deposit\", args: {}, deposit: Big(requiredDepositFloat).mul(Big(10).pow(24)), gas: \"100000000000000\" }); } const now = Date.now(); Near.call(transactions); const pollIntervalMs = 1000; const pollId = setInterval(() => { (potIdContained ? PotSDK.asyncGetDonationsForDonor(potIdContained, context.accountId) : DonateSDK.asyncGetDonationsForDonor(context.accountId)).then(donations => { const foundDonations = []; for (const donation of donations) { const { recipient_id, project_id, donated_at_ms, donated_at, total_amount } = donation; const matchingCartItem = cart[project_id || recipient_id]; if (matchingCartItem && (donated_at_ms > now || donated_at > now)) { foundDonations.push(donation); } } if (foundDonations.length) { clearInterval(pollId); props.updateSuccessfulDonationRecipientId(foundDonations[0].recipient_id); clearCart(); } }); }, pollIntervalMs); }; return <A_18> <Title>Breakdown summary</Title> <CurrencyHeader> <CurrencyHeaderText>Currency</CurrencyHeaderText> <CurrencyHeaderText>Amount</CurrencyHeaderText> </CurrencyHeader> {Object.entries(amountsByFt).map(([ft, amount]) => { const amountFloat = parseFloat(amount || 0); return <BreakdownItemContainer> <BreakdownItemLeft> {ft == \"NEAR\" ? <CurrencyIcon src={SUPPORTED_FTS.NEAR.iconUrl} /> : <CurrencyIcon src={tokens[ft].icon} />} <BreakdownItemText>{tokens[ft].text}</BreakdownItemText> </BreakdownItemLeft> <BreakdownItemRight> <BreakdownItemText>{amountFloat.toFixed(2)}</BreakdownItemText> </BreakdownItemRight> </BreakdownItemContainer>; })} {Object.keys(amountsByFt).length <= 1 && amountsByFt.NEAR && <TotalContainer> <TotalText>Total</TotalText> <TotalText>{totalAmount.toFixed(2)}</TotalText> </TotalContainer>} <Button {...{ type: \"primary\", text: \\`Process Donation\\`, disabled: !Object.keys(cart).length || donationTooSmall || !context.accountId, onClick: handleDonate, style: { width: \"100%\" } }} /> {donationTooSmall && <ErrorText>Minimum required donation per project is {MIN_REQUIRED_DONATION_AMOUNT_PER_PROJECT} N</ErrorText>} {!context.accountId && <ErrorText>Please sign in to donate</ErrorText>} </A_18>; `, CheckoutItem: ` function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_233 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_234 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_235 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 10px; background: #ffffff; border: 1px solid #d0d5dd; /* box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); */ box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset; border-radius: 4px; color: #101828; width: 100%;\\`;const Placeholder = styled.span\\` color: #a0a3a8;\\`;const scaleOut = styled.keyframes\\` from { transform: scaleY(0); } to { transform: scaleY(1); }\\`;const SelectContent = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; gap: 0.5em; width: 100%; border: 1px solid #d0d5dd; border-radius: 4px; background: #ffffff; z-index: 3 !important;\\`;const Viewport = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; width: 100%;\\`;const Item = styled.button\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 0.5em; width: 100%; cursor: pointer; background: transparent; border: none; transition: background 0.2s ease-in-out; &:nth-child(n + 1) { border-top: 1px solid #d0d5dd; } &:hover { background: #d0d5dd; boder: none; } &:focus { outline: none; }\\`; const Select = (componentProps) => { const label = componentProps.label ?? \"Label\"; const noLabel = componentProps.noLabel ?? false; const placeholder = componentProps.placeholder ?? \"Select an option\"; const value = componentProps.value ?? \"\"; const options = componentProps.options ?? []; const onChange = componentProps.onChange ?? (() => {}); const validate = componentProps.validate ?? (() => {}); const error = componentProps.error ?? \"\"; return <A_233 style={componentProps.containerStyles || {}}> {noLabel ? <></> : <A_234>{label}</A_234>} <Select.Root value={value?.value} onValueChange={(value) => onChange(options.find((option) => option.value === value))}> <Select.Trigger asChild={true}> <A_235 style={componentProps.inputStyles || {}}> {componentProps.iconLeft && componentProps.iconLeft} <Select.Value aria-label={value.value} placeholder={<Placeholder>{placeholder}</Placeholder>} /> {} <Select.Icon> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1 1.5L6 6.5L11 1.5\" stroke=\"currentColor\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg> </Select.Icon> {} </A_235> </Select.Trigger> <Select.Content asChild={true}> <SelectContent> <Select.Viewport asChild={true}> <Viewport> {options.map(({ text, value }) => <Select.Item value={value} asChild={true}> <Item> <Select.ItemText>{text}</Select.ItemText> <Select.ItemIndicator> <svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" /> </svg> </Select.ItemIndicator> </Item> </Select.Item>)} </Viewport> </Select.Viewport> </SelectContent> </Select.Content> </Select.Root> </A_233>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const TagContainer = styled.div\\` display: flex; justify-content: center; align-items: center; text-align: center; padding: 6px 8px; border-radius: 4px;\\`;const TagText = styled.span\\` font-size: 14px;\\`; const Tag = __props__ => { const { backgroundColor, borderColor, textColor, text } = __props__; const textStyle = __props__.textStyle || {}; return <TagContainer style={{ backgroundColor: backgroundColor || \"#ffffff\", border: \\`1px solid \\${borderColor || \"#000000\"}\\`, boxShadow: \\`0px -0.699999988079071px 0px \\${borderColor} inset\\` }}> {__props__.preElements} <TagText style={{ color: textColor || \"#000000\", ...textStyle }}> {text} </TagText> </TagContainer>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_237 = (__props__) => <svg {...__props__} style={{ width: 16}} viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg>; const ItemContainer = styled.div\\` display: flex; flex-direction: row; max-width: 800px; background: white; /* background: pink; */ border: 1px solid #dbdbdb; box-shadow: 0px -2px 0px #dbdbdb inset; border-radius: 6px; justify-content: flex-start; align-items: flex-start;\\`;const ItemLeft = styled.div\\` height: 100%; padding: 24px 16px; /* background: green; */\\`;const ItemRight = styled.div\\` display: flex; flex-direction: row; padding: 24px 24px 24px 16px; width: 100%; /* background: yellow; */ border-left: 1px solid #dbdbdb;\\`;const ImageContainer = styled.div\\` display: flex; flex-direction: row; gap: 32px;\\`;const DetailsContainer = styled.div\\` display: flex; flex-direction: column; width: 100%; overflow: hidden;\\`;const A_19 = styled.a\\` color: #2e2e2e; font-size: 16px; line-height: 24px; font-weight: 600; word-wrap: break-word;\\`;const A_20 = styled.div\\` color: #2e2e2e; font-size: 16px; line-height: 24px; font-weight: 400; word-wrap: break-word; overflow-wrap: break-word; margin: 16px 0px 24px 0px;\\`;const FtIcon = styled.img\\` width: 20px; height: 20px;\\`;const A_21 = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center;\\`;const A_22 = styled.svg\\` width: 20px; height: 20px;\\`; const props = props; const { cartItem, checked, handleCheckboxClick } = props; const { referrerId } = props.alem.useParams(); const projectId = cartItem?.id; const isPotDonation = cartItem?.potId; const profile = props.profile || Social.get(\\`\\${projectId}/profile/**\\`, \"final\") || {}; const [itemAmount, setItemAmount] = useState(cartItem?.amount); const [itemToken, setItemToken] = useState(cartItem?.token); const [potDetail, setPotDetail] = useState(null); State.init({ ftBalances: null, denominationOptions: [{ text: \"NEAR\", value: \"NEAR\", selected: itemToken.text === \"NEAR\", decimals: 24 }] }); if (!potDetail && cartItem.potId) { if (cartItem.potDetail) { setPotDetail(cartItem.potDetail); } else { PotSDK.asyncGetConfig(cartItem.potId).then((potDetail) => { setPotDetail(potDetail); }).catch((err) => console.log(\"error while fetching pot detail for cart Item \", err)); } } return <ItemContainer> <ItemLeft> <CheckBox {...{ id: \"selector-\" + projectId, checked, onClick: handleCheckboxClick }} /> </ItemLeft> <ItemRight> <ImageContainer> <ProfileImage {...{ accountId: projectId, style: { width: \"40px\", height: \"40px\", border: \"none\", marginRight: \"24px\" }, className: \"mb-2\", imageClassName: \"rounded-circle w-100 h-100 d-block\", thumbnail: false }} /> </ImageContainer> <DetailsContainer> <A_21> <A_19 href={hrefWithParams(\\`?tab=project&projectId=\\${projectId}\\`)}>{profile.name ?? \"\"}</A_19> <Tag {...{ backgroundColor: isPotDonation ? \"#FEF6EE\" : \"#F6F5F3\", borderColor: isPotDonation ? \"rgba(219, 82, 27, 0.36)\" : \"#DBDBDB\", textColor: isPotDonation ? \"#EA6A25\" : \"#292929\", text: isPotDonation ? potDetail?.pot_name ?? cartItem.potId.slice(0, 20) : \"Direct Donation\" }} /> </A_21> <A_20>{profile.description ?? \"\"}</A_20> <A_62 {...{ label: \"Amount\", placeholder: \"0\", value: itemAmount, onChange: (amount) => { amount = amount.replace(/[^\\\\d.]/g, \"\"); if (amount === \".\") amount = \"0.\"; setItemAmount(amount); }, onBlur: (e) => {}, inputStyles: { textAlign: \"right\", borderRadius: \"0px 4px 4px 0px\" }, preInputChildren: <Select {...{ noLabel: true, placeholder: \"\", options: state.denominationOptions, value: { text: itemToken.text, value: itemToken.value }, onChange: ({ text, value }) => { const token = state.denominationOptions.find((option) => option.text === text); setItemToken(token); setItemAmount(undefined); }, containerStyles: { width: \"auto\" }, inputStyles: { border: \"none\", borderRight: \"1px #F0F0F0 solid\", borderRadius: \"4px 0px 0px 4px\", width: \"auto\", padding: \"12px 16px\", boxShadow: \"0px -2px 0px rgba(93, 93, 93, 0.24) inset\" }, iconLeft: itemToken.text == \"NEAR\" ? <A_237 /> : <FtIcon src={itemToken.icon} /> }} /> }} /> <Widget loading=\" \" code={props.alem.componentsCode.BreakdownSummary} props={{ ...{ ...{ ftIcon: itemToken.icon, referrerId, totalAmount: itemAmount, bypassProtocolFee: false, containerStyle: { marginTop: \"16px\" } }, ...props } }} /> </DetailsContainer> </ItemRight> </ItemContainer>; `, Cart: ` const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useCart = () => useContext(\"cart-context\"); const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_23 = styled.div\\` background: #fafafa; display: flex; flex-direction: row; height: 100%; min-height: 100vh; @media screen and (max-width: 768px) { flex-direction: column; }\\`;const SuccessContainer = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; padding: 24px; gap: 24px;\\`;const ColumnLeft = styled.div\\` display: flex; flex-direction: column; width: 55%; padding: 48px 40px 48px 64px; gap: 48px; @media screen and (max-width: 768px) { width: 100%; padding: 24px 16px 24px 16px; }\\`;const ColumnRight = styled.div\\` flex: 1; padding: 152px 148px 152px 84px; border-left: 1px #c7c7c7 solid; @media screen and (max-width: 768px) { padding: 24px 16px 24px 16px; border-left: none; border-top: 1px #c7c7c7 solid; }\\`;const A_24 = styled.div\\` color: #2e2e2e; font-size: 48px; font-family: Lora; font-weight: 500; line-height: 56px; word-wrap: break-word; text-align: center;\\`;const A_25 = styled.svg\\` width: 1rem; height: 1rem; path { transition: 300ms; }\\`;const ActionsContainer = styled.div\\` width: 100%; padding: 16px; background: white; border: 1px solid #dbdbdb; box-shadow: 0px -2px 0px #dbdbdb inset; border-radius: 6px; overflow: hidden; justify-content: flex-start; align-items: center; display: inline-flex; gap: 24px;\\`;const InnerContainer = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 6px; :hover path { fill: #dd3345; }\\`;const SubTitle = styled.div\\` color: #2e2e2e; font-weight: 600; font-size: 14px;\\`;const TxLink = styled.a\\` color: #2e2e2e; cursor: pointer; &:hover { text-decoration: none; }\\`; const { cart, removeItemsFromCart } = useCart(); const numCartItems = Object.keys(cart).length; const { transactionHashes: passedTransactionHashes } = props.alem.useParams(); const DEFAULT_GATEWAY = \"https://bos.potlock.org/\"; const POTLOCK_TWITTER_ACCOUNT_ID = \"PotLock_\"; const DEFAULT_SHARE_HASHTAGS = [\"BOS\", \"PublicGoods\", \"Donations\"]; State.init({ selectedProjectIds: [], masterSelectorSelected: false, successfulDonationRecipientId: null, successfulDonationsRecipientProfiles: null }); const allSelected = state.selectedProjectIds.length !== 0 && state.selectedProjectIds.length === numCartItems; if (passedTransactionHashes && !state.successfulDonationsRecipientProfiles) { const transactionHashes = passedTransactionHashes.split(\",\"); for (let i = 0; i < transactionHashes.length; i++) { const txHash = transactionHashes[i]; const body = JSON.stringify({ jsonrpc: \"2.0\", id: \"dontcare\", method: \"tx\", params: [txHash, context.accountId] }); const res = fetch(\"https://rpc.mainnet.near.org\", { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body }); if (res.ok) { const methodName = res.body.result.transaction.actions[0].FunctionCall.method_name; const successVal = res.body.result.status?.SuccessValue; const result = JSON.parse(Buffer.from(successVal, \"base64\").toString(\"utf-8\")); const args = JSON.parse(Buffer.from(res.body.result.transaction.actions[0].FunctionCall.args, \"base64\").toString(\"utf-8\")); const recipientId = methodName === \"donate\" ? result.recipient_id : methodName === \"ft_transfer_call\" ? JSON.parse(args.msg).recipient_id : \"\"; if (recipientId) { Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${recipientId}/profile/**\\`] }).then((socialData) => { State.update({ successfulDonationsRecipientProfiles: { ...state.successfulDonationsRecipientProfiles, [recipientId]: socialData[recipientId][\"profile\"] } }); }); } } } } useEffect(() => { if (state.successfulDonationRecipientId && !state.successfulDonationsRecipientProfiles) { Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${state.successfulDonationRecipientId}/profile/**\\`] }).then((socialData) => { State.update({ successfulDonationsRecipientProfiles: { [state.successfulDonationRecipientId]: socialData[state.successfulDonationRecipientId][\"profile\"] } }); }); } }, [state.successfulDonationRecipientId, state.successfulDonationsRecipientProfiles]); const twitterIntent = useMemo(() => { if (!state.successfulDonationsRecipientProfiles) return; const recipientIds = Object.keys(state.successfulDonationsRecipientProfiles); const twitterIntentBase = \"https://twitter.com/intent/tweet?text=\"; let url = DEFAULT_GATEWAY + \\`potlock.near/widget/Index?referrerId=\\${context.accountId}\\`; if (recipientIds.length === 1) { url = url + \\`&tab=project&projectId=\\${recipientIds[0]}\\`; } else { url = url + \\`&tab=projects\\`; } const recipientProfiles = []; for (const [recipientId, profile] of Object.entries(state.successfulDonationsRecipientProfiles)) { const identifier = profile.linktree?.twitter ? \\`@\\${profile.linktree.twitter}\\` : profile.name ? profile.name : recipientId; recipientProfiles.push({ identifier, hasTwitter: !!profile.linktree?.twitter }); } recipientProfiles.sort((a, b) => { if (a.hasTwitter && !b.hasTwitter) return -1; if (!a.hasTwitter && b.hasTwitter) return 1; return 0; }); const sortedIdentifiers = recipientProfiles.map((profile) => profile.identifier); const recipientsText = sortedIdentifiers.join(\" & \"); let text = \\`I just donated to \\${recipientsText} on @\\${POTLOCK_TWITTER_ACCOUNT_ID}! Support public goods at \\`; text = encodeURIComponent(text); url = encodeURIComponent(url); return twitterIntentBase + text + \\`&url=\\${url}\\` + \\`&hashtags=\\${DEFAULT_SHARE_HASHTAGS.join(\",\")}\\`; }, [state.successfulDonationsRecipientProfiles]); return <A_23> {passedTransactionHashes || state.successfulDonationRecipientId ? <SuccessContainer> <A_24>Thanks for donating!</A_24> {twitterIntent && <Button {...{ href: twitterIntent, target: \"_blank\", type: \"primary\", text: \"Share to Twitter\", disabled: !twitterIntent, style: { width: \"300px\" } }} />} <Button {...{ href: hrefWithParams(\\`?tab=projects\\`), type: twitterIntent ? \"secondary\" : \"primary\", text: \"Explore projects\", style: { width: \"300px\" } }} /> </SuccessContainer> : <> <ColumnLeft> <A_24>Donation Cart</A_24> <ActionsContainer> <InnerContainer> <CheckBox {...{ id: \"masterSelector\", disabled: numCartItems === 0, checked: state.masterSelectorSelected, onClick: (e) => { const selectedProjectIds = Object.keys(cart).filter((_) => { if (allSelected) { return false; } return true; }); State.update({ selectedProjectIds, masterSelectorSelected: !allSelected }); } }} /> <SubTitle>Select all</SubTitle> </InnerContainer> <InnerContainer style={{ cursor: \"pointer\" }} onClick={() => { if (state.selectedProjectIds.length === 0) return; removeItemsFromCart(state.selectedProjectIds); State.update({ selectedProjectIds: [], masterSelectorSelected: false }); }}> <A_25 viewBox=\"0 0 12 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M2.5 14C2.0875 14 1.73437 13.8531 1.44062 13.5594C1.14687 13.2656 1 12.9125 1 12.5V2.5H0V1H4V0H8V1H12V2.5H11V12.491C11 12.9137 10.8531 13.2708 10.5594 13.5625C10.2656 13.8542 9.9125 14 9.5 14H2.5ZM9.5 2.5H2.5V12.5H9.5V2.5ZM4 11H5.5V4H4V11ZM6.5 11H8V4H6.5V11Z\" fill=\"#7B7B7B\" /> </A_25> <SubTitle>Delete</SubTitle> </InnerContainer> </ActionsContainer> {numCartItems === 0 ? <div>No items in cart</div> : Object.keys(cart).map((projectId) => { const checked = state.selectedProjectIds.includes(projectId); return <Widget loading=\" \" code={props.alem.componentsCode.CheckoutItem} props={{ ...{ ...{ cartItem: cart[projectId], checked, handleCheckboxClick: (e) => {let selectedProjectIds = state.selectedProjectIds;if (checked) {selectedProjectIds = selectedProjectIds.filter((id) => id !== projectId);} else {selectedProjectIds.push(projectId);}const updatedState = { selectedProjectIds };if (selectedProjectIds.length !== 0 && selectedProjectIds.length !== numCartItems) {updatedState.masterSelectorSelected = false;}State.update(updatedState);} }, ...props } }} />; })} </ColumnLeft> <ColumnRight> <Widget loading=\" \" code={props.alem.componentsCode.CheckoutBreakdown} props={{ ...{ ...{ updateSuccessfulDonationRecipientId: (recipientId) => State.update({ successfulDonationRecipientId: recipientId }) }, ...props } }} /> </ColumnRight> </>} </A_23>; `, DonorsLeaderboard: ` const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_27 = styled.div\\` width: 100%; display: flex; flex-direction: column; align-items: center; gap: 2rem; .transcation { display: flex; flex-direction: column; width: 100%; font-size: 14px; .header { display: flex; align-items: center; justify-content: space-between; padding: 10px; gap: 1rem; background: #f6f5f3; color: #292929; div { width: 130px; display: flex; justify-content: center; align-items: center; font-weight: 600; } } .address { width: 190px !important; margin-right: auto; justify-content: start !important; } .rank { width: 80px !important; } } @media only screen and (max-width: 768px) { .transcation { font-size: 12px; .header { padding: 10px 0; div { width: 80px; } } } } @media only screen and (max-width: 480px) { .transcation { font-size: 9px; .address { width: 120px !important; } } }\\`;const TrRow = styled.div\\` display: flex; align-items: center; justify-content: space-between; width: 100%; gap: 1rem; padding: 20px 10px; > div, > span { width: 130px; display: flex; justify-content: center; align-items: center; } .price { display: flex; gap: 1rem; align-items: center; img { width: 1.5rem; } } .address { color: #292929; display: flex; align-items: center; justify-content: flex-start; border-radius: 2px; transition: all 200ms; .profile-image { width: 2rem; height: 2rem; margin-right: 1rem; } } @media only screen and (max-width: 768px) { padding: 10px 0; > div, > span { width: 80px; } .price { gap: 8px; img { width: 1.25rem; } } .address .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0.5rem; } } @media only screen and (max-width: 480px) { .price img { width: 1rem; } }\\`;const NoResult = styled.div\\` font-size: 2rem; text-align: center;\\`; const propsLeaderboard = props; const { sponsors, sortedDonations, filter, currentTab } = propsLeaderboard; const { tab } = props.alem.useParams(); const donations = currentTab === \"sponsors\" ? sponsors : sortedDonations; const isInPot = tab === \"pot\"; const [currentPage, setCurrentPage] = useState(1); const perPage = 30; useEffect(() => { setCurrentPage(1); }, [filter]); const nearLogo = \"https://ipfs.near.social/ipfs/bafkreicdcpxua47eddhzjplmrs23mdjt63czowfsa2jnw4krkt532pa2ha\"; return donations.length ? <A_27> <div className=\"transcation\"> <div className=\"header\"> <div className=\"rank\">Rank</div> <div className=\"address\">Donor</div> <div>Amount</div> {isInPot && <div>Percentage</div>} {A_236 && !isInPot && <div>Amount (USD)</div>} </div> {donations.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation, idx) => { const { donor_id, amount, percentage_share } = donation; return <TrRow> <div className=\"rank\">#{idx + 1 + (currentPage - 1) * perPage}</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`)} className=\"address\" target=\"_blank\"> <ProfileImage style={{}} className=\"profile-image\" accountId={donor_id} /> {_address(donor_id, 15)} </a> <div className=\"price\"> <img src={nearLogo} alt=\"NEAR\" /> {amount.toFixed(2).replace(/[.,]00$/, \"\")} </div> {isInPot && <div>{percentage_share}%</div>} {A_236 && !isInPot && <div>~\\${(amount * A_236).toFixed(2)}</div>} </TrRow>; })} </div> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: donations, currentPage, perPage: perPage, bgColor: \"#292929\" }, ...props } }} /> </A_27> : <NoResult>No Donations</NoResult>; `, DonorsTrx: ` const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_28 = styled.div\\` display: flex; flex-direction: column; align-items: center; gap: 2rem; width: 100%; .transcation { display: flex; flex-direction: column; width: 100%; font-size: 14px; .header { display: flex; align-items: center; justify-content: space-between; padding: 10px; gap: 1rem; background: #f6f5f3; color: #292929; div { width: 100px; display: flex; justify-content: center; align-items: center; font-weight: 600; } } .address { width: 143px !important; } } @media only screen and (max-width: 768px) { .transcation { font-size: 12px; .header { padding: 10px 0; div { width: 80px !important; } } .address { width: 80px !important; justify-content: center; .profile-image { display: none !important; } } } } @media only screen and (max-width: 480px) { .transcation { font-size: 9px; } }\\`;const A_29 = styled.div\\` display: flex; width: 100%; justify-content: space-between; gap: 1rem; padding: 20px 10px; > div, > span { width: 100px; display: flex; justify-content: center; align-items: center; } .price { display: flex; gap: 1rem; align-items: center; img { width: 1.5rem; } } .address { color: #292929; display: flex; align-items: center; justify-content: flex-start; border-radius: 2px; transition: all 200ms; .profile-image { width: 2rem; height: 2rem; margin-right: 1rem; } } @media only screen and (max-width: 768px) { padding: 10px 0; > div, > span { width: 80px; } .price { gap: 8px; img { width: 1.25rem; } } .address .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0.5rem; } } @media only screen and (max-width: 480px) { .price img { width: 1rem; } }\\`;const A_30 = styled.div\\` font-size: 2rem; text-align: center;\\`; const props = props; const { allDonations: donations, filter } = props; const allDonations = [...donations].reverse(); const [currentPage, setCurrentPage] = useState(1); const perPage = 30; const nearLogo = \"https://ipfs.near.social/ipfs/bafkreicdcpxua47eddhzjplmrs23mdjt63czowfsa2jnw4krkt532pa2ha\"; useEffect(() => { setCurrentPage(1); }, [filter]); const NEAR_DECEMIALS = 24; const calcNetDonationAmount = (amount, decimals) => Big(amount).div(Big(\\`1e\\${decimals}\\`)); return allDonations.length ? <A_28> <div className=\"transcation\"> <div className=\"header\"> <div className=\"address\">Donor</div> <div className=\"address\">Project</div> <div>Amount</div> <div>Date</div> </div> {allDonations.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation) => { const { donor_id, recipient_id, donated_at_ms, donated_at, project_id, ft_id, total_amount } = donation; const projectId = recipient_id || project_id; const isNear = ft_id === \"near\"; const frMetaDate = !isNear ? Near.view(ft_id, \"ft_metadata\", {}) : null; const assetIcon = isNear ? nearLogo : frMetaDate.icon; const decimals = isNear ? NEAR_DECEMIALS : frMetaDate.decimals; return <A_29> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`)} className=\"address\" target=\"_blank\"> <ProfileImage style={{}} accountId={donor_id} /> {_address(donor_id)} </a> <a href={hrefWithParams(\\`?tab=project&projectId=\\${projectId}\\`)} className=\"address\" target=\"_blank\"> <ProfileImage style={{}} accountId={projectId} /> {_address(projectId)} </a> <div className=\"price\"> <img src={assetIcon} alt={ft_id} /> {decimals ? calcNetDonationAmount(total_amount, decimals).toFixed(2) : \"-\"} </div> <div>{getTimePassed(donated_at_ms || donated_at)} ago</div> </A_29>; })} </div> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: allDonations, currentPage, perPage: perPage, bgColor: \"#292929\" }, ...props } }} /> </A_28> : <A_30>No Donations</A_30>; `, Leaderboard: ` const A_26 = styled.div\\` display: flex; flex-direction: column; .leaderboard { width: 100%; h1 { font-size: 2.5rem; font-weight: 600; margin-top: 20px; } .cards { display: flex; gap: 3rem; margin-top: 2rem; margin-bottom: 5rem; > div { width: 30%; display: flex; } .top { width: 40%; scale: 1.05; } @media only screen and (max-width: 670px) { flex-direction: column; justify-content: center; > div { width: 100%; display: flex; } .top { order: -1; scale: 1; width: 100%; } } } } .filter-menu { left: 0; right: auto; }\\`;const Tabs = styled.div\\` display: flex; justify-content: space-between; flex-wrap: wrap; align-items: center; gap: 2rem; font-size: 14px; margin-bottom: 24px; .menu-item { font-weight: 600; display: flex; width: 100%; justify-content: space-between; gap: 20px; } .selected { gap: 10px; .label { text-transform: uppercase; color: #7b7b7b; } .count { color: #dd3345; } } .select { width: fit-content; }\\`;const LoadingWrapper = styled.div\\` font-size: 1.5rem; margin-top: 1rem;\\`;const Filter = styled.div\\` display: flex; flex-wrap: wrap; gap: 8px; .option { padding: 0.8em 1em; border-radius: 8px; color: #292929; box-shadow: 0px -1px 0px 0px #dbdbdb inset, 0px 0px 0px 0.5px #dbdbdb; transition: all 300ms ease-in-out; cursor: pointer; &.active, :hover { background: #292929; color: white; } } @media only screen and (max-width: 480px) { font-size: 10px; }\\`; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsdWithFallback = (amountNear, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas((amountNear * A_236).toFixed(2)) : formatWithCommas(amountNear.toString()) + (abbreviate ? \"N\" : \" NEAR\");}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_31 = styled.div\\` display: flex; flex-direction: column; align-items: center; box-shadow: 0px 2px 4px #00000081; width: 100%; position: relative; padding-bottom: 1rem; font-size: 14px; .name { font-weight: bold; color: var(--primary-color); } .description { color: #b3b3b3; } .tag { position: absolute; right: 4px; top: 4px; background: white; border-radius: 2px; width: 2rem; height: 2rem; display: flex; align-items: center; justify-content: center; img { width: 18px; height: auto; } } .background { height: 100px; width: 100%; } .profile { position: relative; transform: translateY(-50%); width: 4rem; height: 4rem; border-radius: 50%; } .amount { margin-top: 1rem; border: 1px solid #b3b3b3; padding: 4px; border-radius: 4px; }\\`; const DonorsCards = (__props__) => { const { sponsors, sortedDonations, currentTab } = __props__; const donations = currentTab === \"sponsors\" ? sponsors : sortedDonations; const Card = ({ donor }) => { const { id, rank, className, amount } = donor; const profile = Social.getr(\\`\\${id}/profile\\`); return <div className={className || \"\"}> <A_31> {profile === null ? <div className=\"spinner-border text-secondary\" role=\"status\" /> : <> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: profile.backgroundImage, className: \"background\", alt: profile.name, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreidla73cknxbeovrhgb2blax2j2qgcgcn6ibluzza3buq2mbkoqs2e\" }, ...props } }} /> <div className=\"tag\">{rank}</div> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: profile.image, className: \"profile\", alt: profile.name, style: {}, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\" }, ...props } }} /> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${id}\\`)} className=\"name\" target=\"_blank\"> {_address(profile.name ? profile.name : id)} </a> <div className=\"description\">{profile.description ? _address(profile.description, 20) : \"-\"}</div> <div className=\"amount\">{nearToUsdWithFallback(amount)} Donated</div> </>} </A_31> </div>; }; const leaderboard = [{ rank: \"#2\", id: donations[1].donor_id, amount: donations[1].amount }, { rank: <img src=\"https://ipfs.near.social/ipfs/bafkreicjk6oy6465ps32owoomppfkvimbjlnhbaldvf6ujuyhkjas6ghjq\" alt=\"top\" />, id: donations[0].donor_id, className: \"top\", amount: donations[0].amount }, { rank: \"#3\", id: donations[2].donor_id, amount: donations[2].amount }]; return <div className=\"cards\">{leaderboard.map((donor) => donor.id ? <Card donor={donor} /> : \"\")}</div>; }; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const filterByDate = (filter, donation) => { const currentTimestamp = new Date().getTime(); const oneDayTime = 24 * 60 * 60 * 1000; const donateAt = donation.donated_at_ms || donation.donated_at; const yesterday = currentTimestamp - oneDayTime; const lastWeek = currentTimestamp - oneDayTime * 7; const lastMonth = currentTimestamp - oneDayTime * 30; const lastYear = currentTimestamp - oneDayTime * 365; switch (filter) { case \"day\": if (donateAt > yesterday) return true; return false; case \"week\": if (donateAt > lastWeek) return true; return false; case \"month\": if (donateAt > lastMonth) return true; return false; case \"year\": if (donateAt > lastYear) return true; return false; case \"all\": return true; default: return true; }}; const A_103 = (donation) => { const lastDonationAmount = Big(donation.total_amount - (donation.referrer_fee || 0) - (donation.protocol_fee || 0)).div(Big(1e24)); return parseFloat(lastDonationAmount);}; const Loading = () => <LoadingWrapper>Loading...</LoadingWrapper>; const [currentTab, setTab] = useState(\"leaderboard\"); const [title, setTitle] = useState(\"\"); const [filter, setFilter] = useState(\"\"); const [allDonationsFetched, setAllDonationsFetched] = useState(false); const [fetchDonationsError, setFetchDonationsError] = useState(\"\"); const direct_donations_count = useMemo(() => { const data = DonateSDK.getConfig(); return data.total_donations_count; }, []); const getSponsorshipDonations = (potId) => PotSDK.asyncGetMatchingPoolDonations(potId); const allSponsors = useCache(() => { return PotFactorySDK.asyncGetPots().then((pots) => { if (pots) { const sponsors = pots.map(({ id }) => getSponsorshipDonations(id)); return Promise.all(sponsors).then((allSponsors) => { const sumUpSponsors = allSponsors.flat().reduce((accumulator, currentDonation) => { accumulator[currentDonation.donor_id] = { amount: (accumulator[currentDonation.donor_id].amount || 0) + A_103(currentDonation), ...currentDonation }; return accumulator; }, {}); return Object.values(sumUpSponsors); }); } else return []; }).catch((err) => { console.log(\"error fetching pots \", err); return []; }); }, \"sponsors-funding\"); const sponsors = useMemo(() => { if (allSponsors) { let sponsors = allSponsors.filter((donation) => filterByDate(filter, donation)); sponsors = allSponsors.sort((a, b) => b.amount - a.amount); return sponsors; } }, [allSponsors, filter]); const allDonationsPaginated = useCache(() => { const limit = 900; const paginations = [...Array(Math.ceil(direct_donations_count / limit)).keys()]; try { const allDonations = paginations.map((index) => DonateSDK.asyncGetDonations(limit * index, limit)); return Promise.all(allDonations); } catch (error) { console.error(\\`error getting direct donations\\`, error); setFetchDonationsError(error); return Promise.all([]); } }, \"direct-donations\"); const [allDonations, sortedDonations] = useMemo(() => { if (allDonationsPaginated) { let donations = allDonationsPaginated.flat(); donations = donations.filter((donation) => filterByDate(filter, donation)); const totalsByDonor = donations.reduce((accumulator, currentDonation) => { accumulator[currentDonation.donor_id] = { amount: (accumulator[currentDonation.donor_id].amount || 0) + (currentDonation.ft_id === \"near\" ? A_103(currentDonation) : 0), ...currentDonation }; return accumulator; }, {}); const sortedDonations = Object.values(totalsByDonor).sort((a, b) => b.amount - a.amount); setAllDonationsFetched(true); return [donations, sortedDonations]; } else { return [[], []]; } }, [allDonationsPaginated, filter]); const filterOptions = [{ text: \"All Time\", value: \"all\" }, { text: \"1Y\", value: \"year\" }, { text: \"1M\", value: \"month\" }, { text: \"1W\", value: \"week\" }, { text: \"24H\", value: \"day\" }]; const MenuItem = ({ count, children, className }) => <div className={\\`menu-item \\${className || \"\"}\\`}> <div className=\"label\">{children}</div> <div className=\"count\">{count}</div> </div>; const tabs = [{ label: \"Donor Leaderboard\", val: \"leaderboard\", count: sortedDonations.length }, { label: \"Sponsors Leaderboard\", val: \"sponsors\", count: sponsors.length }, { label: \"Donor Feed\", val: \"feed\", count: allDonations.length }]; const options = [{ tab: \"feed\", src: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.DonorsTrx} props={{ ...{ ...compProps, ...props } }} /> }, { tab: \"sponsors\", src: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.DonorsLeaderboard} props={{ ...{ ...compProps, ...props } }} /> }]; const SelectedNavComponent = options.find((option) => option.tab == currentTab).src; const sortList = tabs.map((tab) => ({ label: <MenuItem key={tab.val} count={tab.count}> {tab.label} </MenuItem>, val: tab })); return <A_26> {fetchDonationsError ? <div> <h1>Error fetching donations</h1> <p>{fetchDonationsError}</p> </div> : !allDonationsFetched ? <Loading /> : <> <div className=\"leaderboard\"> <h1>Donors Leaderboard</h1> <DonorsCards {...{ sponsors, sortedDonations, currentTab }} /> </div> <Tabs> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ ...{ sortVal: title, title: <MenuItem className=\"selected\" count={tabs[0].count}> {tabs[0].val}{\" \"} </MenuItem>, sortList: sortList, FilterMenuCustomClass: \\`filter-menu\\`, handleSortChange: ({ val: option }) => {setTitle(<MenuItem className=\"selected\" count={option.count}> {option.val} </MenuItem>);setTab(option.val);} }, ...props } }} /> <Filter> {filterOptions.map((option) => <div className={\\`option \\${filter === option.value ? \"active\" : \"\"}\\`} key={option.value} onClick={() => setFilter(option.value)}> {option.text} </div>)} </Filter> </Tabs> {currentTab === \"leaderboard\" ? <Widget loading=\" \" code={props.alem.componentsCode.DonorsLeaderboard} props={{ ...{ ...{ allDonations: allDonations, filter, sponsors, sortedDonations, currentTab }, ...props } }} /> : <SelectedNavComponent {...{ allDonations: allDonations, filter, sponsors, sortedDonations, currentTab }} />} </>} </A_26>; `, ChallengeModal: ` const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_36 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; background: white; padding: 24px 24px 12px 24px; border-top-left-radius: 6px; border-top-right-radius: 6px; font-weight: 500;\\`;const A_37 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 24px; border-top: 1px #f0f0f0 solid; background: #fafafa; gap: 8px;\\`;const ModalFooter = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-end; background: #fafafa; padding: 12px 24px 24px 24px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; gap: 24px; width: 100%;\\`; const {onClose: onClose, existingChallengeForUser: existingChallengeForUser} = props; const { potId } = props.alem.useParams(); State.init({ challengeReason: \"\", challengeReasonError: \"\" }); useEffect(() => { if (existingChallengeForUser?.reason) { State.update({ challengeReason: existingChallengeForUser?.reason }); } }, [existingChallengeForUser]); const { challengeReason, challengeReasonError } = state; const handleCancelChallenge = () => { onClose(); State.update({ challengeReason: \"\", challengeReasonError: \"\" }); }; const handleSubmitChallenge = () => { PotSDK.challengePayouts(potId, challengeReason); onClose(); }; const MAX_CHALLENGE_TEXT_LENGTH = 1000; return <ModalOverlay onOverlayClick={onClose}> <A_36>Challenge Payouts</A_36> <A_37> <div>Explain the reason for your challenge</div> <TextArea {...{ noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Type the reason for your challenge here\", value: challengeReason, onChange: challengeReason => State.update({ challengeReason }), validate: () => { if (challengeReason.length > MAX_CHALLENGE_TEXT_LENGTH) { State.update({ challengeReasonError: \\`Challenge reason must be less than \\${MAX_CHALLENGE_TEXT_LENGTH} characters\\` }); return; } State.update({ challengeReasonError: \"\" }); }, error: challengeReasonError }} /> </A_37> <ModalFooter> <Button {...{ type: \"tertiary\", text: \"Cancel\", onClick: handleCancelChallenge }} /> <Button {...{ type: \"primary\", text: \"Submit Challenge\", disabled: !challengeReason || !!challengeReasonError, onClick: handleSubmitChallenge }} /> </ModalFooter> </ModalOverlay>; `, FundModal: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; function doesUserHaveDaoFunctionCallProposalPermissions(accountId, policy) { const userRoles = policy.roles.filter(role => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); return allowed;} const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const ModalTitle = styled.div\\` color: #525252; font-size: 16px; font-weight: 400; line-height: 20px; word-wrap: break-word; margin: 8px 0px;\\`;const A_38 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const UserChipLink = styled.a\\` display: flex; flex-direction: row; margin: 0px 4px; margin-left: auto; padding: 2px 12px; gap: 4px; border-radius: 32px; background: #ebebeb; &:hover { text-decoration: none; }\\`;const TextBold = styled.div\\` color: #292929; font-size: 12px; font-weight: 600; line-height: 20px; word-wrap: break-word; text-align: center;\\`;const FeeText = styled.div\\` color: #292929; font-size: 14px; font-weight: 400; line-height: 20px; word-wrap: break-word; display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const A_39 = styled.label\\` width: 100%; font-size: 12px; line-height: 16px; word-wrap: break-word; color: #2e2e2e; display: flex; flex-direction: row; align-items: center; justify-content: center;\\`; const {potDetail: potDetail, onClose: onClose} = props; const { referrerId, potId } = props.alem.useParams(); const { MAX_DONATION_MESSAGE_LENGTH, SUPPORTED_FTS, ONE_TGAS } = constants; const { protocol_config_provider, chef_fee_basis_points, chef, base_currency, min_matching_pool_donation_amount, referral_fee_matching_pool_basis_points } = potDetail; State.init({ matchingPoolDonationAmountNear: \"\", matchingPoolDonationAmountNearError: \"\", matchingPoolDonationMessage: \"\", matchingPoolDonationMessageError: \"\", bypassProtocolFee: false, bypassChefFee: false, fundAsDao: false, daoAddress: \"\", daoAddressError: \"\", daoPolicy: {} }); const { matchingPoolDonationAmountNear, matchingPoolDonationAmountNearError, matchingPoolDonationMessage, matchingPoolDonationMessageError, bypassProtocolFee, bypassChefFee, fundAsDao, daoAddress, daoAddressError } = state; Big.PE = 100; const FIFTY_TGAS = \"50000000000000\"; const THREE_HUNDRED_TGAS = \"300000000000000\"; const MIN_DAO_PROPOSAL_DEPOSIT_FALLBACK = \"100000000000000000000000\"; const protocolConfigContractId = protocol_config_provider.split(\":\")[0]; const protocolConfigViewMethodName = protocol_config_provider.split(\":\")[1]; const protocolConfig = Near.view(protocolConfigContractId, protocolConfigViewMethodName, {}); const protocolFeeRecipientProfile = Social.getr(\\`\\${protocolConfig?.account_id}/profile\\`); const chefProfile = Social.getr(\\`\\${chef}/profile\\`); const chefFeeAmountNear = bypassChefFee ? 0 : matchingPoolDonationAmountNear * potDetail?.chef_fee_basis_points / 10_000 || 0; const protocolFeeAmountNear = bypassProtocolFee ? 0 : matchingPoolDonationAmountNear * protocolConfig?.basis_points / 10_000 || 0; const referrerFeeAmountNear = referrerId ? matchingPoolDonationAmountNear * referral_fee_matching_pool_basis_points / 10_000 || 0 : 0; const handleMatchingPoolDonation = () => { const args = { message: matchingPoolDonationMessage, matching_pool: true, referrer_id: referrerId || null, bypass_protocol_fee: bypassProtocolFee }; if (state.bypassChefFee) { args.custom_chef_fee_basis_points = 0; } const amountFloat = parseFloat(matchingPoolDonationAmountNear || 0); if (!amountFloat) { State.update({ matchingPoolDonationAmountNearError: \"Invalid amount\" }); return; } const amountIndivisible = SUPPORTED_FTS[base_currency.toUpperCase()].toIndivisible(amountFloat); let transactions = [{ contractName: potId, methodName: \"donate\", deposit: amountIndivisible, args, gas: ONE_TGAS.mul(100) }]; if (state.fundAsDao) { const clonedTransactions = JSON.parse(JSON.stringify(transactions)); transactions = clonedTransactions.map(tx => { const action = { method_name: tx.methodName, gas: FIFTY_TGAS, deposit: tx.deposit ? tx.deposit.toString() : \"0\", args: Buffer.from(JSON.stringify(tx.args), \"utf-8\").toString(\"base64\") }; return { ...tx, contractName: state.daoAddress, methodName: \"add_proposal\", args: { proposal: { description: \\`Contribute to matching pool for \\${potDetail.pot_name} pot (\\${potId}) on Potlock\\`, kind: { FunctionCall: { receiver_id: tx.contractName, actions: [action] } } } }, deposit: state.daoPolicy.proposal_bond || MIN_DAO_PROPOSAL_DEPOSIT_FALLBACK, gas: THREE_HUNDRED_TGAS }; }); } Near.call(transactions); }; const disabled = fundAsDao && !daoAddress || daoAddressError || !matchingPoolDonationAmountNear || !!matchingPoolDonationAmountNearError || !parseFloat(matchingPoolDonationAmountNear); return <ModalOverlay onOverlayClick={onClose}> <CheckBox label=\"Fund as DAO\" id=\"fundAsDaoSelector\" checked={fundAsDao} onClick={e => { State.update({ fundAsDao: e.target.checked }); }} /> {fundAsDao && <A_62 inputStyles={{ background: \"#FAFAFA\" }} placeholder=\"Enter DAO address\" value={daoAddress} onChange={daoAddress => State.update({ daoAddress: daoAddress.trim().toLowerCase() })} validate={() => { Near.asyncView(daoAddress, \"get_policy\", {}).then(policy => { if (!policy) { State.update({ daoAddressError: \"Invalid DAO address\" }); } if (!doesUserHaveDaoFunctionCallProposalPermissions(context.accountId || \"\", policy)) { State.update({ daoAddressError: \"Your account does not have permission to create proposals\" }); } else { State.update({ daoAddressError: \"\", daoPolicy: policy }); } }).catch(e => { State.update({ daoAddressError: \"Invalid DAO address\" }); }); }} error={daoAddressError} />} <ModalTitle> Enter matching pool contribution amount in NEAR {[\"0\", \"1\"].includes(min_matching_pool_donation_amount) ? \"(no minimum)\" : \\`(Min. \\${yoctosToNear(min_matching_pool_donation_amount)})\\`} </ModalTitle> <A_62 inputStyles={{ background: \"#FAFAFA\" }} placeholder=\"Enter amount here in NEAR\" value={matchingPoolDonationAmountNear} onChange={matchingPoolDonationAmountNear => State.update({ matchingPoolDonationAmountNear })} validate={() => { State.update({ matchingPoolDonationAmountNearError: \"\" }); }} error={matchingPoolDonationAmountNearError} /> <TextArea noLabel={true} inputRows={5} inputStyle={{ marginTop: \"0.45rem\", background: \"#FAFAFA\" }} placeholder=\"Enter an optional message\" value={matchingPoolDonationMessage} onChange={matchingPoolDonationMessage => State.update({ matchingPoolDonationMessage })} validate={() => { if (matchingPoolDonationMessage.length > MAX_DONATION_MESSAGE_LENGTH) { State.update({ matchingPoolDonationMessageError: \\`Message must be less than \\${MAX_DONATION_MESSAGE_LENGTH} characters\\` }); return; } State.update({ matchingPoolDonationMessageError: \"\" }); }} error={matchingPoolDonationMessageError} /> <A_38> <CheckBox id=\"bypassProtocolFeeSelector\" checked={bypassProtocolFee} onClick={e => { State.update({ bypassProtocolFee: e.target.checked }); }} /> <A_39 htmlFor=\"bypassProtocolFeeSelector\"> Bypass {protocolConfig?.basis_points / 100 || \"-\"}% protocol fee to{\" \"} <UserChipLink href={hrefWithParams(\\`?tab=profile&accountId=\\${protocolConfig?.account_id}\\`)} target=\"_blank\"> <ProfileImage accountId={protocolConfig?.account_id} style={{ height: \"12px\", width: \"12px\" }} /> <TextBold>{_address(protocolFeeRecipientProfile?.name || protocolConfig?.account_id)}</TextBold> </UserChipLink> </A_39> </A_38> {chef && chef_fee_basis_points > 0 && <A_38 style={{ marginTop: \"6px\" }}> <CheckBox id=\"bypassChefFeeSelector\" checked={bypassChefFee} onClick={e => { State.update({ bypassChefFee: e.target.checked }); }} /> <A_39 htmlFor=\"bypassChefFeeSelector\"> Bypass {chef_fee_basis_points / 100 || \"-\"}% chef fee to <UserChipLink href={hrefWithParams(\\`?tab=profile&accountId=\\${chef}\\`)} target=\"_blank\"> <ProfileImage accountId={chef} style={{ height: \"12px\", width: \"12px\" }} /> <TextBold>{chefProfile?.name || chef}</TextBold> </UserChipLink> </A_39> </A_38>} <A_38 style={{ marginTop: \"12px\" }}> <FeeText>Protocol fee: {protocolFeeAmountNear} NEAR</FeeText> </A_38> {chef && chef_fee_basis_points > 0 && <A_38 style={{ marginTop: \"12px\" }}> <FeeText>Chef fee: {chefFeeAmountNear} NEAR</FeeText> </A_38>} <A_38 style={{ marginTop: \"6px\" }}> {referrerId && <FeeText> Referrer fee (to {referrerId}): {referrerFeeAmountNear} NEAR </FeeText>} </A_38> <A_38 style={{ marginTop: \"6px\" }}> <FeeText> Net donation amount:{\" \"} {(matchingPoolDonationAmountNear - protocolFeeAmountNear - chefFeeAmountNear - referrerFeeAmountNear).toFixed(2)}{\" \"} NEAR </FeeText> </A_38> <A_38 style={{ justifyContent: \"flex-end\", marginTop: \"12px\" }}> <Button type=\"primary\" disabled={disabled} text={\\`\\${fundAsDao ? \"Create proposal to contribute \" : \"Contribute\"}\\${matchingPoolDonationAmountNear ? \\` \\${matchingPoolDonationAmountNear} \\${base_currency.toUpperCase()}\\` : \"\"} to matching pool\\`} onClick={disabled ? () => {} : handleMatchingPoolDonation} /> </A_38> </ModalOverlay>; `, NewApplicationModal: ` function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const handleSendApplication = (potId, potDetail, setApplicationSuccess, isDao) => { const FIFTY_TGAS = \"50000000000000\"; const MIN_PROPOSAL_DEPOSIT_FALLBACK = \"100000000000000000000000\"; const THREE_HUNDRED_TGAS = \"300000000000000\"; const { ONE_TGAS, SUPPORTED_FTS: { NEAR } } = constants; const args = { message: state.applicationMessage }; let deposit = NEAR.toIndivisible(\"0.01\"); const extraDeposit = Big(state.applicationMessage.length * 0.0001).mul(Big(10).pow(24)); deposit = deposit.plus(extraDeposit); let transactions = [{ contractName: potId, methodName: \"apply\", deposit, args, gas: ONE_TGAS.mul(100) }]; if (isDao) { const clonedTransactions = JSON.parse(JSON.stringify(transactions)); transactions = clonedTransactions.map(tx => { const action = { method_name: tx.methodName, gas: FIFTY_TGAS, deposit: tx.deposit ? tx.deposit.toString() : \"0\", args: Buffer.from(JSON.stringify(tx.args), \"utf-8\").toString(\"base64\") }; return { ...tx, contractName: state.daoAddress, methodName: \"add_proposal\", args: { proposal: { description: \\`Application to PotLock pot: \\${potDetail.pot_name} (\\${potId})\\`, kind: { FunctionCall: { receiver_id: tx.contractName, actions: [action] } } } }, deposit: state.daoPolicy.proposal_bond || MIN_PROPOSAL_DEPOSIT_FALLBACK, gas: THREE_HUNDRED_TGAS }; }); } Near.call(transactions); const pollIntervalMs = 1000; const pollId = setInterval(() => { PotSDK.asyncGetApplications(potId).then(applications => { const application = applications.find(application => application.project_id === (isDao ? state.daoAddress : context.accountId)); if (application) { clearInterval(pollId); setApplicationSuccess(true); } }); }, pollIntervalMs);}; function doesUserHaveDaoFunctionCallProposalPermissions(accountId, policy) { const userRoles = policy.roles.filter(role => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); return allowed;} const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_40 = styled.div\\` color: #525252; font-size: 16px; font-weight: 400; line-height: 20px; word-wrap: break-word; margin-bottom: 8px;\\`;const A_41 = styled.div\\` display: flex; flex-direction: row; align-items: center;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const {potDetail: potDetail, onClose: onClose, setIsDao: setIsDao, isDao: isDao, setApplicationSuccess: setApplicationSuccess, setRegistryStatus: setRegistryStatus, registryStatus: registryStatus} = props; const MAX_APPLICATION_MESSAGE_LENGTH = 1000; State.init({ applicationMessage: \"\", applicationMessageError: \"\", daoAddress: \"\", daoPolicy: \"\", daoAddressError: \"\" }); const updateState = State.update; const { applicationMessage, applicationMessageError, daoAddress, daoAddressError } = state; const accountId = context.accountId || \"\"; const { potId } = props.alem.useParams(); const verifyIsOnRegistry = address => { Near.asyncView(\"lists.potlock.near\", \"get_registrations_for_registrant\", { registrant_id: address }).then(registrations => { const registration = registrations.find(registration => registration.list_id === 1); if (registration) { setRegistryStatus(registration.status); } }); }; useEffect(() => { if (!state.isDao) { verifyIsOnRegistry(context.accountId || \"\"); } }, []); const textAreaProps = { noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Your application message here...\", value: applicationMessage, onChange: applicationMessage => updateState({ applicationMessage }), validate: () => { if (applicationMessage.length > MAX_APPLICATION_MESSAGE_LENGTH) { updateState({ applicationMessageError: \\`Application message must be less than \\${MAX_APPLICATION_MESSAGE_LENGTH} characters\\` }); return; } updateState({ applicationMessageError: \"\" }); }, error: applicationMessageError }; const checkBoxProps = { id: \"isDaoSelector\", checked: isDao, onClick: e => { setIsDao(e.target.checked); if (!e.target.checked) { verifyIsOnRegistry(accountId || \"\"); } }, label: \"I'm applying as a DAO\" }; const textProps = { label: \"DAO address *\", placeholder: \"E.g. mydao.sputnikdao.near\", value: daoAddress, onChange: daoAddress => updateState({ daoAddress, daoAddressError: \"\" }), validate: () => { Near.asyncView(daoAddress, \"get_policy\", {}).then(policy => { const hasPermissions = !policy ? false : doesUserHaveDaoFunctionCallProposalPermissions(accountId, policy); updateState({ daoAddressError: hasPermissions ? \"\" : \"You don't have required permissions to submit proposals to this DAO.\", daoPolicy: policy }); verifyIsOnRegistry(daoAddress); }).catch(e => { updateState({ daoAddressError: \"Invalid DAO address\" }); }); }, error: daoAddressError }; const isError = applicationMessageError || daoAddressError; const registrationApproved = registryStatus === \"Approved\"; const registrationApprovedOrNoRegistryProvider = registrationApproved || !potDetail?.registry_provider; return <ModalOverlay onOverlayClick={onClose}> <A_40> Application message <span style={{ color: \"#DD3345\" }}>*</span> </A_40> <TextArea {...textAreaProps} /> <A_41 style={{ margin: \"12px 0px\" }}> <CheckBox {...checkBoxProps} /> </A_41> {isDao && <A_62 {...textProps} />} <A_41 style={{ justifyContent: \"flex-end\", marginTop: \"12px\" }}> <Button type=\"primary\" text={isDao ? \"Propose to Send Application\" : registrationApprovedOrNoRegistryProvider ? \"Send application\" : \"Register to apply\"} onClick={(isDao || registrationApprovedOrNoRegistryProvider) && !isError ? () => { handleSendApplication(potId, potDetail, setApplicationSuccess, isDao); } : () => {}} disabled={isError} href={isDao || registrationApprovedOrNoRegistryProvider ? \"\" : hrefWithParams(\\`?tab=createproject\\`)} target={isDao || registrationApprovedOrNoRegistryProvider ? \"_self\" : \"_blank\"} /> </A_41> </ModalOverlay>; `, A_44: ` const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_42 = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 12px; border-top: 1px solid #292929; border-right: 1px solid #292929; border-bottom: 2px solid #292929; border-left: 1px solid #292929; overflow: hidden; .header { font-size: 18px; font-weight: 600; background: #fef6ee; padding: 1rem; span { color: #ee8949; } } .sort { display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 1rem; font-size: 11px; background: #fef6ee; .title { font-weight: 500; letter-spacing: 0.44px; text-transform: uppercase; } .sort-btn { font-weight: 500; display: flex; align-items: center; gap: 0.5rem; cursor: pointer; } }\\`;const A_43 = styled.div\\` display: flex; align-items: center; font-size: 14px; padding: 1rem; gap: 8px; border-bottom: 1px solid #c7c7c7; &:last-of-type { border-bottom: none; } .address { display: flex; text-decoration: none; align-items: center; font-weight: 600; gap: 8px; margin-left: 24px; flex: 1; color: #292929; transition: color 200ms ease-in; :hover { color: #dd3345; } } .profile-image { width: 18px; height: 18px; border-radius: 50%; display: flex !important; }\\`; const { donations: donations, totalAmount: totalAmount, totalUniqueDonors: totalUniqueDonors, title: title, allPayouts: allPayouts, potDetail: potDetail } = props; const [usdToggle, setUsdToggle] = useState(false); const { SUPPORTED_FTS } = constants; return <A_42> <div className=\"header\"> {totalAmount} <span>raised from</span> {totalUniqueDonors} <span>{title === \"sponsors\" ? \"sponsors\" : \"donors\"}</span> </div> <div className=\"sort\"> <div className=\"title\">Top {title} </div> <div className=\"sort-btn\" style={{ cursor: A_236 ? \"pointer\" : \"default\" }} onClick={() => A_236 ? setUsdToggle(!usdToggle) : \"\"}> {A_236 && <svg width=\"12\" height=\"14\" viewBox=\"0 0 12 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9 10.7575V5.5H7.5V10.7575H5.25L8.25 13.75L11.25 10.7575H9ZM3.75 0.25L0.75 3.2425H3V8.5H4.5V3.2425H6.75L3.75 0.25ZM9 10.7575V5.5H7.5V10.7575H5.25L8.25 13.75L11.25 10.7575H9ZM3.75 0.25L0.75 3.2425H3V8.5H4.5V3.2425H6.75L3.75 0.25Z\" fill=\"#7B7B7B\" /> </svg>} {usdToggle ? \"USD\" : \"NEAR\"} </div> </div> {donations.map(({ projectId, donor_id, matchingAmount, net_amount }, idx) => { const id = donor_id || projectId; const nearAmount = formatWithCommas(SUPPORTED_FTS[potDetail.base_currency.toUpperCase()].fromIndivisible(net_amount || matchingAmount)); const profile = Social.getr(\\`\\${id}/profile\\`); const matchedAmout = usdToggle ? yoctosToUsdWithFallback(matchingAmount || net_amount, true) : nearAmount; const url = projectId ? \\`?tab=project&projectId=\\${projectId}\\` : \\`?tab=profile&accountId=\\${donor_id}\\`; return <A_43> <div>#{idx + 1}</div> <a className=\"address\" href={hrefWithParams(url)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ image: profile?.image, className: \"profile-image\", fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\", style: {}, ...props } }} /> {_address(profile?.name || id, 15)} </a> <div> {matchedAmout} {usdToggle ? \" \" : \"N\"} </div> </A_43>; })} </A_42>; `, PoolAllocationTable: ` const calculatePayouts = (allPotDonations, totalMatchingPool, blacklistedAccounts) => { const { NADABOT_CONTRACT_ID } = constants; return new Promise((resolve, reject) => { console.log(\"Calculting payouts; ignoring blacklisted donors &/or projects: \", blacklistedAccounts.join(\", \")); console.log(\"totalMatchingPool: \", totalMatchingPool); const projectContributions = []; const allDonors = new Set(); for (const d of allPotDonations) { if (blacklistedAccounts.includes(d.donor_id) || blacklistedAccounts.includes(d.project_id)) { continue; } const amount = new Big(d.total_amount); const val = [d.project_id, d.donor_id, amount]; projectContributions.push(val); allDonors.add(d.donor_id); } const limit = 100; let curIndex = 0; let humanScores = {}; let promises = []; while (curIndex < allDonors.size) { promises.push(Near.asyncView(NADABOT_CONTRACT_ID, \"get_human_score_batch\", { account_ids: Array.from(allDonors).slice(curIndex, curIndex + limit) })); curIndex += limit; } Promise.all(promises).then(res => { for (const r of res) { humanScores = { ...humanScores, ...r }; } }).catch(e => { console.error(\"error fetching human scores. Continuing anyway: \", e); }).finally(() => { console.log(\"human scores: \", humanScores); const contributions = {}; for (const [proj, user, amount] of projectContributions) { if (!humanScores[user] || !humanScores[user].is_human) { console.log(\"skipping non-human: \", user); continue; } if (!contributions[proj]) { contributions[proj] = {}; } contributions[proj][user] = Big(contributions[proj][user] || 0).plus(amount); } console.log(\"contributions: \", contributions); const pairTotals = {}; for (const contribz of Object.values(contributions)) { for (const [k1, v1] of Object.entries(contribz)) { if (!pairTotals[k1]) { pairTotals[k1] = {}; } for (const [k2, v2] of Object.entries(contribz)) { if (!pairTotals[k1][k2]) { pairTotals[k1][k2] = Big(0); } pairTotals[k1][k2] = pairTotals[k1][k2].plus(v1.times(v2).sqrt()); } } } const threshold = Big(\"25000000000000000000000000\"); const totalPot = Big(totalMatchingPool); let bigtot = Big(0); const totals = []; for (const [proj, contribz] of Object.entries(contributions)) { let tot = Big(0); let _num = 0; let _sum = Big(0); for (const [k1, v1] of Object.entries(contribz)) { _num += 1; _sum = _sum.plus(v1); for (const [k2, v2] of Object.entries(contribz)) { if (k2 > k1 || Object.keys(contribz).length === 1) { const sqrt = v1.times(v2).sqrt(); tot = tot.plus(sqrt.div(pairTotals[k1][k2].div(threshold))); } } } bigtot = bigtot.plus(tot); totals.push({ id: proj, number_contributions: _num, contribution_amount_str: _sum.toFixed(0), matching_amount_str: tot.toFixed(0) }); } console.log(\"totals before: \", totals); if (bigtot.gte(totalPot)) { console.log(\"NORMALIZING\"); for (const t of totals) { t.matching_amount_str = Big(t.matching_amount_str).div(bigtot).times(totalPot).toFixed(0); } } let totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } let residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"first round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(\"0\"))) { for (let i = 0; i < totals.length; i++) { let proportion = Big(totals[i].matching_amount_str).div(totalAllocatedBeforeRounding); let additionalAllocation = proportion.times(residual); totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(additionalAllocation).toFixed(0); } console.log(\"CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION\"); totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"second round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(0))) { totals.sort((a, b) => Big(b.matching_amount_str).minus(Big(a.matching_amount_str))); if (residual.gt(Big(0))) { totals[0].matching_amount_str = Big(totals[0].matching_amount_str).plus(residual).toFixed(0); } else { for (let i = 0; i < totals.length; i++) { if (Big(totals[i].matching_amount_str).gt(residual.abs())) { totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(residual).toFixed(0); break; } } } totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"Residual after final adjustment: \", residual.toFixed(0)); } } const payouts = totals.reduce((acc, t) => { acc[t.id] = { totalAmount: t.contribution_amount_str, matchingAmount: t.matching_amount_str, donorCount: t.number_contributions }; return acc; }, {}); resolve(payouts); }); });}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsdWithFallback = (amountNear, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas((amountNear * A_236).toFixed(2)) : formatWithCommas(amountNear.toString()) + (abbreviate ? \"N\" : \" NEAR\");}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const { potDetail: potDetail, allDonations: allDonations } = props; const { SUPPORTED_FTS } = constants; const { base_currency, total_public_donations, matching_pool_balance, public_donations_count } = potDetail; const [projectsId, setProjectsId] = useState(null); const [allPayouts, setAllPayouts] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); const { potId } = props.alem.useParams(); if (!projectsId) { PotSDK.asyncGetApprovedApplications(potId).then((projects) => { setProjectsId(projects); }); } let sponsorshipDonations = PotSDK.getMatchingPoolDonations(potId); if (sponsorshipDonations) sponsorshipDonations.sort((a, b) => b.net_amount - a.net_amount); const calcMatchedAmount = (donations) => { if (donations) { let total = Big(0); donations?.forEach((donation) => { total = total.plus(Big(donation.net_amount)); }); const amount = SUPPORTED_FTS[base_currency.toUpperCase() || \"NEAR\"].fromIndivisible(total.toString()); return amount; } }; const uniqueDonorIds = allDonations ? new Set(allDonations.map((donation) => donation.donor_id)) : new Set([]); const donorsCount = uniqueDonorIds.size; if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => { if (data) { const listOfFlagged = []; data?.forEach((adminFlaggedAcc) => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); listOfFlagged.push(...addresses); }); setFlaggedAddresses(listOfFlagged); } }).catch((err) => console.log(\"error getting the flagged accounts \", err)); } const sortAndSetPayouts = (payouts) => { payouts.sort((a, b) => { return b.matchingAmount - a.matchingAmount; }); setAllPayouts(payouts.slice(0, 5)); }; if (!allPayouts && allDonations?.length > 0 && flaggedAddresses) { let allPayouts = []; if (potDetail.payouts.length) { allPayouts = potDetail.payouts.map((payout) => { const { project_id, amount } = payout; return { projectId: project_id, matchingAmount: amount }; }); sortAndSetPayouts(allPayouts); } else { calculatePayouts(allDonations, matching_pool_balance, flaggedAddresses).then((calculatedPayouts) => { allPayouts = Object.entries(calculatedPayouts).map(([projectId, { matchingAmount }]) => { return { projectId, matchingAmount }; }); sortAndSetPayouts(allPayouts); }); } } return allPayouts?.length > 0 ? <Widget loading=\" \" code={props.alem.componentsCode.A_44} props={{ ...{ title: \"matching pool allocations\", totalAmount: yoctosToUsdWithFallback(total_public_donations, true), totalUniqueDonors: donorsCount, donations: allPayouts, ...props } }} /> : sponsorshipDonations.length > 0 ? <Widget loading=\" \" code={props.alem.componentsCode.A_44} props={{ ...{ title: \"sponsors\", totalAmount: nearToUsdWithFallback(calcMatchedAmount(sponsorshipDonations)), totalUniqueDonors: new Set(sponsorshipDonations.map((obj) => obj.donor_id)).size, donations: sponsorshipDonations.slice(0, 5), ...props } }} /> : \"\"; `, A_48: ` const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useDonationModal = () => useContext(\"donation-modal\"); const calculatePayouts = (allPotDonations, totalMatchingPool, blacklistedAccounts) => { const { NADABOT_CONTRACT_ID } = constants; return new Promise((resolve, reject) => { console.log(\"Calculting payouts; ignoring blacklisted donors &/or projects: \", blacklistedAccounts.join(\", \")); console.log(\"totalMatchingPool: \", totalMatchingPool); const projectContributions = []; const allDonors = new Set(); for (const d of allPotDonations) { if (blacklistedAccounts.includes(d.donor_id) || blacklistedAccounts.includes(d.project_id)) { continue; } const amount = new Big(d.total_amount); const val = [d.project_id, d.donor_id, amount]; projectContributions.push(val); allDonors.add(d.donor_id); } const limit = 100; let curIndex = 0; let humanScores = {}; let promises = []; while (curIndex < allDonors.size) { promises.push(Near.asyncView(NADABOT_CONTRACT_ID, \"get_human_score_batch\", { account_ids: Array.from(allDonors).slice(curIndex, curIndex + limit) })); curIndex += limit; } Promise.all(promises).then(res => { for (const r of res) { humanScores = { ...humanScores, ...r }; } }).catch(e => { console.error(\"error fetching human scores. Continuing anyway: \", e); }).finally(() => { console.log(\"human scores: \", humanScores); const contributions = {}; for (const [proj, user, amount] of projectContributions) { if (!humanScores[user] || !humanScores[user].is_human) { console.log(\"skipping non-human: \", user); continue; } if (!contributions[proj]) { contributions[proj] = {}; } contributions[proj][user] = Big(contributions[proj][user] || 0).plus(amount); } console.log(\"contributions: \", contributions); const pairTotals = {}; for (const contribz of Object.values(contributions)) { for (const [k1, v1] of Object.entries(contribz)) { if (!pairTotals[k1]) { pairTotals[k1] = {}; } for (const [k2, v2] of Object.entries(contribz)) { if (!pairTotals[k1][k2]) { pairTotals[k1][k2] = Big(0); } pairTotals[k1][k2] = pairTotals[k1][k2].plus(v1.times(v2).sqrt()); } } } const threshold = Big(\"25000000000000000000000000\"); const totalPot = Big(totalMatchingPool); let bigtot = Big(0); const totals = []; for (const [proj, contribz] of Object.entries(contributions)) { let tot = Big(0); let _num = 0; let _sum = Big(0); for (const [k1, v1] of Object.entries(contribz)) { _num += 1; _sum = _sum.plus(v1); for (const [k2, v2] of Object.entries(contribz)) { if (k2 > k1 || Object.keys(contribz).length === 1) { const sqrt = v1.times(v2).sqrt(); tot = tot.plus(sqrt.div(pairTotals[k1][k2].div(threshold))); } } } bigtot = bigtot.plus(tot); totals.push({ id: proj, number_contributions: _num, contribution_amount_str: _sum.toFixed(0), matching_amount_str: tot.toFixed(0) }); } console.log(\"totals before: \", totals); if (bigtot.gte(totalPot)) { console.log(\"NORMALIZING\"); for (const t of totals) { t.matching_amount_str = Big(t.matching_amount_str).div(bigtot).times(totalPot).toFixed(0); } } let totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } let residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"first round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(\"0\"))) { for (let i = 0; i < totals.length; i++) { let proportion = Big(totals[i].matching_amount_str).div(totalAllocatedBeforeRounding); let additionalAllocation = proportion.times(residual); totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(additionalAllocation).toFixed(0); } console.log(\"CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION\"); totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"second round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(0))) { totals.sort((a, b) => Big(b.matching_amount_str).minus(Big(a.matching_amount_str))); if (residual.gt(Big(0))) { totals[0].matching_amount_str = Big(totals[0].matching_amount_str).plus(residual).toFixed(0); } else { for (let i = 0; i < totals.length; i++) { if (Big(totals[i].matching_amount_str).gt(residual.abs())) { totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(residual).toFixed(0); break; } } } totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"Residual after final adjustment: \", residual.toFixed(0)); } } const payouts = totals.reduce((acc, t) => { acc[t.id] = { totalAmount: t.contribution_amount_str, matchingAmount: t.matching_amount_str, donorCount: t.number_contributions }; return acc; }, {}); resolve(payouts); }); });}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_45 = styled.div\\` display: flex; flex-wrap: wrap; gap: 2rem; padding: 64px 4rem 80px; .pool-table { max-width: 514px; width: 100%; } @media only screen and (max-width: 1068px) { flex-direction: column; .pool-table { margin: auto; } } @media only screen and (max-width: 768px) { padding: 3rem 0; .pool-table { max-width: 100%; } }\\`;const HeaderWrapper = styled.div\\` display: flex; flex-direction: column; gap: 24px; flex: 1;\\`;const A_46 = styled.div\\` font-size: 40px; font-weight: 500; font-family: \"Lora\";\\`;const A_47 = styled.div\\` max-width: 498px; line-height: 1.5em; a { color: #7b7b7b; font-weight: 600; }\\`;const Fund = styled.div\\` display: flex; flex-direction: column; gap: 8px; > div { display: flex; gap: 8px; align-items: baseline; div { font-weight: 600; } } .near-price { font-size: 24px; }\\`;const ButtonsWrapper = styled.div\\` display: flex; flex-wrap: wrap; gap: 2rem; a, button { width: 180px; padding: 16px; } @media only screen and (max-width: 480px) { flex-direction: column; gap: 1rem; a, button { width: 100%; } }\\`;const Referral = styled.div\\` font-size: 14px; gap: 12px; display: flex; align-items: center;\\`; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const { potDetail: potDetail, allDonations: allDonations } = props; const { admins, chef, owner, pot_name, pot_description, matching_pool_balance, public_round_end_ms, public_round_start_ms, application_start_ms, application_end_ms, cooldown_end_ms, all_paid_out } = potDetail; const { IPFS_BASE_URL, NADA_BOT_URL } = constants; const { potId } = props.alem.useParams(); const { setDonationModalProps } = useDonationModal(); const NADABOT_ICON_URL = IPFS_BASE_URL + \"bafkreiecgkoybmplo4o542fphclxrhh4nlof5uit3lkzyv4eo2qymrpsru\"; const accountId = context.accountId || \"\"; const [isMatchingPoolModalOpen, setIsMatchingPoolModalOpen] = useState(false); const [isApplicationModalOpen, setIsApplicationModalOpen] = useState(false); const [showChallengePayoutsModal, setShowChallengePayoutsModal] = useState(false); const [projects, setProjects] = useState(null); const [registryStatus, setRegistryStatus] = useState(null); const [isDao, setIsDao] = useState(null); const [applicationSuccess, setApplicationSuccess] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); const verifyIsOnRegistry = (address) => { Near.asyncView(\"lists.potlock.near\", \"get_registrations_for_registrant\", { registrant_id: address }).then((registrations) => { const registration = registrations.find((registration) => registration.list_id === 1); if (registration) { setRegistryStatus(registration.status); } }); }; useEffect(() => { if (!isDao) { verifyIsOnRegistry(context.accountId || \"\"); } }, []); const projectNotRegistered = registryStatus === null; const userIsAdminOrGreater = admins.includes(accountId) || owner === accountId; const userIsChefOrGreater = userIsAdminOrGreater || chef === accountId; const existingApplication = PotSDK.getApplicationByProjectId(potId, context.accountId); useEffect(() => { if (!projects) { PotSDK.asyncGetApprovedApplications(potId).then((projects) => { setProjects(projects); }); } }, []); const applicationExists = existingApplication || applicationSuccess; const now = Date.now(); const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; const publicRoundEnded = now > public_round_end_ms; const applicationOpen = now >= application_start_ms && now < application_end_ms; const canApply = applicationOpen && !applicationExists && !userIsChefOrGreater; const potLink = \\`https://bos.potlock.io/?tab=pot&potId=\\${potId}\\${context.accountId && \\`&referrerId=\\${context.accountId}\\`}\\`; const canPayoutsBeProcessed = userIsAdminOrGreater && now >= cooldown_end_ms && !all_paid_out; const canPayoutsBeSet = userIsChefOrGreater && !all_paid_out && publicRoundEnded; const payoutsChallenges = PotSDK.getPayoutsChallenges(potId); if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => { const listOfFlagged = []; data.forEach((adminFlaggedAcc) => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); listOfFlagged.push(...addresses); }); setFlaggedAddresses(listOfFlagged); }).catch((err) => console.log(\"error getting the flagged accounts \", err)); } const handleSetPayouts = () => { if (allDonations && flaggedAddresses !== null) { calculatePayouts(allDonations, matching_pool_balance, flaggedAddresses).then((calculatedPayouts) => { const payouts = Object.entries(calculatedPayouts).map(([projectId, { matchingAmount }]) => ({ project_id: projectId, amount: matchingAmount })).filter((payout) => payout.amount !== \"0\"); PotSDK.chefSetPayouts(potId, payouts); }); } else { console.log(\"error fetching donations or flagged addresses\"); } }; const handleProcessPayouts = () => { PotSDK.adminProcessPayouts(potId); }; const existingChallengeForUser = (payoutsChallenges || []).find((challenge) => challenge.challenger_id === context.accountId); const canDonate = projects.length > 0 && publicRoundOpen && context.accountId; const registrationApproved = registryStatus === \"Approved\"; return <A_45> <HeaderWrapper> <A_46>{pot_name}</A_46> <A_47> <Markdown text={pot_description} /> </A_47> <Fund> <div className=\"label\">Matching Funds Available:</div> <div> <div className=\"near-price\">{yoctosToNear(matching_pool_balance, true)}</div> {A_236 && <div className=\"usd-price\"> {yoctosToUsdWithFallback(matching_pool_balance, true)}</div>} </div> </Fund> <ButtonsWrapper> {canDonate && <Button type=\"primary\" text={\"Donate\"} href={canDonate ? \"\" : NADA_BOT_URL} onClick={canDonate ? () => { setDonationModalProps({ potId, potDetail, projects, multiple: true }); } : () => {}} target={canDonate ? \"_self\" : \"_blank\"} iconSrc={canDonate ? \"\" : NADABOT_ICON_URL} />} {now < public_round_end_ms && <Button type=\"secondary\" text=\"Fund matching pool\" onClick={() => setIsMatchingPoolModalOpen(true)} />} {canApply && <Button type={registrationApproved || projectNotRegistered ? \"primary\" : \"tertiary\"} text={registryStatus && !registrationApproved ? \\`Project Registration \\${registryStatus}\\` : \"Apply to pot\"} style={{ marginRight: \"24px\" }} disabled={registryStatus && !registrationApproved} onClick={() => setIsApplicationModalOpen(true)} />} {now > public_round_end_ms && now < cooldown_end_ms && <Button type=\"secondary\" text={existingChallengeForUser ? \"Update challenge\" : \"Challenge payouts\"} onClick={() => setShowChallengePayoutsModal(true)} />} {canPayoutsBeSet && <Button {...{ text: \"Set Payouts\", onClick: handleSetPayouts }} />} {canPayoutsBeProcessed && <Button type=\"primary\" text=\"Process Payouts\" onClick={handleProcessPayouts} />} </ButtonsWrapper> <Referral> <Widget loading=\" \" code={props.alem.componentsCode.CopyIcon} props={{ ...{ textToCopy: potLink, ...props } }} /> Earn referral fees </Referral> </HeaderWrapper> <div className=\"pool-table\"> <Widget loading=\" \" code={props.alem.componentsCode.PoolAllocationTable} props={{ ...{ allDonations: allDonations, potDetail: potDetail, ...props } }} /> </div> {isApplicationModalOpen && <Widget loading=\" \" code={props.alem.componentsCode.NewApplicationModal} props={{ ...{ onClose: () => setIsApplicationModalOpen(false), setIsDao: setIsDao, isDao: isDao, registryStatus: registryStatus, setRegistryStatus: setRegistryStatus, setApplicationSuccess: setApplicationSuccess, potDetail: potDetail, ...props } }} />} {isMatchingPoolModalOpen && <Widget loading=\" \" code={props.alem.componentsCode.FundModal} props={{ ...{ potDetail: potDetail, onClose: () => {setIsMatchingPoolModalOpen(false);}, ...props } }} />} {showChallengePayoutsModal && <Widget loading=\" \" code={props.alem.componentsCode.ChallengeModal} props={{ ...{ existingChallengeForUser: existingChallengeForUser, onClose: () => setShowChallengePayoutsModal(false), ...props } }} />} </A_45>; `, TimeLeft: ` const {daysLeft: daysLeft} = props; const [timeLeft, setTimeLeft] = useState(\"-\"); function formatTimeLeft(targetTimestamp) { const now = new Date().getTime(); const timeRemaining = targetTimestamp - now; if (timeRemaining <= 0) { return \"Time's up!\"; } const days = Math.floor(timeRemaining / (1000 * 60 * 60 * 24)); const hours = Math.floor(timeRemaining % (1000 * 60 * 60 * 24) / (1000 * 60 * 60)); const minutes = Math.floor(timeRemaining % (1000 * 60 * 60) / (1000 * 60)); const seconds = Math.floor(timeRemaining % (1000 * 60) / 1000); const formattedTime = \\`\\${days ? days + \"d\" : \"\"} \\${hours ? hours + \"h\" : \"\"} \\${minutes ? minutes + \"m\" : \"\"} \\${seconds ? seconds + \"s\" : \"\"}\\`; return formattedTime.trim(); } useEffect(() => { const intervelId = setInterval(() => { const time = formatTimeLeft(daysLeft); setTimeLeft(time); const now = new Date().getTime(); if (now > daysLeft) { clearInterval(intervelId); } }, 1000); }, []); return timeLeft; `, HeaderStatus: ` const Wrapper = styled.div\\` border-top: 1px solid rgb(199 199 199 / 50%); border-bottom: 1px solid rgb(199 199 199 / 50%); position: relative; display: flex; align-items: center; margin-top: -1px; pointer-events: none; .spread-indicator { height: auto; width: 12px; transition: all 300ms ease-in-out; display: none; } @media only screen and (max-width: 1100px) { pointer-events: all; cursor: pointer; .spread-indicator { display: block; } }\\`;const State = styled.div\\` display: flex; align-items: center; position: relative; gap: 1rem; font-size: 14px; white-space: nowrap; span { font-weight: 600; color: #dd3345; }\\`;const Loader = styled.div\\` position: relative; background: #dbdbdb; border-radius: 1px; height: 4px; width: 130px; @media only screen and (max-width: 1400px) { width: 90px; } @media only screen and (max-width: 1100px) { height: 40px; width: 4px; position: absolute; left: 10px; z-index: 0; top: 50%; }\\`;const ProgressBarWrapper = styled.div\\` position: relative; display: flex; .circle { width: 24px; height: 24px; transform: rotate(-90deg); } .check { width: 12px; position: absolute; transform: translate(-50%, -50%); top: 50%; left: 50%; } @media only screen and (max-width: 1100px) { z-index: 1; background: white; padding: 2px 0; }\\`; const ProgressBar = ({ progress, completed, started}) => <ProgressBarWrapper> <svg viewBox=\"0 0 160 160\" className=\"circle\"> <circle r=\"70\" cx=\"80\" cy=\"80\" fill=\"transparent\" stroke={completed ? \"#629D13\" : started ? \"#000000\" : \"#C7C7C7\"} strokeWidth=\"12px\"> </circle> <circle r=\"70\" cx=\"80\" cy=\"80\" fill=\"transparent\" stroke=\"#C7C7C7\" strokeWidth=\"12px\" strokeDasharray=\"439.6px\" strokeDashoffset={439.6 * progress + \"px\"}> </circle> </svg> <svg className=\"check\" viewBox=\"0 0 12 9\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M3.72667 7.05333L0.946667 4.27333L0 5.21333L3.72667 8.94L11.7267 0.94L10.7867 0L3.72667 7.05333Z\" style={{ fill: completed ? \"#629D13\" : started ? \"#7B7B7B\" : \"#C7C7C7\" }} /> </svg> </ProgressBarWrapper>; const statsList = potDetail => { const { application_start_ms, application_end_ms, public_round_start_ms, public_round_end_ms, cooldown_end_ms, all_paid_out } = potDetail; const now = Date.now(); const stats = [{ label: \"Applications round\", daysLeft: application_end_ms, started: now >= application_start_ms, completed: now > application_end_ms, progress: now > application_end_ms ? 1 : (now - application_start_ms) / (application_end_ms - application_start_ms) }, { label: \"Matching round\", daysLeft: public_round_end_ms, started: now >= public_round_start_ms, completed: now > public_round_end_ms, progress: now > public_round_end_ms ? 1 : (now - public_round_start_ms) / (public_round_end_ms - public_round_start_ms) }, { label: \"Challenge period\", daysLeft: cooldown_end_ms, started: now >= public_round_end_ms, completed: now > cooldown_end_ms && !!cooldown_end_ms, progress: now > cooldown_end_ms && !!cooldown_end_ms ? 1 : (cooldown_end_ms - now) / (public_round_end_ms - cooldown_end_ms) }, { label: \"Payouts completed\", daysLeft: null, started: null, completed: all_paid_out, progress: all_paid_out ? 1 : 0 }]; return stats;}; const { potDetail: potDetail } = props; const [mobileMenuActive, setMobileMenuActive] = useState(false); const stats = statsList(potDetail); const getIndexOfActive = () => { let index = 0; stats.forEach((state, idx) => { if (state.started && !state.completed) { index = idx; } }); if (index === null) return 3; return index; }; const containerHeight = 181; const showActiveState = getIndexOfActive() * (containerHeight / 4); const Container = styled.div\\` display: flex; width: 100%; justify-content: center; transition: all 300ms ease-in-out; .mobile-selected { display: flex; justify-content: space-between; gap: 1rem; margin: 1rem 0; transition: all 300ms ease-in-out; } @media only screen and (max-width: 1100px) { justify-content: left; height: \\${containerHeight / 4}px; overflow: hidden; .mobile-selected { margin: 10px 0; transform: translateY(\\${-showActiveState}px); flex-direction: column; } } \\`; return <Wrapper onClick={() => setMobileMenuActive(!mobileMenuActive)}> <Container style={mobileMenuActive ? { height: containerHeight + \"px\" } : {}}> <div className=\"mobile-selected\" style={mobileMenuActive ? { transform: \"translateY(0px)\" } : {}}> {stats.map(({ label, daysLeft, progress, started, completed }, idx) => { return <State style={{ color: completed || started ? \"#000\" : \"#7b7b7b\" }} key={label}> <ProgressBar progress={progress} started={started} completed={completed} /> <div> {label} {!daysLeft && started && <span>pending </span>} {started && !completed && daysLeft && <span> ends in <Widget loading=\" \" code={props.alem.componentsCode.TimeLeft} props={{ ...{ daysLeft: daysLeft, ...props } }} /> </span>} {idx === 0 && !started && \" hasn’t started\"} </div> <Loader style={{ background: completed ? \"#629D13\" : \"#dbdbdb\", display: idx === 3 ? \"none\" : \"flex\" }} /> </State>; })} </div> </Container> <svg className=\"spread-indicator\" style={{ rotate: mobileMenuActive ? \"180deg\" : \"0deg\" }} viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.59 0.294922L6 4.87492L1.41 0.294922L0 1.70492L6 7.70492L12 1.70492L10.59 0.294922Z\" fill=\"#7B7B7B\" /> </svg> </Wrapper>; `, ConfigForm: ` const A_49 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; margin-bottom: 24px;\\`;const ModalHeaderLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const IconContainer = styled.div\\` width: 40px; height: 40px; background: #f0f0f0; border-radius: 50%; display: flex; justify-content: center; align-items: center; margin-right: 16px;\\`;const A_50 = styled.svg\\` width: 20px; height: 20px; cursor: pointer; transition: 300ms ease-in-out; :hover { rotate: 180deg; }\\`;const A_51 = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 600;\\`;const ModalDescription = styled.p\\` color: #2e2e2e; font-size: 16px; font-weight: 400;\\`;const A_52 = styled.div\\` height: 24px;\\`;const MembersCount = styled.span\\` color: #2e2e2e; font-weight: 600;\\`;const MembersText = styled.div\\` color: #7b7b7b; font-size: 12px; font-weight: 400;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const ModalMultiAccount = (__props__) => { const { onClose, titleText, descriptionText, unitText, inputValue, onInputChange, handleAddAccount, handleRemoveAccount, accountError, accountIds } = __props__; return <ModalOverlay onOverlayClick={onClose}> <A_49> <ModalHeaderLeft> <IconContainer> <A_50 viewBox=\"0 0 24 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M16.24 7.65C15.07 7.13 13.63 6.75 12 6.75C10.37 6.75 8.93 7.14 7.76 7.65C6.68 8.13 6 9.21 6 10.39V12H18V10.39C18 9.21 17.32 8.13 16.24 7.65ZM8.07 10C8.16 9.77 8.34 9.58 8.56 9.48C9.66 8.99 10.82 8.75 11.99 8.75C13.17 8.75 14.32 9 15.42 9.48C15.65 9.58 15.82 9.77 15.91 10H8.07Z\" fill=\"#151A23\" /> <path d=\"M1.22 8.58C0.48 8.9 0 9.62 0 10.43V12H4.5V10.39C4.5 9.56 4.73 8.78 5.13 8.1C4.76 8.04 4.39 8 4 8C3.01 8 2.07 8.21 1.22 8.58Z\" fill=\"#151A23\" /> <path d=\"M22.78 8.58C21.93 8.21 20.99 8 20 8C19.61 8 19.24 8.04 18.87 8.1C19.27 8.78 19.5 9.56 19.5 10.39V12H24V10.43C24 9.62 23.52 8.9 22.78 8.58Z\" fill=\"#151A23\" /> <path d=\"M12 6C13.66 6 15 4.66 15 3C15 1.34 13.66 0 12 0C10.34 0 9 1.34 9 3C9 4.66 10.34 6 12 6ZM12 2C12.55 2 13 2.45 13 3C13 3.55 12.55 4 12 4C11.45 4 11 3.55 11 3C11 2.45 11.45 2 12 2Z\" fill=\"#151A23\" /> <path d=\"M3.9999 2.49687L1.49677 5L3.9999 7.50313L6.50303 5L3.9999 2.49687Z\" fill=\"#151A23\" /> <path d=\"M20 3L17.5 7H22.5L20 3Z\" fill=\"#151A23\" /> </A_50> </IconContainer> <A_51>{titleText}</A_51> </ModalHeaderLeft> <A_50 viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" onClick={onClose}> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#7B7B7B\" /> </A_50> </A_49> <ModalDescription>{descriptionText}</ModalDescription> <A_62 {...{ placeholder: \"NEAR account ID\", value: inputValue, onChange: onInputChange, postInputChildren: <Button {...{ type: \"primary\", text: \"Add\", onClick: handleAddAccount, style: { borderRadius: \\`0px 4px 4px 0px\\` }, submit: true }} />, handleKeyPress: (e) => { if (e.key === \"Enter\") { handleAddAccount(); } }, error: accountError }} /> <A_52 /> <MembersText> <MembersCount>{accountIds.length} </MembersCount> {accountIds.length == 1 ? unitText : \\`\\${unitText}s\\`} </MembersText> <AccountsList {...{ accountIds, allowRemove: true, handleRemoveAccount }} /> </ModalOverlay>;}; const A_53 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_54 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const Error = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const InputContainer = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_55 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const Input = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`; const DateInput = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_53> {label && <A_54>{label}</A_54>} <InputContainer> {__props__.preInputChildren && __props__.preInputChildren} <Input type={__props__.selectTime ? \"datetime-local\" : \"date\"} placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? (() => {})} style={__props__.inputStyles || {}} /> {__props__.postInputChildren && __props__.postInputChildren} </InputContainer> <Error className={error ? \"show\" : \"\"}>{error}</Error> </A_53>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const MembersListItem = styled.div\\` padding: 16px 0px; border-top: 1px #f0f0f0 solid; display: flex; flex-direction: row; align-items: center; justify-content: space-between;\\`;const MembersListItemLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 16px;\\`;const MembersListItemText = styled.div\\` font-size: 16px; font-weight: 400; color: #2e2e2e;\\`;const RemoveMember = styled.a\\` color: #2e2e2e; font-size: 14px; font-weight: 600; visibility: hidden; cursor: pointer; opacity: 0; transition: opacity 0.2s ease-in-out; &:hover { text-decoration: none; } \\${MembersListItem}:hover & { visibility: visible; opacity: 1; }\\`; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const AccountsList = __props__ => { const { accountIds, allowRemove, handleRemoveAccount } = __props__; return <> {accountIds.map(accountId => { return <MembersListItem> <MembersListItemLeft> <ProfileImage {...{ accountId, style: { width: \"40px\", height: \"40px\", margin: \"0 -8px 0 0\", borderRadius: \"50%\", background: \"white\" }, imageClassName: \"rounded-circle w-100 h-100 d-block\", thumbnail: false, tooltip: true }} /> <MembersListItemText>@{accountId}</MembersListItemText> </MembersListItemLeft> {allowRemove && <RemoveMember onClick={() => handleRemoveAccount(accountId)}>Remove</RemoveMember>} </MembersListItem>; })} </>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const validateNearAddress = address => { const NEAR_ACCOUNT_ID_REGEX = /^(?=.{2,64}$)(?!.*\\\\.\\\\.)(?!.*-$)(?!.*_$)[a-z\\\\d._-]+$/i; let isValid = NEAR_ACCOUNT_ID_REGEX.test(address); if (address.length < 64 && !address.endsWith(\".near\")) { isValid = false; } return isValid;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_63 = styled.div\\` display: flex; flex-direction: column; padding: 32px 0px; width: 100%; @media screen and (max-width: 880px) { padding: 10px 10px; }\\`;const A_64 = styled.div\\` height: 2px; width: 100%; background-color: #ebebeb; @media screen and (max-width: 768px) { display: none; }\\`;const A_65 = styled.div\\` display: flex; flex-direction: row; margin: 48px 0; @media screen and (max-width: 768px) { flex-direction: column; gap: 32px; }\\`;const A_66 = styled.div\\` width: 30%; display: flex; flex-direction: column; gap: 16px; @media screen and (max-width: 768px) { width: 100%; }\\`;const A_67 = styled.div\\` width: 70%; display: flex; flex-direction: column; gap: 26px; @media screen and (max-width: 768px) { width: 100%; }\\`;const A_68 = styled.div\\` color: #2e2e2e; font-size: 16; font-weight: 600; word-wrap: break-word;\\`;const A_69 = styled.div\\` color: #2e2e2e; font-size: 16; font-weight: 400; word-wrap: break-word;\\`;const A_70 = styled.div\\` display: flex; flex-direction: row; gap: 24px; align-items: end; justify-content: flex-start; @media screen and (max-width: 768px) { flex-direction: column; align-items: flex-start; }\\`;const CheckboxWrapper = styled.div\\` display: flex; @media screen and (max-width: 768px) { flex-direction: row; }\\`;const A_71 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const {potDetail: potDetail, style: style} = props; const { potId } = props.alem.useParams(); const { NADABOT_HUMAN_METHOD, ONE_TGAS, NADABOT_CONTRACT_ID, SUPPORTED_FTS: { NEAR } } = constants; const potFactoryContractId = PotFactorySDK.getContractId(); const protocolConfig = PotFactorySDK.getProtocolConfig(); const DEFAULT_REGISTRY_PROVIDER = \\`\\${ListsSDK.getContractId()}:is_registered\\`; const DEFAULT_SYBIL_WRAPPER_PROVIDER = \\`\\${NADABOT_CONTRACT_ID}:\\${NADABOT_HUMAN_METHOD}\\`; const CURRENT_SOURCE_CODE_VERSION = \"0.1.0\"; const SOURCE_CODE_LINK = \"https://github.com/PotLock/core\"; const MAX_POT_NAME_LENGTH = 64; const MAX_POT_DESCRIPTION_LENGTH = 256; const MAX_MAX_PROJECTS = 100; const MAX_REFERRAL_FEE_MATCHING_POOL_BASIS_POINTS = 1000; const MAX_REFERRAL_FEE_PUBLIC_ROUND_BASIS_POINTS = 1000; const MAX_CHEF_FEE_BASIS_POINTS = 1000; Big.PE = 100; const isUpdate = !!potDetail; const convertToUTCTimestamp = localDateTime => { if (!localDateTime) { return; } return new Date(localDateTime).getTime(); }; const formatTimestampForDateTimeLocal = timestamp => { const date = new Date(timestamp); const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, \"0\"); const day = date.getDate().toString().padStart(2, \"0\"); const hours = date.getHours().toString().padStart(2, \"0\"); const minutes = date.getMinutes().toString().padStart(2, \"0\"); return \\`\\${year}-\\${month}-\\${day}T\\${hours}:\\${minutes}\\`; }; State.init({ owner: isUpdate ? potDetail.owner : context.accountId, ownerError: \"\", admin: \"\", admins: isUpdate ? potDetail.admins.map(accountId => ({ accountId })) : [], adminsError: \"\", isAdminsModalOpen: false, name: isUpdate ? potDetail.pot_name : \"\", nameError: \"\", customHandle: isUpdate ? potId.split(\\`.\\${potFactoryContractId}\\`)[0] : \"\", customHandleError: \"\", description: isUpdate ? potDetail.pot_description : \"\", descriptionError: \"\", referrerFeeMatchingPoolPercent: isUpdate ? potDetail.referral_fee_matching_pool_basis_points / 100 : \"\", referrerFeeMatchingPoolPercentError: \"\", referrerFeePublicRoundPercent: isUpdate ? potDetail.referral_fee_public_round_basis_points / 100 : \"\", referrerFeePublicRoundPercentError: \"\", protocolFeeBasisPoints: isUpdate ? potDetail.protocol_fee_basis_points : \"\", protocolFeeBasisPointsError: \"\", applicationStartDate: isUpdate ? formatTimestampForDateTimeLocal(potDetail.application_start_ms) : \"\", applicationStartDateError: \"\", applicationEndDate: isUpdate ? formatTimestampForDateTimeLocal(potDetail.application_end_ms) : \"\", applicationEndDateError: \"\", matchingRoundStartDate: isUpdate ? formatTimestampForDateTimeLocal(potDetail.public_round_start_ms) : \"\", matchingRoundStartDateError: \"\", matchingRoundEndDate: isUpdate ? formatTimestampForDateTimeLocal(potDetail.public_round_end_ms) : \"\", matchingRoundEndDateError: \"\", chef: isUpdate ? potDetail.chef : \"\", chefError: \"\", chefFeePercent: isUpdate ? potDetail.chef_fee_basis_points / 100 : \"\", chefFeePercentError: \"\", maxProjects: isUpdate ? potDetail.max_projects : \"\", maxProjectsError: \"\", baseCurrency: isUpdate ? potDetail.base_currency : \"\", baseCurrencyError: \"\", minMatchingPoolDonationAmount: NEAR.fromIndivisible(isUpdate ? potDetail.min_matching_pool_donation_amount : \"1\"), minMatchingPoolDonationAmountError: \"\", useNadabotSybil: isUpdate ? potDetail.sybil_wrapper_provider == DEFAULT_SYBIL_WRAPPER_PROVIDER : true, usePotlockRegistry: isUpdate ? potDetail.registry_provider == DEFAULT_REGISTRY_PROVIDER : true, latestSourceCodeCommitHash: \"\", deploymentSuccess: false }); if (!isUpdate && !state.latestSourceCodeCommitHash) { const res = fetch(\"https://api.github.com/repos/PotLock/core/commits\"); if (res.ok && res.body.length > 0) { State.update({ latestSourceCodeCommitHash: res.body[0].sha }); } } const getPotDetailArgsFromState = () => { const args = { owner: state.owner, admins: state.admins.filter(admin => !admin.remove).map(admin => admin.accountId), chef: state.chef || null, pot_name: state.name, pot_description: state.description, max_projects: parseInt(state.maxProjects) || null, application_start_ms: convertToUTCTimestamp(state.applicationStartDate), application_end_ms: convertToUTCTimestamp(state.applicationEndDate), public_round_start_ms: convertToUTCTimestamp(state.matchingRoundStartDate), public_round_end_ms: convertToUTCTimestamp(state.matchingRoundEndDate), min_matching_pool_donation_amount: NEAR.toIndivisible(state.minMatchingPoolDonationAmount).toString(), registry_provider: state.usePotlockRegistry ? DEFAULT_REGISTRY_PROVIDER : null, sybil_wrapper_provider: state.useNadabotSybil ? DEFAULT_SYBIL_WRAPPER_PROVIDER : null, custom_sybil_checks: null, custom_min_threshold_score: null, referral_fee_matching_pool_basis_points: parseInt((state.referrerFeeMatchingPoolPercent * 100).toFixed(0)), referral_fee_public_round_basis_points: parseInt((state.referrerFeePublicRoundPercent * 100).toFixed(0)), chef_fee_basis_points: parseInt((state.chefFeePercent * 100).toFixed(0)), source_metadata: isUpdate ? null : { version: CURRENT_SOURCE_CODE_VERSION, commit_hash: state.latestSourceCodeCommitHash, link: SOURCE_CODE_LINK } }; return args; }; const canDeploy = useMemo(() => { if (!state.owner || state.ownerError || !state.name || state.nameError || !state.description || state.descriptionError || !state.referrerFeeMatchingPoolPercent || state.referrerFeeMatchingPoolPercentError || !state.applicationStartDate || state.applicationStartDateError || !state.applicationEndDate || state.applicationEndDateError || !state.matchingRoundStartDate || state.matchingRoundStartDateError || !state.matchingRoundEndDate || state.matchingRoundEndDateError || !state.chef || state.chefError || !state.chefFeePercent || state.chefFeePercentError || !state.maxProjects || state.maxProjectsError) { return false; } return true; }, [state]); const handleDeploy = () => { const deployArgs = getPotDetailArgsFromState(); console.log(\"deployArgs: \", deployArgs); Near.asyncView(potFactoryContractId, \"calculate_min_deployment_deposit\", { args: deployArgs }).then(amount => { const amountYoctos = Big(amount).plus(Big(\"20000000000000000000000\")); const args = { pot_args: deployArgs }; if (state.customHandle) { args.pot_handle = state.customHandle; } const transactions = [{ contractName: potFactoryContractId, methodName: \"deploy_pot\", deposit: amountYoctos, args, gas: ONE_TGAS.mul(300) }]; const now = Date.now(); Near.call(transactions); const pollIntervalMs = 1000; const pollId = setInterval(() => { PotFactorySDK.asyncGetPots().then(pots => { const pot = pots.find(pot => pot.deployed_by === context.accountId && pot.deployed_at_ms > now); if (pot) { clearInterval(pollId); State.update({ deploymentSuccess: true }); } }); }, pollIntervalMs); }); }; const handleUpdate = () => { const updateArgs = getPotDetailArgsFromState(); const depositFloat = JSON.stringify(updateArgs).length * 0.00003; const deposit = Big(depositFloat).mul(Big(10).pow(24)); const transactions = [{ contractName: potId, methodName: \"admin_dangerously_set_pot_config\", deposit, args: { update_args: updateArgs }, gas: ONE_TGAS.mul(100) }]; Near.call(transactions); }; const validateAndUpdatePercentages = (percent, stateKey, errorKey, maxVal) => { const updates = { [errorKey]: \"\" }; if (!percent) { updates[stateKey] = \"0\"; } else { const split = percent.split(\".\"); if (split.length > 2) { return; } if (split.length === 2 && split[1].length > 2) { return; } if (percent.endsWith(\".\") && percent.indexOf(\".\") === percent.length - 1) { State.update({ [stateKey]: percent }); return; } const percentFloat = parseFloat(percent); if (percentFloat) { updates[stateKey] = percentFloat.toString(); if (percentFloat > maxVal) { updates[errorKey] = \\`Maximum \\${maxVal}%\\`; } } } State.update(updates); }; const handleAddAdmin = () => { let isValid = validateNearAddress(state.admin); if (!isValid) { State.update({ adminsError: \"Invalid NEAR account ID\" }); return; } if (!state.admins.find(admin => admin.accountId == state.admin && !admin.remove)) { const newAdmin = { accountId: state.admin.toLowerCase() }; const admins = [...state.admins, newAdmin]; State.update({ admins, admin: \"\", adminsError: \"\" }); } }; const handleRemoveAdmin = accountId => { State.update({ admins: state.admins.map(admin => { if (admin.accountId == accountId) { return { ...admin, remove: true }; } return admin; }) }); }; const userIsOwner = context.accountId === potDetail?.owner; const userIsAdmin = isUpdate && potDetail.admins.includes(context.accountId || \"\"); const isAdminOrGreater = userIsOwner || userIsAdmin; const FormSectionLeft = (title, description) => { return <A_66> <A_68>{title}</A_68> <A_69>{description}</A_69> </A_66>; }; return <A_63> <A_65> {FormSectionLeft(\"Pot details\", \"\")} <A_67> <A_62 {...{ label: \"Owner *\", placeholder: \\`E.g. \\${context.accountId}\\`, value: state.owner, onChange: owner => State.update({ owner, ownerError: \"\" }), validate: () => { const valid = validateNearAddress(state.owner); State.update({ ownerError: valid ? \"\" : \"Invalid NEAR account ID\" }); }, error: state.ownerError, disabled: isUpdate ? !userIsOwner : true }} /> <A_71>Admins</A_71> <AccountsList {...{ accountIds: state.admins.filter(account => !account.remove).map(account => account.accountId), allowRemove: isUpdate ? userIsOwner : true, handleRemoveAccount: handleRemoveAdmin }} /> {(!isUpdate || userIsOwner) && <Button {...{ type: \"tertiary\", text: \"Add admins\", style: { width: \"fit-content\" }, onClick: () => State.update({ isAdminsModalOpen: true }) }} />} <A_62 {...{ label: \"Name *\", placeholder: \"E.g. DeFi Center\", value: state.name, onChange: name => State.update({ name, nameError: \"\" }), validate: () => { const valid = state.name.length <= MAX_POT_NAME_LENGTH; State.update({ nameError: valid ? \"\" : \\`Name must be \\${MAX_POT_NAME_LENGTH} characters or less\\` }); }, error: state.nameError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_62 {...{ label: \"Custom handle (optional - will slugify name by default)\", placeholder: \"e.g. my-pot-handle\", value: state.customHandle, onChange: customHandle => State.update({ customHandle, customHandleError: \"\" }), validate: () => { const suffix = \\`.\\${potFactoryContractId}\\`; const fullAddress = \\`\\${state.customHandle}\\${suffix}\\`; let customHandleError = \"\"; if (fullAddress.length > 64) { customHandleError = \\`Handle must be \\${64 - suffix.length} characters or less\\`; } else { const valid = validateNearAddress(fullAddress); customHandleError = valid ? \"\" : \\`Invalid handle (can only contain lowercase alphanumeric symbols + _ or -)\\`; } State.update({ customHandleError }); }, error: state.customHandleError, disabled: isUpdate }} /> <TextArea {...{ label: \"Description\", placeholder: \"Type description\", value: state.description, onChange: description => State.update({ description }), validate: () => { const valid = state.description.length <= MAX_POT_DESCRIPTION_LENGTH; State.update({ descriptionError: valid ? \"\" : \\`Description must be \\${MAX_POT_DESCRIPTION_LENGTH} characters or less\\` }); }, error: state.descriptionError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_70> <A_62 {...{ label: \"Referrer fee % (matching pool)\", placeholder: \"0\", percent: true, value: state.referrerFeeMatchingPoolPercent, onChange: percent => { validateAndUpdatePercentages(percent, \"referrerFeeMatchingPoolPercent\", \"referrerFeeMatchingPoolPercentError\", MAX_REFERRAL_FEE_MATCHING_POOL_BASIS_POINTS / 100); }, validate: () => {}, error: state.referrerFeeMatchingPoolPercentError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_62 {...{ label: \"Referrer fee % (public round)\", placeholder: \"0\", percent: true, value: state.referrerFeePublicRoundPercent, onChange: percent => { validateAndUpdatePercentages(percent, \"referrerFeePublicRoundPercent\", \"referrerFeePublicRoundPercentError\", MAX_REFERRAL_FEE_PUBLIC_ROUND_BASIS_POINTS / 100); }, validate: () => {}, error: state.referrerFeeMatchingPoolPercentError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_62 {...{ label: \"Protocol fee %\", value: protocolConfig ? \\`\\${protocolConfig.basis_points / 100}\\` : \"-\", disabled: true, percent: true }} /> </A_70> <DateInput {...{ label: \"Application start date\", selectTime: true, value: state.applicationStartDate, onChange: date => { State.update({ applicationStartDate: date }); }, validate: () => { const now = new Date().getTime(); const applicationStartDate = new Date(state.applicationStartDate).getTime(); const applicationEndDate = new Date(state.applicationEndDate).getTime(); const valid = applicationStartDate > now && (!applicationEndDate || applicationStartDate < applicationEndDate); State.update({ applicationStartDateError: valid ? \"\" : \"Invalid application start date\" }); }, error: state.applicationStartDateError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <DateInput {...{ label: \"Application end date\", selectTime: true, value: state.applicationEndDate, onChange: date => State.update({ applicationEndDate: date }), validate: () => { const valid = (!state.matchingRoundStartDate || state.applicationEndDate < state.matchingRoundStartDate) && (!state.applicationStartDate || state.applicationEndDate > state.applicationStartDate); State.update({ applicationEndDateError: valid ? \"\" : \"Invalid application end date\" }); }, error: state.applicationEndDateError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <DateInput {...{ label: \"Matching round start date\", selectTime: true, value: state.matchingRoundStartDate, onChange: date => State.update({ matchingRoundStartDate: date }), validate: () => { const valid = (!state.applicationEndDate || state.matchingRoundStartDate > state.applicationEndDate) && (!state.matchingRoundEndDate || state.matchingRoundStartDate < state.matchingRoundEndDate); State.update({ matchingRoundStartDateError: valid ? \"\" : \"Invalid round start date\" }); }, error: state.matchingRoundStartDateError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <DateInput {...{ label: \"Matching round end date\", selectTime: true, value: state.matchingRoundEndDate, onChange: date => State.update({ matchingRoundEndDate: date }), validate: () => { const valid = !state.matchingRoundStartDate || state.matchingRoundEndDate > state.matchingRoundStartDate; State.update({ matchingRoundEndDateError: valid ? \"\" : \"Invalid round end date\" }); }, error: state.matchingRoundEndDateError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_70> <A_62 {...{ label: \"Optional: Min matching pool donation amount (in NEAR)\", placeholder: \"0\", value: state.minMatchingPoolDonationAmount, onChange: amountNear => { State.update({ minMatchingPoolDonationAmount: amountNear }); }, validate: () => {}, error: state.referrerFeeMatchingPoolPercentError, disabled: isUpdate ? !isAdminOrGreater : false }} /> </A_70> </A_67> </A_65> {} <A_65> {FormSectionLeft(\"Chef details\", \"\")} <A_67> <A_70> <A_62 {...{ label: \"Assign chef\", placeholder: \"E.g. user.near\", value: state.chef, onChange: chef => State.update({ chef }), validate: () => { const valid = validateNearAddress(state.chef); State.update({ chefError: valid ? \"\" : \"Invalid NEAR account ID\" }); }, error: state.chefError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_62 {...{ label: \"Chef fee %\", placeholder: \"0\", percent: true, value: state.chefFeePercent, onChange: percent => { validateAndUpdatePercentages(percent, \"chefFeePercent\", \"chefFeePercentError\", MAX_CHEF_FEE_BASIS_POINTS / 100); }, validate: () => {}, error: state.chefFeePercentError, disabled: isUpdate ? !isAdminOrGreater : false }} /> </A_70> </A_67> </A_65> {} <A_65> {FormSectionLeft(\"Application details\", \"\")} <A_67> <A_62 {...{ label: \"Max. approved projects\", placeholder: \"e.g. 20\", value: state.maxProjects, onChange: maxProjects => State.update({ maxProjects }), validate: () => { const valid = parseInt(state.maxProjects) <= MAX_MAX_PROJECTS; State.update({ maxProjectsError: valid ? \"\" : \\`Maximum \\${MAX_MAX_PROJECTS}\\` }); }, error: state.maxProjectsError, disabled: isUpdate ? !isAdminOrGreater : false }} /> </A_67> </A_65> <A_65> {FormSectionLeft(\"Project Registration\", \"\")} <A_67> <A_70> <CheckboxWrapper> <CheckBox {...{ id: \"registrationSelector\", checked: state.usePotlockRegistry, onClick: e => { State.update({ usePotlockRegistry: e.target.checked }); }, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_71 htmlFor=\"sybilSelector\">Require approval on PotLock registry (recommended)</A_71> </CheckboxWrapper> </A_70> </A_67> </A_65> <A_65> {FormSectionLeft(\"Donor Sybil Resistance\", \"\")} <A_67> <A_70> <CheckboxWrapper> <CheckBox {...{ id: \"sybilSelector\", checked: state.useNadabotSybil, onClick: e => { State.update({ useNadabotSybil: e.target.checked }); }, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_71 htmlFor=\"sybilSelector\">🤖 nada.bot human verification (recommended)</A_71> </CheckboxWrapper> </A_70> <A_70 style={{ justifyContent: \"flex-end\", marginTop: \"36px\" }}> {!isUpdate && isAdminOrGreater && <Button {...{ type: \"tertiary\", text: \"Cancel\", style: style || {}, onClick: () => {} }} />} {(isUpdate && isAdminOrGreater || !isUpdate) && <Button {...{ type: \"primary\", text: isUpdate ? \"Save changes\" : \"Deploy\", style: style || {}, onClick: isUpdate ? handleUpdate : handleDeploy }} />} </A_70> </A_67> </A_65> {state.isAdminsModalOpen && <ModalMultiAccount {...{ onClose: () => State.update({ isAdminsModalOpen: false }), titleText: \"Add admins\", descriptionText: \"Add NEAR account IDs for your admins.\", inputValue: state.admin, onInputChange: admin => { State.update({ admin, adminsError: \"\" }); }, handleAddAccount: handleAddAdmin, handleRemoveAccount: handleRemoveAdmin, accountError: state.adminsError, accountIds: state.admins.map(admin => admin.accountId), unitText: \"admin\" }} />} </A_63>; `, Settings: ` const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_72 = styled.div\\` display: flex; flex-direction: column; width: 100%;\\`;const A_73 = styled.div\\` font-weight: 600; font-size: 22px;\\`;const PrviewContainer = styled.div\\` display: flex; flex-direction: column; max-width: 922px;\\`;const AdminsWrapper = styled.div\\` position: absolute; display: flex; flex-direction: column; opacity: 0; pointer-events: none; padding-top: 5px; transition: all 300ms; top: 100%; .list { background: white; color: #292929; border-radius: 4px; overflow: hidden; box-shadow: 0px 0px 1px 0px rgba(41, 41, 41, 0.74), 0px 3px 3px 0px rgba(123, 123, 123, 0.12), 0px 6px 6px 0px rgba(123, 123, 123, 0.12); a { display: flex; align-items: center; gap: 0.5rem; padding: 12px 16px; color: #292929; transition: 300ms; .profile-image { width: 24px; height: 24px; box-shadow: 0px 0px 1px 0px #a6a6a6 inset; border: 2px solid #f8d3b0; border-radius: 50%; } &:hover { background: #292929; text-decoration: none; color: white; } } } .tip-icon { display: flex; justify-content: center; z-index: 1; svg { stroke: rgb(41 41 41 / 21%); } }\\`;const Admins = styled.div\\` display: flex; font-size: 11px; gap: 2rem; .owner { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; .address { display: flex; gap: 0.5rem; align-items: center; div { font-size: 14px; font-weight: 500; } .profile-image { width: 24px; height: 24px; border-radius: 50%; } } } .admins { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; .avaters { display: flex; gap: 0.5rem; } .profile-image { width: 24px; height: 24px; } .icons-tolltip { position: relative; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; color: white; border-radius: 50%; border: 2px solid #f8d3b0; background: #b8182d; &:hover { \\${AdminsWrapper} { opacity: 1; pointer-events: all; } } } } .edit { display: flex; gap: 0.5rem; color: #dd3345; align-items: center; cursor: pointer; margin-left: auto; } @media only screen and (max-width: 768px) { flex-wrap: wrap; .edit { width: 100%; } }\\`;const Detail = styled.div\\` display: flex; flex-direction: column; gap: 1rem; border-radius: 12px; border: 1px solid #c7c7c7; padding: 3rem; margin-top: 1.5rem; .row-field { display: flex; align-items: center; gap: 2rem; font-size: 14px; } .label { font-weight: 500; max-width: 238px; width: 100%; text-align: left; } .input { color: #7b7b7b; } @media only screen and (max-width: 768px) { padding: 1rem; gap: 1.5rem; .row-field { flex-direction: column; align-items: flex-start; gap: 4px; } }\\`; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const A_74 = (timestamp) => { const date = new Date(timestamp); const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, \"0\"); const day = date.getDate().toString().padStart(2, \"0\"); const hours = date.getHours().toString().padStart(2, \"0\"); const minutes = date.getMinutes().toString().padStart(2, \"0\"); return \\`\\${year}-\\${month}-\\${day}T\\${hours}:\\${minutes}\\`;}; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const getFields = (potId, potDetail) => { const { owner, chef, admins, pot_name, pot_description, max_projects, application_start_ms, application_end_ms, public_round_start_ms, public_round_end_ms, sybil_wrapper_provider, referral_fee_matching_pool_basis_points, referral_fee_public_round_basis_points, chef_fee_basis_points, min_matching_pool_donation_amount, registry_provider } = potDetail; const { SUPPORTED_FTS: { NEAR } } = constants; const potFactoryContractId = PotFactorySDK.getContractId(); return [{ label: \"Name\", val: pot_name }, { label: \"Custom handle\", val: potId.split(\\`.\\${potFactoryContractId}\\`)[0] }, { label: \"Description\", val: pot_description }, { label: \"Referrer fee % (matching pool)\", val: referral_fee_matching_pool_basis_points / 100 + \"%\" }, { label: \"Referrer fee % (public round)\", val: referral_fee_public_round_basis_points / 100 + \"%\" }, { label: \"Application date\", val: \\`\\${A_74(application_start_ms)} - \\${A_74(application_end_ms)}\\` }, { label: \"Matching round date\", val: \\`\\${A_74(public_round_start_ms)} - \\${A_74(public_round_end_ms)}\\` }, { label: \"Min matching pool donation\", val: NEAR.fromIndivisible(min_matching_pool_donation_amount) }, { label: \"Chef fee\", val: chef_fee_basis_points / 100 + \"%\" }, { label: \"Assigned Chef\", val: chef }, { label: \"Max. approved projects\", val: max_projects }, { label: \"Registry Provider\", val: registry_provider }, { label: \"Donor Sybil Resistance\", val: sybil_wrapper_provider ? \"🤖 nada.bot human verified\" : \"none\" }];}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const { potDetail: potDetail } = props; const { owner, admins } = potDetail; const { potId } = props.alem.useParams(); const [editSettings, setEditSettings] = useState(false); const userIsAdminOrGreater = PotSDK.isUserPotAdminOrGreater(potId, context.accountId); const fields = getFields(potId, potDetail); const AdminsTooltip = () => <AdminsWrapper> <div className=\"tip-icon\"> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 5.24537e-07L-2.54292e-07 8L12 8L6 5.24537e-07Z\" fill=\"white\" /> </svg> </div> <div className=\"list\"> {admins.slice(0, admins.length).map((admin) => <a href={hrefWithParams(\\`?tab=profile&accountId=\\${admin}\\`)} target=\"_blank\"> <ProfileImage style={{}} className=\"profile-image\" accountId={admin} /> <div>{admin}</div> </a>)} </div> </AdminsWrapper>; return editSettings ? <A_72> <A_73>Edit Pot settings</A_73> <Widget loading=\" \" code={props.alem.componentsCode.ConfigForm} props={{ ...{ potDetail: potDetail, ...props } }} /> </A_72> : <PrviewContainer> <Admins> <div className=\"owner\"> <div>Owner</div> <div className=\"address\"> <ProfileImage style={{}} className=\"profile-image\" accountId={owner} /> <div>{_address(owner, 15)}</div> </div> </div> {admins.length > 0 && <div className=\"admins\"> <div>Admins</div> <div className=\"avaters\"> {admins.slice(0, 4).map((admin, idx) => <OverlayTrigger placement=\"bottom\" overlay={<Tooltip id={\\`tooltip-\\${idx}\\`}>{admin}</Tooltip>} key={admin}> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${admin}\\`)} target=\"_blank\"> <ProfileImage style={{}} className=\"profile-image\" accountId={admin} /> </a> </OverlayTrigger>)} {admins.length > 4 && <div className=\"icons-tolltip\"> +{admins.length - 4} <AdminsTooltip /> </div>} </div> </div>} {userIsAdminOrGreater && <div className=\"edit\" onClick={() => setEditSettings(true)}> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0.25 13.7501H3.0625L11.3575 5.45508L8.545 2.64258L0.25 10.9376V13.7501ZM1.75 11.5601L8.545 4.76508L9.235 5.45508L2.44 12.2501H1.75V11.5601Z\" fill=\"#DD3345\" /> <path d=\"M11.7777 0.469375C11.4852 0.176875 11.0127 0.176875 10.7202 0.469375L9.34766 1.84187L12.1602 4.65438L13.5327 3.28187C13.8252 2.98937 13.8252 2.51688 13.5327 2.22438L11.7777 0.469375Z\" fill=\"#DD3345\" /> </svg> Edit Pot </div>} </Admins> <Detail> {fields.map((field) => <div className=\"row-field\" key={field.label}> <div className=\"label\">{field.label}</div> <div className=\"input\">{field.val ?? \"-\"}</div> </div>)} </Detail> </PrviewContainer>; `, ChallangeResolveModal: ` const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_75 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; background: white; padding: 24px 24px 12px 24px; border-top-left-radius: 6px; border-top-right-radius: 6px; font-weight: 500;\\`;const A_76 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 24px; border-top: 1px #f0f0f0 solid; background: #fafafa; gap: 8px;\\`;const A_77 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-end; background: #fafafa; padding: 12px 24px 24px 24px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; gap: 24px; width: 100%;\\`;const HeaderItemText = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const {adminModalChallengerId: adminModalChallengerId, onClose: onClose} = props; State.init({ challengeAdminNotes: \"\", challengeAdminNotesError: \"\", resolveChallenge: false }); const { challengeAdminNotes, challengeAdminNotesError, resolveChallenge } = state; const { potId } = props.alem.useParams(); const payoutsChallenges = PotSDK.getPayoutsChallenges(potId); const MAX_CHALLENGE_TEXT_LENGTH = 1000; const handleAdminUpdateChallenge = () => { PotSDK.adminUpdatePayoutsChallenge(potId, adminModalChallengerId, challengeAdminNotes, resolveChallenge); State.update({ challengeAdminNotes: \"\", challengeAdminNotesError: \"\", resolveChallenge: false }); onClose(); }; const handleCancelAdminUpdateChallenge = () => { State.update({ challengeAdminNotes: \"\", challengeAdminNotesError: \"\", resolveChallenge: false }); onClose(); }; return <ModalOverlay onOverlayClick={onClose}> <A_75>Update Challenge from {adminModalChallengerId}</A_75> <A_76> <HeaderItemText>Challenge Reason:</HeaderItemText> <div> {payoutsChallenges.find(challenge => challenge.challenger_id === adminModalChallengerId).reason} </div> <TextArea {...{ noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Respond to the challenge here\", value: challengeAdminNotes, onChange: challengeAdminNotes => State.update({ challengeAdminNotes }), validate: () => { if (challengeAdminNotes.length > MAX_CHALLENGE_TEXT_LENGTH) { State.update({ challengeAdminNotesError: \\`Notes must be less than \\${MAX_CHALLENGE_TEXT_LENGTH} characters\\` }); return; } State.update({ challengeAdminNotesError: \"\" }); }, error: challengeAdminNotesError }} /> <CheckBox {...{ label: \"Resolve this challenge?\", checked: resolveChallenge, onClick: e => { State.update({ resolveChallenge: e.target.checked }); } }} /> </A_76> <A_77> <Button {...{ type: \"tertiary\", text: \"Cancel\", onClick: handleCancelAdminUpdateChallenge }} /> <Button {...{ type: \"primary\", text: \"Submit\", disabled: !challengeAdminNotes || !!challengeAdminNotesError, onClick: handleAdminUpdateChallenge }} /> </A_77> </ModalOverlay>; `, PayoutsChallenges: ` const AdminIcon = __props__ => <svg {...__props__} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <mask id=\"path-1-inside-1_10044_14103\" fill=\"white\"> <path d=\"M0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12Z\" /> </mask> <path d=\"M0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12Z\" fill=\"#656565\" /> <path d=\"M-0.666667 12C-0.666667 5.00439 5.00439 -0.666667 12 -0.666667C18.9956 -0.666667 24.6667 5.00439 24.6667 12H23.3333C23.3333 5.74077 18.2592 0.666667 12 0.666667C5.74077 0.666667 0.666667 5.74077 0.666667 12H-0.666667ZM24.6667 12.6667C24.6667 19.6623 18.9956 25.3333 12 25.3333C5.00439 25.3333 -0.666667 19.6623 -0.666667 12.6667L0.666667 12C0.666667 17.891 5.74077 22.6667 12 22.6667C18.2592 22.6667 23.3333 17.891 23.3333 12L24.6667 12.6667ZM12 25.3333C5.00439 25.3333 -0.666667 19.6623 -0.666667 12.6667V12C-0.666667 5.00439 5.00439 -0.666667 12 -0.666667V0.666667C5.74077 0.666667 0.666667 5.74077 0.666667 12C0.666667 17.891 5.74077 22.6667 12 22.6667V25.3333ZM12 -0.666667C18.9956 -0.666667 24.6667 5.00439 24.6667 12V12.6667C24.6667 19.6623 18.9956 25.3333 12 25.3333V22.6667C18.2592 22.6667 23.3333 17.891 23.3333 12C23.3333 5.74077 18.2592 0.666667 12 0.666667V-0.666667Z\" fill=\"#292929\" mask=\"url(#path-1-inside-1_10044_14103)\" /> <path d=\"M9.20029 12.7527L11.087 10.866L6.40695 6.19268C5.36695 7.23268 5.36695 8.91935 6.40695 9.96601L9.20029 12.7527ZM13.7203 11.546C14.7403 12.0193 16.1736 11.686 17.2336 10.626C18.507 9.35268 18.7536 7.52601 17.7736 6.54601C16.8003 5.57268 14.9736 5.81268 13.6936 7.08601C12.6336 8.14601 12.3003 9.57935 12.7736 10.5993L6.26695 17.106L7.20695 18.046L11.8003 13.466L16.387 18.0527L17.327 17.1127L12.7403 12.526L13.7203 11.546Z\" fill=\"white\" /> </svg>; const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_78 = styled.div\\` display: flex; flex-direction: column;\\`;const A_79 = styled.div\\` font-size: 18px; display: flex; align-items: center; gap: 1.5rem; width: fit-content; cursor: pointer; margin-bottom: 1.5rem; div:first-of-type { font-weight: 600; } svg { rotate: 180deg; transition: all 300ms ease-in-out; }\\`;const A_80 = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; transition: max-height 400ms ease-in-out; overflow: hidden; max-height: 1000px; opacity: 1; &.hidden { opacity: 0; max-height: 0; }\\`;const Challenge = styled.div\\` display: flex; padding: 1rem; border-bottom: 1px solid #c7c7c7; font-size: 14px; &:last-of-type { border-bottom: none; } .vertical-line { height: 100%; background: #c7c7c7; width: 1px; transform: translateX(0.75rem); z-index: -1; } .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0.5rem; } .admin-icon { margin-right: 0.75rem; svg { width: 1.5rem; height: 1.5rem; } } .content { display: flex; flex-direction: column; } .header { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; } .id { font-weight: 600; color: #292929; transition: 200ms; :hover { text-decoration: none; color: #dd3345; } } .title { font-weight: 600; color: #8b5af8; } .reason { color: #7b7b7b; margin-top: 0.5rem; margin-bottom: 1rem; padding-left: 2.5rem; background: white; } .admin-header { display: flex; align-items: center; gap: 0.25rem; } .dot { background: #7b7b7b; width: 5px; height: 5px; border-radius: 50%; } .resolved-state { font-weight: 600; } .resolve-btn { cursor: pointer; background: none; border: none; } @media only screen and (max-width: 480px) { .profile-image { margin-right: 0; } .admin-icon { margin-right: 0.25rem; } .reason { padding-left: 2rem; } .date { width: 100%; margin-top: -0.5rem; padding-left: 2rem; } }\\`;const A_81 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; background: white; padding: 24px 24px 12px 24px; border-top-left-radius: 6px; border-top-right-radius: 6px; font-weight: 500;\\`;const A_82 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 24px; border-top: 1px #f0f0f0 solid; background: #fafafa; gap: 8px;\\`;const A_83 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-end; background: #fafafa; padding: 12px 24px 24px 24px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; gap: 24px; width: 100%;\\`;const A_84 = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const Line = styled.div\\` width: 100%; height: 1px; background: #c7c7c7; margin: 3rem 0;\\`; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const { potId } = props.alem.useParams(); const payoutsChallenges = PotSDK.getPayoutsChallenges(potId); const userIsAdminOrGreater = PotSDK.isUserPotAdminOrGreater(potId, context.accountId); State.init({ adminModalChallengerId: \"\", toggleChallenges: false }); const { adminModalChallengerId, toggleChallenges } = state; return !payoutsChallenges ? \"Loading...\" : payoutsChallenges.length === 0 ? \"\" : <> <A_78> <A_79 onClick={() => State.update({ toggleChallenges: !toggleChallenges })}> <div>Payout Challenges</div> <div>{payoutsChallenges?.length}</div> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" style={{ rotate: toggleChallenges ? \"0deg\" : \"180deg\" }} fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294922L0 6.29492L1.41 7.70492L6 3.12492L10.59 7.70492L12 6.29492L6 0.294922Z\" fill=\"#151A23\" /> </svg> </A_79> <A_80 className={\\`\\${!toggleChallenges ? \"hidden\" : \"\"}\\`}> {payoutsChallenges.map(({ challenger_id, admin_notes, created_at, reason, resolved }) => <Challenge key={challenger_id}> {} <div className=\"content\"> <div className=\"header\"> <ProfileImage className=\"profile-image\" accountId={challenger_id} /> <a className=\"id\" href={hrefWithParams(\\`?tab=profile&accountId=\\${challenger_id}\\`)}> {challenger_id} </a> <div className=\"title\">Challenged payout</div> <div className=\"date\"> {getTimePassed(created_at)}</div> </div> <div className=\"reason\">{reason}</div> <div className=\"admin-header\"> <div className=\"admin-icon\"> <AdminIcon /> </div> <div className=\"resolved-state\" style={{ color: resolved ? \"#4a7714\" : \"#C7C7C7\" }}> {resolved ? \"Resolved\" : \"Unresolved\"} </div> {resolved ? <> <div className=\"dot\" /> <div>1 Response</div> </> : userIsAdminOrGreater ? <> <div className=\"dot\" /> <button className=\"resolve-btn\" onClick={() => State.update({ adminModalChallengerId: challenger_id })}> Reply </button> </> : \"\"} </div> <div className=\"reason\">{admin_notes}</div> </div> </Challenge>)} </A_80> {} {adminModalChallengerId && <Widget loading=\" \" code={props.alem.componentsCode.ChallangeResolveModal} props={{ ...{ adminModalChallengerId: adminModalChallengerId, onClose: () => State.update({ adminModalChallengerId: \"\" }), ...props } }} />} </A_78> <Line /> </>; `, FlaggedAccounts: ` const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_85 = styled.div\\` width: 100%; height: 1px; background: #c7c7c7; margin: 3rem 0;\\`;const A_86 = styled.div\\` display: flex; flex-direction: column; width: 100%;\\`;const A_87 = styled.div\\` font-size: 18px; display: flex; align-items: center; gap: 1.5rem; width: fit-content; cursor: pointer; margin-bottom: 1.5rem; div:first-of-type { font-weight: 600; } svg { rotate: 180deg; transition: all 300ms ease-in-out; }\\`;const A_88 = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; transition: max-height 400ms ease-in-out; overflow: hidden; max-height: 1000px; overflow-y: scroll; opacity: 1; &.hidden { opacity: 0; max-height: 0; }\\`;const Flag = styled.div\\` display: flex; padding: 1rem; border-bottom: 1px solid #c7c7c7; font-size: 14px; &:last-of-type { border-bottom: none; } .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0.5rem; } .content { display: flex; flex-direction: column; } .header { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; } .id { font-weight: 600; color: #292929; a { transition: 200ms; font-weight: 600; color: #292929; :hover { text-decoration: none; color: #dd3345; } } } .flagged-account { color: #ed464f; font-weight: 600; :hover { text-decoration: none; } } .role { color: #656565; font-weight: 600; } .reason { color: #656565; margin-top: 0.5rem; margin-bottom: 1rem; padding-left: 2.5rem; background: white; } .dot { background: #656565; width: 5px; height: 5px; border-radius: 50%; } @media only screen and (max-width: 480px) { .profile-image { margin-right: 0; } .reason { padding-left: 2rem; } }\\`; const {potDetail: potDetail} = props; const { potId } = props.alem.useParams(); const [flaggedAddresses, setFlaggedAddresses] = useState(null); const [toggleView, setToggleView] = useState(null); if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then(data => { const listOfFlagged = []; data.forEach(adminFlaggedAcc => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); addresses.forEach(address => { listOfFlagged.push({ address: address, reason: adminFlaggedAcc.potFlaggedAcc[address], flaggedBy: adminFlaggedAcc.flaggedBy, role: adminFlaggedAcc.role }); }); }); setFlaggedAddresses(listOfFlagged); }).catch(err => console.log(\"error getting the flagged accounts \", err)); } return !flaggedAddresses ? \"Loading...\" : flaggedAddresses.length === 0 ? \"\" : <> <A_86> <A_87 onClick={() => setToggleView(!toggleView)}> <div>Flagged Accounts</div> <div>{flaggedAddresses?.length}</div> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" style={{ rotate: toggleView ? \"0deg\" : \"180deg\" }} fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294922L0 6.29492L1.41 7.70492L6 3.12492L10.59 7.70492L12 6.29492L6 0.294922Z\" fill=\"#151A23\" /> </svg> </A_87> <A_88 className={\\`\\${!toggleView ? \"hidden\" : \"\"}\\`}> {flaggedAddresses.map(({ role, flaggedBy, address, reason }) => <Flag key={flaggedBy}> {} <div className=\"content\"> <div className=\"header\"> <ProfileImage className=\"profile-image\" accountId={flaggedBy} /> <div className=\"role\">{role}</div> <div className=\"dot\" /> <div className=\"id\"> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${flaggedBy}\\`)} target=\"_blank\"> {flaggedBy}{\" \"} </a> has flagged </div> <a className=\"flagged-account\" href={hrefWithParams(\\`?tab=profile&accountId=\\${address}\\`)} target=\"_blank\"> {address} </a> </div> <div className=\"reason\">{reason}</div> </div> </Flag>)} </A_88> </A_86> <A_85 /> </>; `, Payouts: ` const Arrow = styled.svg\\` width: 12px; rotate: 180deg; transition: all 200ms; display: none; @media screen and (max-width: 768px) { display: block; }\\`;const ArrowDown = __props__ => <Arrow {...__props__} viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294983L0 6.29498L1.41 7.70498L6 3.12498L10.59 7.70498L12 6.29498L6 0.294983Z\" fill=\"#7B7B7B\" /> </Arrow>; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const calculatePayouts = (allPotDonations, totalMatchingPool, blacklistedAccounts) => { const { NADABOT_CONTRACT_ID } = constants; return new Promise((resolve, reject) => { console.log(\"Calculting payouts; ignoring blacklisted donors &/or projects: \", blacklistedAccounts.join(\", \")); console.log(\"totalMatchingPool: \", totalMatchingPool); const projectContributions = []; const allDonors = new Set(); for (const d of allPotDonations) { if (blacklistedAccounts.includes(d.donor_id) || blacklistedAccounts.includes(d.project_id)) { continue; } const amount = new Big(d.total_amount); const val = [d.project_id, d.donor_id, amount]; projectContributions.push(val); allDonors.add(d.donor_id); } const limit = 100; let curIndex = 0; let humanScores = {}; let promises = []; while (curIndex < allDonors.size) { promises.push(Near.asyncView(NADABOT_CONTRACT_ID, \"get_human_score_batch\", { account_ids: Array.from(allDonors).slice(curIndex, curIndex + limit) })); curIndex += limit; } Promise.all(promises).then(res => { for (const r of res) { humanScores = { ...humanScores, ...r }; } }).catch(e => { console.error(\"error fetching human scores. Continuing anyway: \", e); }).finally(() => { console.log(\"human scores: \", humanScores); const contributions = {}; for (const [proj, user, amount] of projectContributions) { if (!humanScores[user] || !humanScores[user].is_human) { console.log(\"skipping non-human: \", user); continue; } if (!contributions[proj]) { contributions[proj] = {}; } contributions[proj][user] = Big(contributions[proj][user] || 0).plus(amount); } console.log(\"contributions: \", contributions); const pairTotals = {}; for (const contribz of Object.values(contributions)) { for (const [k1, v1] of Object.entries(contribz)) { if (!pairTotals[k1]) { pairTotals[k1] = {}; } for (const [k2, v2] of Object.entries(contribz)) { if (!pairTotals[k1][k2]) { pairTotals[k1][k2] = Big(0); } pairTotals[k1][k2] = pairTotals[k1][k2].plus(v1.times(v2).sqrt()); } } } const threshold = Big(\"25000000000000000000000000\"); const totalPot = Big(totalMatchingPool); let bigtot = Big(0); const totals = []; for (const [proj, contribz] of Object.entries(contributions)) { let tot = Big(0); let _num = 0; let _sum = Big(0); for (const [k1, v1] of Object.entries(contribz)) { _num += 1; _sum = _sum.plus(v1); for (const [k2, v2] of Object.entries(contribz)) { if (k2 > k1 || Object.keys(contribz).length === 1) { const sqrt = v1.times(v2).sqrt(); tot = tot.plus(sqrt.div(pairTotals[k1][k2].div(threshold))); } } } bigtot = bigtot.plus(tot); totals.push({ id: proj, number_contributions: _num, contribution_amount_str: _sum.toFixed(0), matching_amount_str: tot.toFixed(0) }); } console.log(\"totals before: \", totals); if (bigtot.gte(totalPot)) { console.log(\"NORMALIZING\"); for (const t of totals) { t.matching_amount_str = Big(t.matching_amount_str).div(bigtot).times(totalPot).toFixed(0); } } let totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } let residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"first round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(\"0\"))) { for (let i = 0; i < totals.length; i++) { let proportion = Big(totals[i].matching_amount_str).div(totalAllocatedBeforeRounding); let additionalAllocation = proportion.times(residual); totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(additionalAllocation).toFixed(0); } console.log(\"CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION\"); totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"second round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(0))) { totals.sort((a, b) => Big(b.matching_amount_str).minus(Big(a.matching_amount_str))); if (residual.gt(Big(0))) { totals[0].matching_amount_str = Big(totals[0].matching_amount_str).plus(residual).toFixed(0); } else { for (let i = 0; i < totals.length; i++) { if (Big(totals[i].matching_amount_str).gt(residual.abs())) { totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(residual).toFixed(0); break; } } } totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"Residual after final adjustment: \", residual.toFixed(0)); } } const payouts = totals.reduce((acc, t) => { acc[t.id] = { totalAmount: t.contribution_amount_str, matchingAmount: t.matching_amount_str, donorCount: t.number_contributions }; return acc; }, {}); resolve(payouts); }); });}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_89 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; width: 100%; @media screen and (min-width: 375px) and (max-width: 768px) { width: 99%; } @media screen and (max-width: 390px) { width: 98%; }\\`;const OuterTextContainer = styled.div\\` display: flex; flex-direction: row; gap: 10px; @media screen and (max-width: 768px) { padding-right: 10px; }\\`;const OuterText = styled.div\\` color: #7b7b7b; font-size: 14px; font-weight: 500; text-transform: uppercase; line-height: 24px; letter-spacing: 1.12px; word-wrap: break-word;\\`;const Count = styled.div\\` color: #dd3345; font-size: 14px; font-weight: 600; line-height: 24px;\\`;const TableContainer = styled.div\\` display: flex; flex-direction: column; align-items: center; border-radius: 6px; border: 1px solid #7b7b7b; width: 100%; overflow-x: auto; flex-wrap: nowrap;\\`;const A_90 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; gap: 2rem; padding: 0.5rem 1rem; border-bottom: 1px solid rgba(199, 199, 199, 0.5); @media only screen and (max-width: 768px) { display: none; }\\`;const HeaderItem = styled.div\\` display: flex; flex-direction: row; align-items: space-between; justify-content: flex-start; justify-content: space-between; width: 110px; justify-content: right; &.project { flex: 1; justify-content: left; } @media only screen and (max-width: 768px) { display: none; &.project { display: flex; } }\\`;const A_91 = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const MobileAmount = styled.div\\` width: 100%; margin-left: 2rem; display: none; max-height: 0px; overflow: hidden; transition: all 200ms; span { font-weight: 600; } @media screen and (max-width: 768px) { order: 2; display: block; }\\`;const A_92 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; padding: 1rem; gap: 2rem; border-top: 1px solid rgba(199, 199, 199, 0.5); position: relative; .toggle-check { cursor: pointer; position: absolute; left: 0; top: 0; height: 100%; width: 100%; opacity: 0; display: none; } .toggle-check:checked ~ svg { rotate: 0deg; } .toggle-check:checked + \\${MobileAmount} { max-height: 100px; } @media screen and (max-width: 768px) { flex-wrap: wrap; gap: 0.5rem; .toggle-check { display: block; } }\\`;const RowItem = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; width: 110px; justify-content: right; &:hover { text-decoration: none; } &.project { flex: 1; display: flex; gap: 1rem; justify-content: left; transition: 200ms; a { color: #292929; font-weight: 600; transition: 200ms; &:hover { color: #dd3345; text-decoration: none; } } } @media screen and (max-width: 768px) { &.project { gap: 0.5rem; } &.donors, &.amount { display: none; } }\\`;const RowText = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; word-wrap: break-word; span { color: #7b7b7b; font-weight: 600; display: none; } @media screen and (max-width: 768px) { span { display: inline; } &:last-of-type { display: flex; gap: 4px; } }\\`;const SearchBarContainer = styled.div\\` display: flex; align-items: center; gap: 16px; width: 100%; background: #f6f5f3; padding: 0.5rem 1rem; @media only screen and (max-width: 768px) { gap: 8px; }\\`;const SearchBar = styled.input\\` background: none; width: 100%; outline: none; border: none; &:focus { outline: none; border: none; }\\`;const SearchIcon = styled.div\\` display: flex; width: 24px; height: 24px; align-items: center; justify-content: center;\\`;const InfoContainer = styled.div\\` display: flex; align-items: center; justify-content: center; padding: 8px; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; gap: 1rem; margin-left: auto; margin-bottom: 1.5rem;\\`;const WarningText = styled.div\\` text-align: center; color: #dd3345; font-weight: 500; font-size: 14px;\\`;const AlertSvg = styled.svg\\` width: 18px; @media screen and (max-width: 768px) { width: 1rem; }\\`; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const { potDetail: potDetail, allDonations: allDonations } = props; const { potId } = props.alem.useParams(); State.init({ allPayouts: null, filteredPayouts: null, flaggedAddresses: null }); const { allPayouts, filteredPayouts, flaggedAddresses } = state; if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => { const listOfFlagged = []; data.forEach((adminFlaggedAcc) => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); listOfFlagged.push(...addresses); }); State.update({ flaggedAddresses: listOfFlagged }); }).catch((err) => console.log(\"error getting the flagged accounts \", err)); } if (!allPayouts && allDonations && potDetail && flaggedAddresses) { calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((calculatedPayouts) => { if (potDetail.payouts.length) { const synthesizedPayouts = potDetail.payouts.map((payout) => { const { project_id, amount } = payout; const { totalAmount, donorCount } = calculatedPayouts[project_id]; return { projectId: project_id, totalAmount, matchingAmount: amount, donorCount }; }); State.update({ allPayouts: synthesizedPayouts, filteredPayouts: synthesizedPayouts }); } else { const allPayouts = Object.entries(calculatedPayouts).map(([projectId, { totalAmount, matchingAmount, donorCount }]) => { return { projectId, totalAmount, matchingAmount, donorCount }; }); allPayouts.sort((a, b) => { return b.matchingAmount - a.matchingAmount; }); State.update({ allPayouts, filteredPayouts: allPayouts }); } }); } const searchPayouts = (searchTerm) => { const filteredPayouts = allPayouts.filter((payout) => { const { projectId } = payout; const searchFields = [projectId]; return searchFields.some((field) => field.toLowerCase().includes(searchTerm.toLowerCase())); }); filteredPayouts.sort((a, b) => { return b.matchingAmount - a.matchingAmount; }); return filteredPayouts; }; const MAX_ACCOUNT_ID_DISPLAY_LENGTH = 10; return <A_89> <Widget loading=\" \" code={props.alem.componentsCode.FlaggedAccounts} props={{ ...{ potDetail: potDetail, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.PayoutsChallenges} props={{ ...{ ...props } }} /> {!potDetail.all_paid_out && <InfoContainer> <AlertSvg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7.25 4.25H8.75V5.75H7.25V4.25ZM7.25 7.25H8.75V11.75H7.25V7.25ZM8 0.5C3.86 0.5 0.5 3.86 0.5 8C0.5 12.14 3.86 15.5 8 15.5C12.14 15.5 15.5 12.14 15.5 8C15.5 3.86 12.14 0.5 8 0.5ZM8 14C4.6925 14 2 11.3075 2 8C2 4.6925 4.6925 2 8 2C11.3075 2 14 4.6925 14 8C14 11.3075 11.3075 14 8 14Z\" fill=\"#EE8949\" /> </AlertSvg> <WarningText> {potDetail.cooldown_end_ms ? \"These payouts have been set on the contract but have not been paid out yet.\" : \"These payouts are estimated amounts only and have not been set on the contract yet.\"} </WarningText> </InfoContainer>} <TableContainer> <A_90> <HeaderItem className=\"project\"> <A_91>Project</A_91> </HeaderItem> <HeaderItem> <A_91>Total Raised</A_91> </HeaderItem> <HeaderItem> <A_91>Unique Donors</A_91> </HeaderItem> <HeaderItem> <A_91>Pool Allocation</A_91> </HeaderItem> </A_90> <SearchBarContainer> <SearchIcon> <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.7549 14.2549H14.9649L14.6849 13.9849C15.6649 12.8449 16.2549 11.3649 16.2549 9.75488C16.2549 6.16488 13.3449 3.25488 9.75488 3.25488C6.16488 3.25488 3.25488 6.16488 3.25488 9.75488C3.25488 13.3449 6.16488 16.2549 9.75488 16.2549C11.3649 16.2549 12.8449 15.6649 13.9849 14.6849L14.2549 14.9649V15.7549L19.2549 20.7449L20.7449 19.2549L15.7549 14.2549ZM9.75488 14.2549C7.26488 14.2549 5.25488 12.2449 5.25488 9.75488C5.25488 7.26488 7.26488 5.25488 9.75488 5.25488C12.2449 5.25488 14.2549 7.26488 14.2549 9.75488C14.2549 12.2449 12.2449 14.2549 9.75488 14.2549Z\" fill=\"#C7C7C7\" /> </svg> </SearchIcon> <SearchBar placeholder=\"Search payouts\" onChange={({ target: { value } }) => { const filteredPayouts = searchPayouts(value); State.update({ filteredPayouts }); }} /> </SearchBarContainer> {!filteredPayouts ? <div>Loading</div> : filteredPayouts.length === 0 ? <A_92 style={{ padding: \"12px\" }}>No payouts to display</A_92> : filteredPayouts.map((payout, index) => { const { projectId, donorCount, matchingAmount, totalAmount } = payout; return <A_92 key={index}> <RowItem className=\"project\"> <ProfileImage style={{ height: \"24px\", width: \"24px\" }} className=\"profile-image\" accountId={projectId} /> <a href={\\`?tab=project&projectId=\\${projectId}\\`} target={\"_blank\"}> {projectId.length > MAX_ACCOUNT_ID_DISPLAY_LENGTH ? projectId.slice(0, MAX_ACCOUNT_ID_DISPLAY_LENGTH) + \"...\" : projectId} </a> </RowItem> {} <RowItem className=\"amount\"> <RowText>{yoctosToNear(totalAmount, true)}</RowText> </RowItem> <input type=\"checkbox\" className=\"toggle-check\" /> <MobileAmount> <span>{yoctosToNear(totalAmount, true)}</span> raised from <span>{donorCount}</span> unique donors </MobileAmount> {} <RowItem className=\"donors\"> <RowText>{donorCount}</RowText> </RowItem> {} <RowItem> <RowText> {yoctosToNear(matchingAmount, true)} <span>Allocated</span> </RowText> </RowItem> <ArrowDown /> </A_92>; })} </TableContainer> </A_89>; `, SponsorsTable: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_93 = styled.div\\` width: 100%; display: flex; flex-direction: column; align-items: center; gap: 2rem; border-radius: 6px; border: 1px solid #7b7b7b; overflow: hidden; .transcation { display: flex; flex-direction: column; width: 100%; font-size: 14px; .header { display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 1rem; gap: 1rem; color: #7b7b7b; div { display: flex; align-items: center; font-weight: 600; &:last-of-type { justify-content: flex-end; } } } .address { width: 190px; margin-right: auto; justify-content: start !important; } .rank { width: 40px; margin-right: 2rem; justify-content: center; } } @media only screen and (max-width: 768px) { .transcation { font-size: 12px; .header { padding: 0.5rem; } .rank { margin-right: 0; width: 30px; } } } @media only screen and (max-width: 480px) { .transcation .address { width: 135px; flex: 1; } }\\`;const A_94 = styled.div\\` display: flex; align-items: center; justify-content: space-between; width: 100%; gap: 1rem; padding: 1rem; border-top: 1px solid #c7c7c7; > div { display: flex; align-items: center; } .address { color: #292929; font-weight: 600; display: flex; align-items: center; justify-content: flex-start; border-radius: 2px; transition: all 200ms; .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 1rem; } } .sponsors-amount { justify-content: flex-end; font-weight: 600; display: flex; align-items: center; gap: 1rem; } @media only screen and (max-width: 768px) { padding: 1rem 0.5rem; }\\`;const Percentage = styled.div\\` background: #ebebeb; box-shadow: 0px -1px 0px 0px #dbdbdb inset, 0px 0px 0px 0.5px #dbdbdb; border-radius: 4px; padding: 2px 4px; min-width: 60px; text-align: right;\\`;const A_95 = styled.div\\` font-size: 1.125rem; text-align: center;\\`; const { sponsors: sponsors } = props; const { tab } = props.alem.useParams(); const isInPot = tab === \"pot\"; const [currentPage, setCurrentPage] = useState(1); const perPage = 30; useEffect(() => { setCurrentPage(1); }, []); let totalDonations = 0; sponsors.forEach((donation) => { totalDonations += donation.amount; }); return sponsors.length ? <A_93> <div className=\"transcation\"> <div className=\"header\"> <div className=\"rank\">Rank</div> <div className=\"address\">Donor</div> <div>Amount</div> {A_236 && !isInPot && <div>Amount (USD)</div>} </div> {sponsors.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation, idx) => { const { donor_id, amount, percentage_share } = donation; return <A_94> <div className=\"rank\">#{idx + 1 + (currentPage - 1) * perPage}</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`)} className=\"address\" target=\"_blank\"> <ProfileImage style={{}} accountId={donor_id} /> <OverlayTrigger placement=\"top\" overlay={<Tooltip>{donor_id}</Tooltip>}> <div> {_address(donor_id, 15)}</div> </OverlayTrigger> </a> <div className=\"sponsors-amount\"> {amount.toFixed(2).replace(/[.,]00$/, \"\")}N{\" \"} <Percentage>{percentage_share === \"0\" ? \"<0.01\" : percentage_share}%</Percentage>{\" \"} </div> </A_94>; })} </div> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: sponsors, currentPage, perPage: perPage }, ...props } }} /> </A_93> : <A_95>No Sponsors</A_95>; `, Sponsors: ` const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_96 = styled.div\\` display: flex; gap: 2rem; min-height: 430px; width: 100%; .col { display: flex; flex-direction: column; gap: 2rem; } .item { position: relative; display: flex; justify-content: center; gap: 1rem; flex-direction: column; border-radius: 12px; background: #fef6ee; height: 50%; padding: 24px; font-size: 14px; &.first { box-shadow: 0px 1px 2px -1px rgba(0, 0, 0, 0.08), 0px 1px 1px -1px rgba(0, 0, 0, 0.12); border: 2px solid #dd3345; align-items: center; text-align: center; height: 100%; .profile-image { width: 64px; height: 64px; } .footer { flex-direction: column; gap: 1rem; } .amount { font-size: 32px; font-family: \"Lora\"; } } .profile-image { width: 40px; height: 40px; border-radius: 50%; display: flex !important; @media only screen and (max-width: 480px) { width: 40px; height: 40px; } } .name { white-space: nowrap; font-weight: 600; color: #292929; transition: all 300ms; font-size: 1rem; :hover { color: #dd3345; text-decoration: none; } } .footer { display: flex; align-items: center; justify-content: space-between; width: 100%; } .amount { font-size: 22px; font-weight: 600; } .percentage { font-size: 1rem; background: #f8d3b0; padding: 4px 8px; border-radius: 4px; font-weight: 600; height: fit-content; } } @media only screen and (max-width: 960px) { gap: 1rem; flex-direction: column; .col { gap: 1rem; } .col:nth-child(2) { order: -1; } }\\`; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const Sponsor = ({ donation: { amount, donor_id, percentage_share }, colIdx}) => { const profile = Social.getr(\\`\\${donor_id}/profile\\`); return <div className={\\`item \\${colIdx === 2 && \"first\"}\\`}> <ProfileImage style={{}} profile={profile} /> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`)} target=\"_blank\" className=\"name\"> {_address(profile.name || donor_id, 15)} </a> <div>{_address(profile.description, colIdx === 2 ? 120 : 35)}</div> <div className=\"footer\"> <div className=\"amount\">{amount} NEAR</div> <div className=\"percentage\">{percentage_share}%</div> </div> </div>;};const SponsorsBoard = (__props__) => { const { donations } = __props__; const sponsorsLeaderboard = [donations.slice(1, 3), donations.slice(0, 1), donations.slice(3, 5)].filter((subList) => subList.length > 0); return <A_96> {sponsorsLeaderboard.map((donationsCol, colIdx) => <div className=\"col\"> {donationsCol.map((donation, idx) => <Sponsor donation={donation} colIdx={colIdx + 1} idx={idx + 1} />)} </div>)} </A_96>;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_97 = styled.div\\` display: flex; flex-direction: column; align-items: center; gap: 24px; width: 100%; @media screen and (min-width: 375px) and (max-width: 768px) { width: 99%; } @media screen and (max-width: 390px) { width: 98%; }\\`;const A_98 = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; margin-top: 35px; padding-bottom: 1rem;\\`; const { potDetail: potDetail } = props; const { potId } = props.alem.useParams(); let sponsorshipDonations = PotSDK.getMatchingPoolDonations(potId); const { SUPPORTED_FTS } = constants; State.init({ sponsorshipDonations: null }); if (sponsorshipDonations && !state.sponsorshipDonations) { sponsorshipDonations = sponsorshipDonations.reduce((accumulator, currentDonation) => { accumulator[currentDonation.donor_id] = { amount: parseFloat(accumulator[currentDonation.donor_id].amount || 0) + parseFloat(SUPPORTED_FTS.NEAR.fromIndivisible(currentDonation.net_amount)), ...currentDonation }; return accumulator; }, {}); const total = parseFloat(SUPPORTED_FTS.NEAR.fromIndivisible(potDetail.matching_pool_balance)); sponsorshipDonations = Object.values(sponsorshipDonations).sort((a, b) => b.amount - a.amount); sponsorshipDonations = sponsorshipDonations.map((donation) => { return { ...donation, percentage_share: (donation.amount / total * 100).toFixed(2).replace(/[.,]00$/, \"\") }; }); State.update({ sponsorshipDonations }); } if (!state.sponsorshipDonations) return <div className=\"spinner-border text-secondary\" role=\"status\" />; return <A_97> <SponsorsBoard donations={state.sponsorshipDonations.slice(0, 6)} /> <A_98> <Widget loading=\" \" code={props.alem.componentsCode.SponsorsTable} props={{ ...{ sponsors: state.sponsorshipDonations, ...props } }} /> </A_98> </A_97>; `, FlagModal: ` const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const BannerBg = __props__ => <svg {...__props__} viewBox=\"0 0 145 152\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <rect x=\"157.654\" y=\"-37\" width=\"20\" height=\"161.118\" rx=\"10\" transform=\"rotate(45 157.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"189.654\" y=\"-37\" width=\"20\" height=\"245.972\" rx=\"10\" transform=\"rotate(45 189.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"221.654\" y=\"-37\" width=\"20\" height=\"164.654\" rx=\"10\" transform=\"rotate(45 221.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"125.654\" y=\"-37\" width=\"20\" height=\"177.702\" rx=\"10\" transform=\"rotate(45 125.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"93.6543\" y=\"-37\" width=\"20\" height=\"78.4889\" rx=\"10\" transform=\"rotate(45 93.6543 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> </svg>; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const A_100 = styled.div\\` display: flex; flex-direction: column; border-radius: 12px; background: #fff; font-size: 14px; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); overflow: hidden; border-radius: 6px;\\`;const Banner = styled.div\\` position: relative; display: flex; flex-direction: column; padding: 1.5rem 2rem; gap: 0.5rem; overflow: hidden; background: #dd3345; color: white; font-size: 22px; .left-pattern { position: absolute; left: 0; top: 0; width: 30%; transform: translate(-10%, -10%) scaleX(-1); pointer-events: none; } .right-pattern { position: absolute; right: 0; top: 0; width: 30%; transform: translate(10%, -10%); pointer-events: none; } @media only screen and (max-width: 480px) { padding: 1.125rem; }\\`;const HeaderIcons = styled.div\\` display: flex; align-items: center; justify-content: space-between; svg { width: 14px; cursor: pointer; transition: all 300ms ease-in-out; } .close-icon { margin-left: auto; &:hover { rotate: 90deg; } } div { cursor: pointer; display: flex; }\\`;const Content = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem 2rem; gap: 1.5rem; @media only screen and (max-width: 480px) { padding: 1.125rem; }\\`;const A_101 = styled.div\\` font-weight: 500; margin-bottom: 0.5rem;\\`;const Limit = styled.div\\` color: #7b7b7b; text-align: right;\\`;const InfoCard = styled.div\\` display: flex; align-items: center; gap: 1rem; padding: 0.75rem 1rem; width: 100%; border-radius: 6px; border: 1px solid #ebebeb; background: #f6f5f3;\\`;const A_102 = styled.div\\` display: grid; gap: 1.5rem; grid-template-columns: repeat(2, 1fr); button { font-weight: 500; } .cancel { border: none; background: none; color: #dd3345; }\\`; const props = props; const MAX_REASON_LENGTH = 250; const SOCIAL_CONTRACT_ID = \"social.near\"; const accountId = context.accountId || \"\"; const [reason, setReason] = useState(\"\"); const [reasonErr, setReasonErr] = useState(\"\"); const { onClose, flagAddress, potId, setSuccessFlag } = props; const onCancel = () => { onClose(); setReason(\"\"); }; const fetchSocialProfile = () => { return Near.asyncView(SOCIAL_CONTRACT_ID, \"get\", { keys: [\\`\\${accountId}/profile/**\\`] }); }; const handleSuccess = () => { const flsgSuccess = setInterval(() => { fetchSocialProfile().then(profileData => { const profile = profileData[accountId].profile; const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (potFlaggedAcc[flagAddress]) { setSuccessFlag({ account: flagAddress, reason }); onCancel(); clearInterval(flsgSuccess); } }); }, 1000); setTimeout(() => { onCancel(); clearInterval(flsgSuccess); }, 60000); }; const handleFlag = () => { fetchSocialProfile().then(profileData => { const profile = profileData[accountId].profile; const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; const socialArgs = { data: { [accountId]: { profile: { pLBlacklistedAccounts: JSON.stringify({ ...pLBlacklistedAccounts, [potId]: { ...potFlaggedAcc, [flagAddress]: reason } }) } } } }; const socialTransaction = { contractName: SOCIAL_CONTRACT_ID, methodName: \"set\", args: socialArgs }; Near.asyncView(SOCIAL_CONTRACT_ID, \"get_account\", { account_id: accountId }).then(account => { let depositFloat = JSON.stringify(socialArgs).length * 0.00015; if (!account) { depositFloat += 0.1; } socialTransaction.deposit = Big(depositFloat).mul(Big(10).pow(24)); Near.call(socialTransaction); handleSuccess(); }); }); }; return <ModalOverlay onOverlayClick={onCancel}> <A_100> <div> <Banner> <BannerBg className=\"left-pattern\" /> <BannerBg className=\"right-pattern\" /> <HeaderIcons> <svg onClick={() => onCancel()} className=\"close-icon\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#FCCFCF\" /> </svg> </HeaderIcons> Flag {flagAddress} </Banner> </div> <Content> <div> <TextArea {...{ label: \"Reason\", inputRows: 4, inputStyle: { background: \"#FAFAFA\" }, placeholder: \\`Type description\\`, value: reason, onChange: reason => setReason(reason), validate: () => { if (reason.length > MAX_REASON_LENGTH) { setReasonErr(\\`Reason must be less than \\${MAX_REASON_LENGTH} characters\\`); return; } setReasonErr(\"\"); }, error: reasonErr }} /> <Limit> {reason.length}/{MAX_REASON_LENGTH} </Limit> </div> <InfoCard> <div> <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9 5H11V7H9V5ZM9 9H11V15H9V9ZM10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18Z\" fill=\"#7B7B7B\" /> </svg> </div> <div>Flagging this account will remove their donations when calculating payouts for this pot</div> </InfoCard> <A_102> <button className=\"cancel\" onClick={onCancel}> Cancel </button> <Button {...{ type: \"primary\", text: \"Confirm\", disabled: !reason || !!reasonErr, onClick: handleFlag }} /> </A_102> </Content> </A_100> </ModalOverlay>; `, DonationsTable: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_99 = styled.div\\` padding: 16px 32px 40px; svg { display: block; cursor: pointer; width: 14px; transition: all 300ms ease-in-out; margin: 5px 0; margin-left: auto; &:hover { rotate: 90deg; } } .title { margin-bottom: 1rem; font-size: 16px; color: #7b7b7b; font-weight: 600; span { font-weight: 600; color: #292929; } } .reason { font-size: 14px; color: #7b7b7b; }\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const FlagSuccessModal = (__props__) => { const { onClose, successFlag } = __props__; return <ModalOverlay onOverlayClick={onClose}> <A_99> <svg viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" onClick={onClose}> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#7B7B7B\" /> </svg> <div className=\"title\"> <span> {successFlag.address} </span> has been flagged </div> <div className=\"reason\">{successFlag.reason}</div> </A_99> </ModalOverlay>;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_129 = (__props__) => <svg {...__props__} style={{ rotate: !__props__.active ? \"0deg\" : \"180deg\"}} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 6L1.0575 7.0575L5.25 2.8725V12H6.75V2.8725L10.935 7.065L12 6L6 0L0 6Z\" fill=\"#7B7B7B\" /> </svg>; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_103 = (donation) => { const lastDonationAmount = Big(donation.total_amount - (donation.referrer_fee || 0) - (donation.protocol_fee || 0)).div(Big(1e24)); return parseFloat(lastDonationAmount);}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const FlagBtn = ({ isFlagged, isProject, address, setUpdateFlaggedAddresses, updateFlaggedAddresses, setFlagAddress}) => { const accountId = context.accountId; const { potId } = props.alem.useParams(); const handleFlag = (e, address, isFlagged) => { const SOCIAL_CONTRACT_ID = \"social.near\"; e.preventDefault(); if (isFlagged) { Near.asyncView(SOCIAL_CONTRACT_ID, \"get\", { keys: [\\`\\${accountId}/profile/**\\`] }).then((profileData) => { const profile = profileData[accountId].profile; const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; delete potFlaggedAcc[address]; const socialArgs = { data: { [accountId]: { profile: { pLBlacklistedAccounts: JSON.stringify({ ...pLBlacklistedAccounts, [potId]: { ...potFlaggedAcc } }) } } } }; const depositFloat = JSON.stringify(socialArgs).length * 0.00015; const socialTransaction = { contractName: SOCIAL_CONTRACT_ID, methodName: \"set\", args: socialArgs, deposit: Big(depositFloat).mul(Big(10).pow(24)) }; Near.call(socialTransaction); setTimeout(() => { setUpdateFlaggedAddresses(!updateFlaggedAddresses); }, 3000); }); } else { setFlagAddress(address); } }; return isFlagged && accountId === isFlagged.flaggedBy ? <A_109 className=\"flag\" onClick={(e) => handleFlag(e, address, isFlagged)}> <svg width=\"16\" height=\"18\" viewBox=\"0 0 16 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7.86 2.5L8.26 4.5H13.5V10.5H10.14L9.74 8.5H2.5V2.5H7.86ZM9.5 0.5H0.5V17.5H2.5V10.5H8.1L8.5 12.5H15.5V2.5H9.9L9.5 0.5Z\" fill=\"#7B7B7B\" /> </svg> <div>Unflag {isProject ? \"project\" : \"donor\"}</div> </A_109> : <A_109 className=\"flag\" onClick={(e) => handleFlag(e, address, isFlagged)}> <svg width=\"16\" height=\"18\" viewBox=\"0 0 16 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7.86 2.5L8.26 4.5H13.5V10.5H10.14L9.74 8.5H2.5V2.5H7.86ZM9.5 0.5H0.5V17.5H2.5V10.5H8.1L8.5 12.5H15.5V2.5H9.9L9.5 0.5Z\" fill=\"#7B7B7B\" /> </svg> <div>Flag {isProject ? \"project\" : \"donor\"}</div> </A_109>;}; const A_104 = styled.div\\` display: flex; flex-direction: column; width: 100%; margin-top: 24px; padding-bottom: 1rem; border-radius: 6px; border: 1px solid #7b7b7b; align-items: center; gap: 2rem; .transcation { display: flex; flex-direction: column; width: 100%; font-size: 14px; } .header { display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 1rem; gap: 2rem; color: #292929; border-bottom: 1px solid rgba(199, 199, 199, 0.5); div { width: 100px; display: flex; justify-content: center; align-items: center; font-weight: 600; &.sort { cursor: pointer; gap: 8px; svg { transition: rotate 300ms; } } } .price { width: 70px; margin-right: 5rem; } } .address { /* width: 143px !important; */ flex: 1; justify-content: flex-start !important; } @media only screen and (max-width: 992px) { .header .price { margin-right: 0; } } @media only screen and (max-width: 768px) { .header { display: none; } }\\`;const A_105 = styled.div\\` display: flex; align-items: center; gap: 1rem; width: 100%; font-size: 14px; background: #f6f5f3; padding: 0.5rem 1rem; @media only screen and (max-width: 780px) { gap: 0.5rem; }\\`;const A_106 = styled.input\\` background: none; width: 100%; outline: none; border: none; &:focus { outline: none; border: none; }\\`;const A_107 = styled.div\\` display: flex; width: 24px; height: 24px; align-items: center; justify-content: center;\\`;const A_108 = styled.div\\` display: flex; width: 100%; justify-content: space-between; gap: 2rem; padding: 1rem; border-top: 1px solid rgb(199 199 199 / 50%); > div { width: 100px; display: flex; justify-content: center; align-items: center; } .price { display: flex; gap: 1rem; align-items: center; font-weight: 600; width: 74px; margin-right: 5rem; justify-content: flex-start; span { display: none; } img { width: 1.125rem; } } .address { position: relative; color: #292929; display: flex; align-items: center; justify-content: flex-start; border-radius: 2px; transition: all 200ms; font-weight: 600; .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 1rem; } :hover { text-decoration: none; .flag { opacity: 1; pointer-events: all; } } } .project-mobile-view { position: relative; width: fit-content; color: #7b7b7b; border-radius: 14px; padding: 4px 6px; border: 1px solid var(--Neutral-200, #dbdbdb); background: var(--Neutral-100, #ebebeb); cursor: pointer; display: none; align-items: center; gap: 0.5rem; margin-left: 4px; .profile-image { width: 1.125rem; height: 1.125rem; display: flex !important; } .flag { left: -50%; .tip-icon { padding-left: 50%; } } :hover { text-decoration: none; .flag { opacity: 1; pointer-events: all; } } @media only screen and (max-width: 768px) { display: flex; } } .date span { display: none; } @media only screen and (max-width: 992px) { .price { margin-right: 0; } } @media only screen and (max-width: 768px) { flex-wrap: wrap; .project { display: none !important; } .price { min-width: 120px; gap: 8px; width: fit-content; justify-content: flex-start; span { display: inline-block; } } .date { width: 100%; justify-content: start; gap: 4px; span { display: inline; } } .address .profile-image { margin-right: 0.5rem; } }\\`;const A_109 = styled.div\\` display: flex; align-content: center; gap: 12px; margin-left: auto; opacity: 0; pointer-events: none; font-weight: 500; transition: 300ms ease-in-out; @media only screen and (max-width: 992px) { opacity: 1; div { display: none; } } @media only screen and (max-width: 768px) { margin-left: 0.5rem; }\\`;const FlagTooltipWrapper = styled.div\\` position: absolute; display: flex; flex-direction: column; opacity: 0; pointer-events: none; transition: all 300ms ease 0s; top: 100%; background: white; z-index: 1; box-shadow: 0px 0px 1px 0px rgba(41, 41, 41, 0.74), 0px 3px 3px 0px rgba(123, 123, 123, 0.12), 0px 6px 6px 0px rgba(123, 123, 123, 0.12); border-radius: 4px; padding: 1rem; max-width: 550px; width: max-content; margin-top: 8px; cursor: default; .content { display: flex; gap: 1rem; .content-info { display: flex; flex-direction: column; gap: 0.5rem; } .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0; } .title { display: flex; align-items: center; gap: 8px; } .role { font-weight: 600; color: #7b7b7b; } .dot { width: 5px; height: 5px; background: #7b7b7b; border-radius: 50%; } .admin { font-weight: 600; display: flex; gap: 4px; } .text { color: #7b7b7b; } .flaged { color: #ed464f; font-weight: 600; &:hover { text-decoration: none; } } } .tip-icon { display: flex; z-index: 1; position: absolute; top: 0; height: 8px; transform: translateY(-100%); width: 100%; justify-content: flex-start; left: 0; padding-left: 2.5rem; svg { stroke: rgb(41 41 41 / 21%); } } @media only screen and (max-width: 768px) { width: 300px; padding: 0.5rem; font-size: 12px; .content .profile-image { display: none !important; } }\\`; const props = props; const accountId = context.accountId; const { filteredDonations, filter, handleSearch, sortDonation, currentFilter, potDetail } = props; const { potId } = props.alem.useParams(); const { admins, owner, chef, all_paid_out } = potDetail; const nearLogo = \"https://ipfs.near.social/ipfs/bafkreicdcpxua47eddhzjplmrs23mdjt63czowfsa2jnw4krkt532pa2ha\"; const [currentPage, setCurrentPage] = useState(1); const [flagAddress, setFlagAddress] = useState(null); const [successFlag, setSuccessFlag] = useState(null); const [updateFlaggedAddresses, setUpdateFlaggedAddresses] = useState(false); const [flaggedAddresses, setFlaggedAddresses] = useState([]); const perPage = 30; useEffect(() => { setCurrentPage(1); }, [filter]); useEffect(() => { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => setFlaggedAddresses(data)).catch((err) => console.log(\"error getting the flagged accounts \", err)); }, [successFlag, updateFlaggedAddresses]); const potAdmins = [owner, chef, ...admins]; const hasAuthority = potAdmins.includes(accountId) && !all_paid_out; const checkIfIsFlagged = (address) => flaggedAddresses.find((obj) => obj.potFlaggedAcc[address]); const FlagTooltip = ({ flag, href, address }) => <FlagTooltipWrapper className=\"flag\" onClick={(e) => e.preventDefault()}> <div className=\"tip-icon\"> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 5.24537e-07L-2.54292e-07 8L12 8L6 5.24537e-07Z\" fill=\"white\" /> </svg> </div> <div className=\"content\"> <ProfileImage style={{}} accountId={flag.flaggedBy} /> <div className=\"content-info\"> <div className=\"title\"> <div className=\"role\">{flag.role}</div> <div className=\"dot\" /> <div className=\"admin\"> {_address(flag.flaggedBy)} has flagged <a href={href} className=\"flaged\" target=\"_blank\" onClick={(e) => e.stopPropagation()}> {_address(address)} </a> </div> </div> <div className=\"text\">{flag.potFlaggedAcc[address]}</div> </div> </div> </FlagTooltipWrapper>; const AddressItem = ({ href, address, isFlagged, isProject, className }) => <a href={href} className={className} target=\"_blank\" onClick={(e) => { isFlagged ? e.preventDefault() : null; }}> <ProfileImage style={{}} accountId={address} /> <div style={{ color: isFlagged ? \"#ed464f\" : \"#292929\", fontWeight: \"600\" }}> {_address(address)} </div> {isFlagged && <FlagTooltip flag={isFlagged} href={href} address={address} />} {hasAuthority && <FlagBtn isProject={isProject} address={address} isFlagged={isFlagged} setUpdateFlaggedAddresses={setUpdateFlaggedAddresses} updateFlaggedAddresse={updateFlaggedAddresses} setFlagAddress={setFlagAddress} />} </a>; return <A_104> <div className=\"transcation\"> <div className=\"header\"> <div className=\"address\" onClick={() => { setSuccessFlag({ address: \"re.near\", reason: \"test tEST Tetset\" }); }}> Donor </div> <div className=\"address\">Project</div> <div className=\"sort price\" onClick={() => sortDonation(\"price\")}> Amount {currentFilter === \"price\" && <A_129 active={filter.price} />} </div> <div className=\"sort\" onClick={() => sortDonation(\"date\")}> Date {currentFilter === \"date\" && <A_129 active={!filter.date} />} </div> </div> <A_105> <A_107> <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.7549 14.2549H14.9649L14.6849 13.9849C15.6649 12.8449 16.2549 11.3649 16.2549 9.75488C16.2549 6.16488 13.3449 3.25488 9.75488 3.25488C6.16488 3.25488 3.25488 6.16488 3.25488 9.75488C3.25488 13.3449 6.16488 16.2549 9.75488 16.2549C11.3649 16.2549 12.8449 15.6649 13.9849 14.6849L14.2549 14.9649V15.7549L19.2549 20.7449L20.7449 19.2549L15.7549 14.2549ZM9.75488 14.2549C7.26488 14.2549 5.25488 12.2449 5.25488 9.75488C5.25488 7.26488 7.26488 5.25488 9.75488 5.25488C12.2449 5.25488 14.2549 7.26488 14.2549 9.75488C14.2549 12.2449 12.2449 14.2549 9.75488 14.2549Z\" fill=\"#C7C7C7\" /> </svg> </A_107> <A_106 placeholder=\"Search donations\" onChange={handleSearch} /> </A_105> {filteredDonations.length > 0 ? filteredDonations.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation) => { const { donor_id, recipient_id, donated_at_ms, donated_at, project_id } = donation; const projectId = recipient_id || project_id; const isDonorFlagged = checkIfIsFlagged(donor_id); const isProjectFlagged = checkIfIsFlagged(projectId); const projectHref = hrefWithParams(\\`?tab=project&projectId=\\${projectId}\\`); const profileHref = hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`); return <A_108 key={donated_at_ms || donated_at_ms}> {} <AddressItem address={donor_id} isFlagged={isDonorFlagged} href={profileHref} isProject={false} className=\"address\" /> {} <AddressItem address={projectId} isFlagged={isProjectFlagged} href={projectHref} isProject={true} className=\"address project\" /> <div className=\"price\"> <span>Donated</span> <img src={nearLogo} alt=\"NEAR\" /> {A_103(donation).toFixed(2)} </div> <div className=\"date\"> {getTimePassed(donated_at_ms || donated_at)} ago <span> to </span> <AddressItem address={projectId} isFlagged={isProjectFlagged} href={projectHref} isProject={true} className=\"project-mobile-view\" /> </div> </A_108>; }) : <A_108>No donations</A_108>} </div> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: filteredDonations, currentPage, perPage: perPage }, ...props } }} /> {flagAddress != null && <Widget loading=\" \" code={props.alem.componentsCode.FlagModal} props={{ ...{ ...{ flagAddress: flagAddress, setSuccessFlag, onClose: () => setFlagAddress(null) }, ...props } }} />} {successFlag != null && <FlagSuccessModal {...{ successFlag: successFlag, onClose: () => setSuccessFlag(null) }} />} </A_104>; `, Donations: ` const A_129 = (__props__) => <svg {...__props__} style={{ rotate: !__props__.active ? \"0deg\" : \"180deg\"}} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 6L1.0575 7.0575L5.25 2.8725V12H6.75V2.8725L10.935 7.065L12 6L6 0L0 6Z\" fill=\"#7B7B7B\" /> </svg>; const A_110 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; width: 100%; @media screen and (min-width: 375px) and (max-width: 768px) { width: 99%; } @media screen and (max-width: 390px) { width: 98%; }\\`;const A_111 = styled.div\\` display: flex; flex-direction: row; gap: 1.5rem; align-items: center; @media screen and (max-width: 768px) { padding-right: 10px; }\\`;const A_112 = styled.div\\` font-size: 18px; font-weight: 600;\\`;const DonationsCount = styled.div\\` font-size: 16px;\\`;const A_113 = styled.div\\` display: flex; flex-direction: column; align-items: center; border: 0.5px rgba(41, 41, 41, 0.5) solid; box-shadow: 0px 4px 12px -4px rgba(82, 82, 82, 0.2); border-radius: 2px; width: 100%; overflow-x: auto; flex-wrap: nowrap;\\`;const Sort = styled.div\\` display: none; justify-content: space-between; width: 100%; margin-top: 1.5rem; div { display: flex; align-items: center; font-weight: 500; cursor: pointer; gap: 8px; color: #7b7b7b; &.active { color: #292929; } svg { transition: rotate 300ms; } } @media screen and (max-width: 768px) { display: flex; }\\`; const props = props; const { allDonations, potDetail } = props; State.init({ filteredDonations: [], currentFilter: \"date\", filter: { date: false, price: false } }); const { filteredDonations, currentFilter, filter } = state; useEffect(() => { if (allDonations && filteredDonations.length === 0) { const sortedDonations = [...allDonations].reverse(); State.update({ filteredDonations: sortedDonations }); } }, [allDonations]); if (!allDonations) return <div className=\"spinner-border text-secondary\" role=\"status\" />; const searchDonations = (searchTerm) => { const filteredDonations = allDonations.filter((donation) => { const { donor_id, project_id } = donation; const searchFields = [donor_id, project_id]; return searchFields.some((field) => field.toLowerCase().includes(searchTerm.toLowerCase())); }); return filteredDonations; }; const sortDonation = (type) => { const sort = !filter[type]; State.update({ currentFilter: type, filter: { ...filter, [type]: sort } }); if (type === \"price\") { const sortedDonations = filteredDonations.sort((a, b) => sort ? b.total_amount - a.total_amount : a.total_amount - b.total_amount); State.update({ filteredDonations: sortedDonations }); } else if (type === \"date\") { const sortedDonations = filteredDonations.sort((a, b) => { return sort ? b.donated_at - a.donated_at : a.donated_at - b.donated_at; }); State.update({ filteredDonations: sortedDonations }); } }; const handleSearch = ({ target: { value } }) => { const filteredDonations = searchDonations(value); State.update({ filteredDonations }); }; return <A_110> <A_111> <A_112>All donations</A_112> <DonationsCount>{allDonations.length}</DonationsCount> </A_111> <Sort> <div className={\\`\\${currentFilter === \"date\" ? \"active\" : \"\"}\\`} onClick={() => sortDonation(\"date\")}> Sort Date {currentFilter === \"date\" && <A_129 active={!filter.date} />} </div> <div onClick={() => sortDonation(\"price\")} className={\\`\\${currentFilter === \"price\" ? \"active\" : \"\"}\\`}> Sort Amount {currentFilter === \"price\" && <A_129 active={filter.price} />} </div> </Sort> <Widget loading=\" \" code={props.alem.componentsCode.DonationsTable} props={{ ...{ ...{ filteredDonations, filter, handleSearch, sortDonation, currentFilter, potDetail }, ...props } }} /> </A_110>; `, ApplicationReviewModal: ` const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_114 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; background: white; padding: 24px 24px 12px 24px; border-top-left-radius: 6px; border-top-right-radius: 6px; font-weight: 500;\\`;const A_115 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 24px; border-top: 1px #f0f0f0 solid; background: #fafafa; gap: 8px;\\`;const A_116 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-end; background: #fafafa; padding: 12px 24px 24px 24px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; gap: 24px; width: 100%;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const {projectId: projectId, onClose: onClose, newStatus: newStatus} = props; State.init({ reviewMessage: \"\", reviewMessageError: \"\" }); const { reviewMessage, reviewMessageError } = state; const { potId } = props.alem.useParams(); const { ONE_TGAS, SUPPORTED_FTS: { NEAR } } = constants; const MAX_APPLICATION_MESSAGE_LENGTH = 1000; const handleCancel = () => { State.update({ reviewMessage: \"\", reviewMessageError: \"\" }); onClose(); }; const handleSubmit = () => { const args = { project_id: projectId, status: newStatus, notes: reviewMessage }; const transactions = [{ contractName: potId, methodName: \"chef_set_application_status\", deposit: NEAR.toIndivisible(0.01), args, gas: ONE_TGAS.mul(100) }]; Near.call(transactions); }; return <ModalOverlay> <A_114> {newStatus === \"Approved\" ? \"Approve \" : newStatus === \"Rejected\" ? \"Reject \" : \"\"} application from {projectId} </A_114> <A_115> <div>Leave a note *</div> <TextArea {...{ noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Type notes here\", value: reviewMessage, onChange: reviewMessage => State.update({ reviewMessage }), validate: () => { if (reviewMessage.length > MAX_APPLICATION_MESSAGE_LENGTH) { State.update({ reviewMessageError: \\`Application message must be less than \\${MAX_APPLICATION_MESSAGE_LENGTH} characters\\` }); return; } State.update({ reviewMessageError: \"\" }); }, error: reviewMessageError }} /> </A_115> <A_116> <Button {...{ type: \"tertiary\", text: \"Cancel\", onClick: handleCancel }} /> <Button {...{ type: \"primary\", text: \"Submit\", disabled: !reviewMessage || !!reviewMessageError, onClick: handleSubmit }} /> </A_116> </ModalOverlay>; `, Applications: ` function daysAgo(timestamp) { const now = Date.now(); const differenceInTime = now - timestamp; const differenceInDays = Math.floor(differenceInTime / (1000 * 3600 * 24)); return differenceInDays === 0 ? \"< 1 day ago\" : \\`\\${differenceInDays} \\${differenceInDays === 1 ? \"day\" : \"days\"} ago\\`;} const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const APPLICATIONS_FILTERS_TAGS = { Pending: { label: \"Pending\", borderColor: \"#C7C7C7\", color: \"#292929\", background: \"#F6F5F3\", icon: <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M8 0.5C3.86 0.5 0.5 3.86 0.5 8C0.5 12.14 3.86 15.5 8 15.5C12.14 15.5 15.5 12.14 15.5 8C15.5 3.86 12.14 0.5 8 0.5ZM8 14C4.685 14 2 11.315 2 8C2 4.685 4.685 2 8 2C11.315 2 14 4.685 14 8C14 11.315 11.315 14 8 14Z\" fill=\"#7B7B7B\" /> <path d=\"M4.25 9.125C4.87132 9.125 5.375 8.62132 5.375 8C5.375 7.37868 4.87132 6.875 4.25 6.875C3.62868 6.875 3.125 7.37868 3.125 8C3.125 8.62132 3.62868 9.125 4.25 9.125Z\" fill=\"#7B7B7B\" /> <path d=\"M8 9.125C8.62132 9.125 9.125 8.62132 9.125 8C9.125 7.37868 8.62132 6.875 8 6.875C7.37868 6.875 6.875 7.37868 6.875 8C6.875 8.62132 7.37868 9.125 8 9.125Z\" fill=\"#7B7B7B\" /> <path d=\"M11.75 9.125C12.3713 9.125 12.875 8.62132 12.875 8C12.875 7.37868 12.3713 6.875 11.75 6.875C11.1287 6.875 10.625 7.37868 10.625 8C10.625 8.62132 11.1287 9.125 11.75 9.125Z\" fill=\"#7B7B7B\" /> </svg> }, Approved: { label: \"Approved\", color: \"#192C07\", borderColor: \"#9ADD33\", background: \"#F7FDE8\", icon: <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M8 0.5C3.86 0.5 0.5 3.86 0.5 8C0.5 12.14 3.86 15.5 8 15.5C12.14 15.5 15.5 12.14 15.5 8C15.5 3.86 12.14 0.5 8 0.5ZM8 14C4.6925 14 2 11.3075 2 8C2 4.6925 4.6925 2 8 2C11.3075 2 14 4.6925 14 8C14 11.3075 11.3075 14 8 14ZM11.4425 4.685L6.5 9.6275L4.5575 7.6925L3.5 8.75L6.5 11.75L12.5 5.75L11.4425 4.685Z\" fill=\"#629D13\" /> </svg> }, Rejected: { label: \"Rejected\", borderColor: \"#FAA7A8\", color: \"#490813\", background: \"#FEF3F2\", icon: <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M8 0.5C3.86 0.5 0.5 3.86 0.5 8C0.5 12.14 3.86 15.5 8 15.5C12.14 15.5 15.5 12.14 15.5 8C15.5 3.86 12.14 0.5 8 0.5ZM2 8C2 4.685 4.685 2 8 2C9.3875 2 10.6625 2.4725 11.675 3.2675L3.2675 11.675C2.4725 10.6625 2 9.3875 2 8ZM8 14C6.6125 14 5.3375 13.5275 4.325 12.7325L12.7325 4.325C13.5275 5.3375 14 6.6125 14 8C14 11.315 11.315 14 8 14Z\" fill=\"#ED464F\" /> </svg> }}; const A_117 = styled.div\\` display: flex; gap: 2rem; .dropdown { display: none; } @media only screen and (max-width: 768px) { flex-direction: column; gap: 1.5rem; .dropdown { display: flex; } }\\`;const A_118 = styled.div\\` display: grid; width: 286px; border-radius: 6px; padding: 8px 0; border: 1px solid var(--Neutral-500, #7b7b7b); height: fit-content; .item { display: flex; align-items: center; gap: 12px; padding: 0.5rem 1rem; font-size: 14px; cursor: pointer; svg { opacity: 0; transition: all 300ms ease; } &.active { svg { opacity: 1; } } &:hover { svg { opacity: 1; } } } .count { color: #7b7b7b; margin-left: auto; } @media only screen and (max-width: 768px) { display: none; }\\`;const ApplicationsWrapper = styled.div\\` border-radius: 6px; border: 1px solid #7b7b7b; overflow: hidden; display: flex; flex-direction: column; max-width: 711px; width: 100%;\\`;const A_119 = styled.div\\` display: flex; position: relative; svg { position: absolute; left: 1.5rem; top: 50%; transform: translateY(-50%); } input { font-size: 14px; background: #f6f5f3; width: 100%; height: 100%; padding: 8px 24px 8px 60px; border: none; outline: none; } @media only screen and (max-width: 768px) { svg { left: 1rem; } input { padding: 8px 24px 8px 54px; } }\\`;const ApplicationRow = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem; font-size: 14px; position: relative; border-top: 1px solid #c7c7c7; .header { display: flex; gap: 1rem; justify-content: space-between; position: relative; align-items: center; } .header-info { display: flex; gap: 8px; align-items: center; cursor: auto; } .profile-image { margin-right: 8px; width: 24px; height: 24px; } .name { color: #292929; font-weight: 600; } .address { color: #7b7b7b; font-weight: 600; cursor: pointer; transition: all 300ms; position: relative; z-index: 2; &:hover { color: #292929; } } .content { display: flex; flex-direction: column; gap: 1rem; overflow: hidden; transition: all 300ms ease-in-out; max-height: 0; .message { padding-top: 1rem; } .notes { display: flex; flex-direction: column; gap: 8px; .title { color: #7b7b7b; } } button { width: fit-content; } } .arrow { rotate: 180deg; transition: all 300ms; } .toggle-check { position: absolute; top: 0; left: 0; width: 100%; height: 67px; z-index: 1; opacity: 0; cursor: pointer; } .toggle-check:checked + .header .arrow { rotate: 0deg; } .toggle-check:checked + .header + .content { max-height: 100%; } @media only screen and (max-width: 768px) { padding: 1rem; .header-info { flex-wrap: wrap; gap: 0px; } .name { margin: 0 8px; } .date { line-height: 1; width: 100%; margin-left: 2.5rem; } }\\`;const Dot = styled.div\\` width: 6px; height: 6px; background: #7b7b7b; border-radius: 50%; @media only screen and (max-width: 768px) { display: none; }\\`;const Status = styled.div\\` display: flex; padding: 6px 12px; gap: 8px; align-items: center; border-width: 1px; border-style: solid; border-radius: 4px; margin-left: auto; div { font-weight: 500; } svg { width: 1rem; } @media only screen and (max-width: 768px) { padding: 6px; div { display: none; } svg { width: 16px; } }\\`;const DropdownLabel = styled.div\\` display: flex; gap: 10px; align-items: center; .label { font-weight: 500; } .count { display: flex; align-items: center; justify-content: center; border-radius: 50%; background: #ebebeb; }\\`; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const { potDetail: potDetail } = props; const { potId } = props.alem.useParams(); State.init({ newStatus: \"\", projectId: \"\", searchTerm: \"\", allApplications: null, filteredApplications: [], filterVal: \"ALL\" }); const { newStatus, projectId, searchTerm, allApplications, filteredApplications, filterVal } = state; const applications = PotSDK.getApplications(potId); const getApplicationCount = (sortVal) => { if (!applications) return; return applications?.filter((application) => { if (sortVal === \"All\") return true; return application.status === sortVal; })?.length; }; if (applications && !allApplications) { applications.reverse(); State.update({ filteredApplications: applications, allApplications: applications }); } if (!allApplications) return <div className=\"spinner-border text-secondary\" role=\"status\" />; const { owner, admins, chef } = potDetail; const isChefOrGreater = context.accountId === chef || admins.includes(context.accountId || \"\") || context.accountId === owner; const handleApproveApplication = (projectId) => { State.update({ newStatus: \"Approved\", projectId }); }; const handleRejectApplication = (projectId) => { State.update({ newStatus: \"Rejected\", projectId }); }; const handleCloseModal = () => { State.update({ newStatus: \"\", projectId: \"\" }); }; const searchApplications = (searchTerm) => { const filteredApplications = allApplications?.filter((application) => { const { message, project_id, review_notes, status } = application; const searchFields = [message, project_id, review_notes, status]; return searchFields.some((field) => field.toLowerCase().includes(searchTerm.toLowerCase().trim())); }); return filteredApplications; }; const APPLICATIONS_FILTERS = { ALL: { label: \"All applications\", val: \"ALL\", count: getApplicationCount(\"All\") }, PENDING: { label: \"Pending applications\", val: \"PENDING\", count: getApplicationCount(\"Pending\") }, APPROVED: { label: \"Approved applications\", val: \"APPROVED\", count: getApplicationCount(\"Approved\") }, REJECTED: { label: \"Rejected applications\", val: \"REJECTED\", count: getApplicationCount(\"Rejected\") } }; const sortApplications = (key) => { if (key === \"ALL\") { return searchApplications(searchTerm); } const filtered = allApplications?.filter((application) => { return application.status === APPLICATIONS_FILTERS[key].label.split(\" \")[0]; }); return filtered; }; const handleSort = (key) => { const sorted = sortApplications(key); State.update({ filteredApplications: sorted, filterVal: key }); }; const DropdownVal = () => { const digit = APPLICATIONS_FILTERS[filterVal].count.toString().length; return <DropdownLabel> <div className=\"label\">{APPLICATIONS_FILTERS[filterVal].label}</div> <div className=\"count\" style={{ width: \\`\\${24 + (digit - 1) * 6}px\\`, height: \\`\\${24 + (digit - 1) * 6}px\\` }}> {APPLICATIONS_FILTERS[filterVal].count} </div> </DropdownLabel>; }; return <A_117> <div className=\"dropdown\"> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ ...{ sortVal: DropdownVal, showCount: true, sortList: Object.values(APPLICATIONS_FILTERS), FilterMenuCustomStyle: \\`left:0; right:auto;\\`, handleSortChange: ({ val }) => {handleSort(val);} }, ...props } }} /> </div> <A_118> {Object.keys(APPLICATIONS_FILTERS).map((key) => <div key={key} className={\\`item \\${filterVal === key ? \"active\" : \"\"}\\`} onClick={() => handleSort(key)}> <svg width=\"14\" height=\"12\" viewBox=\"0 0 14 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M4.59631 8.9057L1.46881 5.7782L0.403809 6.8357L4.59631 11.0282L13.5963 2.0282L12.5388 0.970703L4.59631 8.9057Z\" fill=\"#7B7B7B\" /> </svg> <div> {APPLICATIONS_FILTERS[key].label}</div> <div className=\"count\">{APPLICATIONS_FILTERS[key].count}</div> </div>)} </A_118> <ApplicationsWrapper> <A_119> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9.81641 8.69141H9.22391L9.01391 8.48891C9.74891 7.63391 10.1914 6.52391 10.1914 5.31641C10.1914 2.62391 8.00891 0.441406 5.31641 0.441406C2.62391 0.441406 0.441406 2.62391 0.441406 5.31641C0.441406 8.00891 2.62391 10.1914 5.31641 10.1914C6.52391 10.1914 7.63391 9.74891 8.48891 9.01391L8.69141 9.22391V9.81641L12.4414 13.5589L13.5589 12.4414L9.81641 8.69141ZM5.31641 8.69141C3.44891 8.69141 1.94141 7.18391 1.94141 5.31641C1.94141 3.44891 3.44891 1.94141 5.31641 1.94141C7.18391 1.94141 8.69141 3.44891 8.69141 5.31641C8.69141 7.18391 7.18391 8.69141 5.31641 8.69141Z\" fill=\"#7B7B7B\" /> </svg> <input type=\"text\" placeholder=\"Search applications\" className=\"search-input\" onChange={(e) => { const results = searchApplications(e.target.value); State.update({ searchTerm: e.target.value, filteredApplications: results }); }} /> </A_119> {filteredApplications.length ? filteredApplications.map(({ project_id, status, message, review_notes, submitted_at }) => { const { borderColor, color, icon, label, background } = APPLICATIONS_FILTERS_TAGS[status]; const profile = Social.getr(\\`\\${project_id}/profile\\`); return <ApplicationRow key={project_id}> <input type=\"checkbox\" className=\"toggle-check\" /> <div className=\"header\"> <div className=\"header-info\"> <ProfileImage style={{}} profile={profile} /> {profile?.name && <div className=\"name\">{_address(profile?.name, 10)}</div>} <OverlayTrigger placement=\"top\" overlay={<Tooltip>{project_id}</Tooltip>}> <a className=\"address\" href={hrefWithParams(\\`?tab=project&projectId=\\${project_id}\\`)} target=\"_blank\"> {_address(project_id, 10)} </a> </OverlayTrigger> <Dot /> <div className=\"date\">{daysAgo(submitted_at)}</div> </div> <Status style={{ borderColor, color, background }}> <div>{label}</div> {icon} </Status> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" className=\"arrow\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294922L0 6.29492L1.41 7.70492L6 3.12492L10.59 7.70492L12 6.29492L6 0.294922Z\" fill=\"#7B7B7B\" /> </svg> </div> <div className=\"content\"> <div className=\"message\">{message}</div> {review_notes && <div className=\"notes\"> <div className=\"title\">Admin notes:</div> <div>{review_notes}</div> </div>} {isChefOrGreater && <> {status !== \"Approved\" && <Button {...{ type: \"secondary\", text: \"Approve\", onClick: () => handleApproveApplication(project_id) }} />} {status !== \"Rejected\" && <Button {...{ type: \"primary\", text: \"Reject\", onClick: () => handleRejectApplication(project_id) }} />} </>} </div> </ApplicationRow>; }) : <div style={{ padding: \"1rem\" }}>No applications to display</div>} </ApplicationsWrapper> {projectId && <Widget loading=\" \" code={props.alem.componentsCode.ApplicationReviewModal} props={{ ...{ projectId: projectId, newStatus: newStatus, onClose: handleCloseModal, ...props } }} />} </A_117>; `, Projects: ` function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const getTeamMembersFromSocialProfileData = profileData => { if (!profileData) return []; const team = profileData.plTeam ? JSON.parse(profileData.plTeam) : profileData.team ? Object.entries(profileData.team).filter(([_, v]) => v !== null).map(([k, _]) => k) : []; return team;}; const getTagsFromSocialProfileData = profileData => { if (!profileData) return []; const DEPRECATED_CATEGORY_MAPPINGS = { \"social-impact\": \"Social Impact\", \"non-profit\": \"NonProfit\", climate: \"Climate\", \"public-good\": \"Public Good\", \"de-sci\": \"DeSci\", \"open-source\": \"Open Source\", community: \"Community\", education: \"Education\" }; const tags = profileData.plCategories ? JSON.parse(profileData.plCategories) : profileData.category ? [profileData.category.text ?? DEPRECATED_CATEGORY_MAPPINGS[profileData.category] ?? \"\"] : []; return tags;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const calculatePayouts = (allPotDonations, totalMatchingPool, blacklistedAccounts) => { const { NADABOT_CONTRACT_ID } = constants; return new Promise((resolve, reject) => { console.log(\"Calculting payouts; ignoring blacklisted donors &/or projects: \", blacklistedAccounts.join(\", \")); console.log(\"totalMatchingPool: \", totalMatchingPool); const projectContributions = []; const allDonors = new Set(); for (const d of allPotDonations) { if (blacklistedAccounts.includes(d.donor_id) || blacklistedAccounts.includes(d.project_id)) { continue; } const amount = new Big(d.total_amount); const val = [d.project_id, d.donor_id, amount]; projectContributions.push(val); allDonors.add(d.donor_id); } const limit = 100; let curIndex = 0; let humanScores = {}; let promises = []; while (curIndex < allDonors.size) { promises.push(Near.asyncView(NADABOT_CONTRACT_ID, \"get_human_score_batch\", { account_ids: Array.from(allDonors).slice(curIndex, curIndex + limit) })); curIndex += limit; } Promise.all(promises).then(res => { for (const r of res) { humanScores = { ...humanScores, ...r }; } }).catch(e => { console.error(\"error fetching human scores. Continuing anyway: \", e); }).finally(() => { console.log(\"human scores: \", humanScores); const contributions = {}; for (const [proj, user, amount] of projectContributions) { if (!humanScores[user] || !humanScores[user].is_human) { console.log(\"skipping non-human: \", user); continue; } if (!contributions[proj]) { contributions[proj] = {}; } contributions[proj][user] = Big(contributions[proj][user] || 0).plus(amount); } console.log(\"contributions: \", contributions); const pairTotals = {}; for (const contribz of Object.values(contributions)) { for (const [k1, v1] of Object.entries(contribz)) { if (!pairTotals[k1]) { pairTotals[k1] = {}; } for (const [k2, v2] of Object.entries(contribz)) { if (!pairTotals[k1][k2]) { pairTotals[k1][k2] = Big(0); } pairTotals[k1][k2] = pairTotals[k1][k2].plus(v1.times(v2).sqrt()); } } } const threshold = Big(\"25000000000000000000000000\"); const totalPot = Big(totalMatchingPool); let bigtot = Big(0); const totals = []; for (const [proj, contribz] of Object.entries(contributions)) { let tot = Big(0); let _num = 0; let _sum = Big(0); for (const [k1, v1] of Object.entries(contribz)) { _num += 1; _sum = _sum.plus(v1); for (const [k2, v2] of Object.entries(contribz)) { if (k2 > k1 || Object.keys(contribz).length === 1) { const sqrt = v1.times(v2).sqrt(); tot = tot.plus(sqrt.div(pairTotals[k1][k2].div(threshold))); } } } bigtot = bigtot.plus(tot); totals.push({ id: proj, number_contributions: _num, contribution_amount_str: _sum.toFixed(0), matching_amount_str: tot.toFixed(0) }); } console.log(\"totals before: \", totals); if (bigtot.gte(totalPot)) { console.log(\"NORMALIZING\"); for (const t of totals) { t.matching_amount_str = Big(t.matching_amount_str).div(bigtot).times(totalPot).toFixed(0); } } let totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } let residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"first round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(\"0\"))) { for (let i = 0; i < totals.length; i++) { let proportion = Big(totals[i].matching_amount_str).div(totalAllocatedBeforeRounding); let additionalAllocation = proportion.times(residual); totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(additionalAllocation).toFixed(0); } console.log(\"CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION\"); totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"second round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(0))) { totals.sort((a, b) => Big(b.matching_amount_str).minus(Big(a.matching_amount_str))); if (residual.gt(Big(0))) { totals[0].matching_amount_str = Big(totals[0].matching_amount_str).plus(residual).toFixed(0); } else { for (let i = 0; i < totals.length; i++) { if (Big(totals[i].matching_amount_str).gt(residual.abs())) { totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(residual).toFixed(0); break; } } } totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"Residual after final adjustment: \", residual.toFixed(0)); } } const payouts = totals.reduce((acc, t) => { acc[t.id] = { totalAmount: t.contribution_amount_str, matchingAmount: t.matching_amount_str, donorCount: t.number_contributions }; return acc; }, {}); resolve(payouts); }); });}; const A_120 = styled.div\\` display: flex; flex-direction: column;\\`;const A_121 = styled.div\\` font-size: 18px; display: flex; align-items: center; gap: 1.5rem; width: fit-content; margin-bottom: 1.5rem; div:first-of-type { font-weight: 600; }\\`;const A_122 = styled.div\\` display: flex; position: relative; width: 100%; border-radius: 6px; border: 0.5px solid #292929; margin-bottom: 0.5rem; svg { position: absolute; left: 1rem; top: 50%; transform: translateY(-50%); } input { font-size: 14px; background: transparent; width: 100%; height: 100%; padding: 12px 16px 12px 3rem; border: none; outline: none; } @media only screen and (max-width: 768px) { svg { left: 1rem; } input { padding: 8px 24px 8px 54px; } }\\`; const props = props; const [searchTerm, setSearchTerm] = useState(\"\"); const [filteredProjects, setFilteredProjects] = useState([]); const [projects, setProjects] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); const [payouts, setPayouts] = useState(null); const { potId } = props.alem.useParams(); const { potDetail, allDonations } = props; if (!projects) { PotSDK.asyncGetApprovedApplications(potId).then((projects) => { setProjects(projects); setFilteredProjects(projects); }); } if (!projects) return <div className=\"spinner-border text-secondary\" role=\"status\" />; const { public_round_start_ms, public_round_end_ms, referral_fee_public_round_basis_points } = potDetail; const now = Date.now(); const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => { const listOfFlagged = []; data.forEach((adminFlaggedAcc) => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); listOfFlagged.push(...addresses); }); setFlaggedAddresses(listOfFlagged); }).catch((err) => console.log(\"error getting the flagged accounts \", err)); } if (!payouts) { if (allDonations.length && flaggedAddresses) calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((payouts) => { setPayouts(payouts ?? []); }).catch((err) => { console.log(\"error while calculating payouts \", err); setPayouts([]); }); } const searchByWords = (searchTerm) => { if (projects.length) { searchTerm = searchTerm.toLowerCase().trim(); setSearchTerm(searchTerm); const updatedProjects = projects.filter((project) => { const profile = Social.getr(\\`\\${project.project_id}/profile\\`); const fields = [project.project_id, project.status, profile.description, profile.name, getTagsFromSocialProfileData(profile).join(\" \"), getTeamMembersFromSocialProfileData(profile).join(\" \")]; return fields.some((item) => (item || \"\").toLowerCase().includes(searchTerm.toLowerCase())); }); setFilteredProjects(updatedProjects); } }; return <A_120> <A_121> <div>Projects</div> <div>{filteredProjects?.length}</div> </A_121> <A_122> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9.81641 8.69141H9.22391L9.01391 8.48891C9.74891 7.63391 10.1914 6.52391 10.1914 5.31641C10.1914 2.62391 8.00891 0.441406 5.31641 0.441406C2.62391 0.441406 0.441406 2.62391 0.441406 5.31641C0.441406 8.00891 2.62391 10.1914 5.31641 10.1914C6.52391 10.1914 7.63391 9.74891 8.48891 9.01391L8.69141 9.22391V9.81641L12.4414 13.5589L13.5589 12.4414L9.81641 8.69141ZM5.31641 8.69141C3.44891 8.69141 1.94141 7.18391 1.94141 5.31641C1.94141 3.44891 3.44891 1.94141 5.31641 1.94141C7.18391 1.94141 8.69141 3.44891 8.69141 5.31641C8.69141 7.18391 7.18391 8.69141 5.31641 8.69141Z\" fill=\"#7B7B7B\" /> </svg> <input type=\"text\" placeholder=\"Search projects\" onChange={(e) => searchByWords(e.target.value)} className=\"search-input\" /> </A_122> <Widget loading=\" \" code={props.alem.componentsCode.ListSection} props={{ ...{ shouldShuffle: true, maxCols: 3, items: filteredProjects, responsive: [{ breakpoint: 1200, items: 2 }, { breakpoint: 870, items: 1 }], renderItem: (project) => {return <Widget loading=\" \" code={props.alem.componentsCode.A_197} props={{ ...{ ...{ potDetail, projects, projectId: project.project_id, allowDonate: publicRoundOpen && project.project_id !== context.accountId, potRferralFeeBasisPoints: referral_fee_public_round_basis_points, payoutDetails: payouts[project.project_id] || { donorCount: 0, matchingAmount: \"0\", totalAmount: \"0\" } }, ...props } }} />;}, ...props } }} /> </A_120>; `, Pot: ` const A_160 = ({ navOptions, nav}) => { const getSelectedNavOption = () => { const navOption = navOptions.find((option) => option.id == nav); return navOption ?? navOptions[0]; }; const NavOptionsContainer = styled.div\\` display: flex; justify-content: flex-start; gap: 2rem; width: 100%; border-bottom: 1px solid #c7c7c7; padding: 0 4rem; .nav-option { font-size: 14px; color: #7b7b7b; padding: 10px 16px; font-weight: 500; white-space: nowrap; border-bottom: 2px solid transparent; transition: 300ms ease; &.disabled { pointer-events: none; cursor: not-allowed; } &.selected { color: #292929; border-bottom-color: #292929; } :hover { border-bottom-color: #292929; text-decoration: none; } } @media screen and (max-width: 768px) { padding: 0px 1rem; overflow-x: scroll; .nav-option.selected { order: -1; } } \\`; return <NavOptionsContainer> {navOptions.map((option) => { const selected = option.id == getSelectedNavOption().id; return option.label ? <a className={\\`nav-option \\${selected && \"selected\"} \\${option.disabled && \"disabled\"}\\`} href={option.href}> {option.label} </a> : \"\"; })} </NavOptionsContainer>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const dateNow = Date.now(); const navOptions = (potId, potDetail) => [{ label: \"Projects\", id: \"projects\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Projects} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=projects\\`) }, { label: \"Applications\", id: \"applications\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Applications} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=applications\\`) }, { label: \"Donations\", id: \"donations\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Donations} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=donations\\`) }, { label: \"Sponsors\", id: \"sponsors\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Sponsors} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=sponsors\\`) }, { label: \"Payouts\", id: \"payouts\", disabled: dateNow < potDetail.public_round_start_ms, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Payouts} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=payouts\\`) }, { label: \"Settings\", id: \"settings\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Settings} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=settings\\`) }]; const A_123 = styled.div\\`\\`;const BodyContainer = styled.div\\` margin-top: 52px; padding: 0 4rem; flex: 1; width: 100%; @media screen and (max-width: 768px) { padding: 0; }\\`; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const { potId, nav: _nav } = props.alem.useParams(); Big.PE = 100; const potDetail = PotSDK.getConfig(potId); const noPot = potDetail === undefined; const loading = potDetail === null; if (loading) return <div className=\"spinner-border text-secondary\" role=\"status\" />; if (noPot) return \"No pot found\"; const now = Date.now(); const applicationNotStarted = now < potDetail.application_start_ms; const applicationOpen = now >= potDetail.application_start_ms && now < potDetail.application_end_ms; const publicRoundOpen = now >= potDetail.public_round_start_ms && now < potDetail.public_round_end_ms; const publicRoundClosed = now >= potDetail.public_round_end_ms; const payoutsPending = publicRoundClosed && !potDetail.cooldown_end_ms; let nav = _nav; if (!nav) { applicationNotStarted ? nav = \"sponsors\" : applicationOpen ? nav = \"applications\" : publicRoundOpen ? nav = \"projects\" : !payoutsPending ? nav = \"donations\" : nav = \"payouts\"; } const allDonationsPaginated = useCache(() => { const limit = 480; const donationsCount = potDetail.public_donations_count; const paginations = [...Array(Math.ceil(donationsCount / limit)).keys()]; try { const allDonations = paginations.map((index) => PotSDK.asyncGetPublicRoundDonations(potId, { from_index: index * limit, limit: limit })); return Promise.all(allDonations); } catch (error) { console.error(\\`error getting public donations\\`, error); return Promise.all([]); } }, \"pot-public-donations\"); const allDonations = allDonationsPaginated ? allDonationsPaginated.flat() : null; const options = navOptions(potId || \"\", potDetail); const SelectedNavComponent = useMemo(() => { return options.find((option) => option.id === nav).source; }, []); return <A_123> <Widget loading=\" \" code={props.alem.componentsCode.HeaderStatus} props={{ ...{ potDetail: potDetail, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.A_48} props={{ ...{ potDetail: potDetail, allDonations: allDonations, ...props } }} /> <A_160 nav={nav} navOptions={options} /> <BodyContainer> {SelectedNavComponent && <SelectedNavComponent allDonations={allDonations} potDetail={potDetail} />} </BodyContainer> </A_123>; `, PotsHome: ` const Outer = styled.div\\` display: flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; @keyframes beacon { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.3); } } animation: beacon 1.5s infinite;\\`;const Inner = styled.div\\` width: 10px; height: 10px; border-radius: 50%;\\`; const Indicator = ({ animate, colorInner, colorOuter}) => { return <Outer style={{ backgroundColor: colorOuter, animationPlayState: animate ? \"running\" : \"paused\" }}> <Inner style={{ backgroundColor: colorInner }} /> </Outer>;}; const TagContainer = styled.div\\` display: flex; justify-content: center; align-items: center; text-align: center; padding: 6px 8px; border-radius: 4px;\\`;const TagText = styled.span\\` font-size: 14px;\\`; const Tag = __props__ => { const { backgroundColor, borderColor, textColor, text } = __props__; const textStyle = __props__.textStyle || {}; return <TagContainer style={{ backgroundColor: backgroundColor || \"#ffffff\", border: \\`1px solid \\${borderColor || \"#000000\"}\\`, boxShadow: \\`0px -0.699999988079071px 0px \\${borderColor} inset\\` }}> {__props__.preElements} <TagText style={{ color: textColor || \"#000000\", ...textStyle }}> {text} </TagText> </TagContainer>;}; const nearToUsd = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then(res => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\");const yoctosToUsd = amount => { return nearToUsd ? \"~\\$\" + formatWithCommas(new Big(amount).mul(nearToUsd).div(1e24).toFixed(2)) : \"0\";}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; function daysUntil(timestamp) { const now = Date.now(); const differenceInTime = timestamp - now; const differenceInDays = Math.ceil(differenceInTime / (1000 * 3600 * 24)); return \\`\\${differenceInDays} \\${differenceInDays === 1 ? \"day\" : \"days\"}\\`;} const tagsList = potConfig => { const { application_start_ms, application_end_ms, public_round_start_ms, public_round_end_ms, cooldown_end_ms, all_paid_out } = potConfig; const now = Date.now(); const applicationOpen = now >= application_start_ms && now < application_end_ms; const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; const cooldownPending = public_round_end_ms && now >= public_round_end_ms && !cooldown_end_ms; const cooldownOpen = now >= public_round_end_ms && now < cooldown_end_ms; const payoutsPending = cooldown_end_ms && now >= cooldown_end_ms && !all_paid_out; const payoutsCompleted = all_paid_out; const tags = [{ backgroundColor: \"#EFFEFA\", borderColor: \"#33DDCB\", textColor: \"#023131\", text: \"Sponsorship Open\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#CAFDF3\", colorInner: \"#33DDCB\", animate: true }, visibility: now < application_start_ms }, { backgroundColor: \"#EFFEFA\", borderColor: \"#33DDCB\", textColor: \"#023131\", text: daysUntil(application_end_ms) + \" left to apply\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#CAFDF3\", colorInner: \"#33DDCB\", animate: true }, visibility: applicationOpen }, { backgroundColor: \"#F7FDE8\", borderColor: \"#9ADD33\", textColor: \"#192C07\", text: daysUntil(public_round_end_ms) + \" left to donate\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#D7F5A1\", colorInner: \"#9ADD33\", animate: true }, visibility: publicRoundOpen }, { backgroundColor: \"#F5F3FF\", borderColor: \"#A68AFB\", textColor: \"#2E0F66\", text: \"Cooldown pending\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#EDE9FE\", colorInner: \"#A68AFB\", animate: true }, visibility: cooldownPending }, { backgroundColor: \"#F5F3FF\", borderColor: \"#A68AFB\", textColor: \"#2E0F66\", text: \"Challenge period\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#EDE9FE\", colorInner: \"#A68AFB\", animate: true }, visibility: cooldownOpen }, { backgroundColor: \"#F7FDE8\", borderColor: \"#9ADD33\", textColor: \"#192C07\", text: \"Payouts pending\", preElementsProps: { colorOuter: \"#D7F5A1\", colorInner: \"#9ADD33\", animate: true }, textStyle: { fontWeight: 500, marginLeft: \"8px\" }, visibility: payoutsPending }, { backgroundColor: \"#464646\", borderColor: \"#292929\", textColor: \"#FFF\", text: \"Payouts completed\", preElementsProps: { colorOuter: \"#656565\", colorInner: \"#A6A6A6\", animate: false }, textStyle: { fontWeight: 500, marginLeft: \"8px\" }, visibility: payoutsCompleted }]; return tags;}; const A_133 = styled.a\\` display: flex; flex-direction: column; min-width: 320px; min-height: 300px; border-radius: 8px; background: white; box-shadow: 0px -2px 0px 0px #464646 inset, 0px 0px 0px 1px #464646; padding-bottom: 5px; height: 100%; &:hover { text-decoration: none; cursor: pointer; }\\`;const CardSection = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 16px; padding: 32px; width: 100%; height: 100%;\\`;const A_134 = styled.div\\` color: #292929; font-size: 22px; font-weight: 600; line-height: 28px; word-wrap: break-word; > div { font-weight: inherit; display: flex; align-items: baseline; } .usd-amount { font-size: 14px; font-weight: 400; margin-left: 0.25rem; } .text { font-size: 14px; color: #7b7b7b; margin-left: 0.5rem; }\\`;const A_135 = styled.div\\` color: #525252; font-size: 16px; font-weight: 400; line-height: 28px; word-wrap: break-word; a { color: rgb(123, 123, 123); }\\`;const Subtitle = styled.span\\` color: #7b7b7b; font-size: 14px; font-weight: 600; line-height: 20px; word-wrap: break-word;\\`; const MAX_DESCRIPTION_LENGTH = 100;const MAX_TITLE_LENGTH = 36;const PotCard = ({ potId}) => { const potConfig = PotSDK.getConfig(potId); if (!potConfig) return <A_133 style={{ justifyContent: \"center\", alignItems: \"center\" }}> {potConfig == null ? <div className=\"spinner-border text-secondary\" role=\"status\" /> : <div>Pot {potId} not found.</div>} </A_133>; const { pot_name, pot_description, matching_pool_balance } = potConfig; const amountNear = yoctosToNear(matching_pool_balance, true); const amountUsd = yoctosToUsd(matching_pool_balance); const description = !pot_description ? \"No description\" : pot_description.length > MAX_DESCRIPTION_LENGTH ? \\`\\${pot_description.slice(0, MAX_DESCRIPTION_LENGTH)}...\\` : pot_description; const title = !pot_name ? \"No title\" : pot_name.length > MAX_TITLE_LENGTH ? \\`\\${pot_name.slice(0, MAX_TITLE_LENGTH)}...\\` : pot_name; const tags = tagsList(potConfig); return <A_133 href={hrefWithParams(\\`?tab=pot&potId=\\${potId}\\`)}> <CardSection> <A_134>{title}</A_134> <A_135> <Markdown text={description} /> </A_135> </CardSection> <CardSection style={{ background: \"#F6F5F3\", borderTop: \"1px #7B7B7B solid\", marginTop: \"auto\", height: \"fit-content\" }}> <A_134> <div> {amountNear} {amountUsd && <span className=\"usd-amount\">{amountUsd}</span>} <span className=\"text\">in pot</span> </div> </A_134> {tags.map((tag) => tag.visibility ? <Tag {...tag} preElements={<Indicator {...tag.preElementsProps || {}} />} key={tag.text} /> : \"\")} </CardSection> </A_133>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const filterBy = { \"no-label\": [{ label: \"Application open\", val: \"application_open\" }, { label: \"Matching round open\", val: \"round_open\" }, { label: \"Application closed\", val: \"application_closed\" }, { label: \"Challenge period\", val: \"cooldown\" }]};const sortOptions = { \"no-label\": [{ label: \"Most to least in pot\", val: \"least_pots\" }, { label: \"Least to most in pot\", val: \"most_pots\" }, { label: \"Most to least donations\", val: \"most_donations\" }, { label: \"Least to most donations\", val: \"least_donations\" }]};const currentDate = Date.now();const filters = { application_not_started: round => currentDate < round.application_start_ms, application_open: round => currentDate > round.application_start_ms && currentDate < round.application_end_ms, application_closed: round => currentDate > round.application_end_ms, round_end: round => currentDate > round.public_round_end_ms, round_open: round => currentDate > round.public_round_start_ms && currentDate < round.public_round_end_ms, cooldown: round => currentDate > round.public_round_end_ms && currentDate < round.cooldown_end_ms, completed: round => round.all_paid_out};const potsSort = { active: { check: filters.round_open, time: \"public_round_end_ms\", items: [] }, cooldown: { check: filters.cooldown, time: \"cooldown_end_ms\", items: [] }, application: { check: filters.application_open, time: \"application_end_ms\", items: [] }, not_started: { check: filters.application_not_started, time: \"application_start_ms\", items: [] }, rest: { check: round => true, time: \"application_start_ms\", items: [] }}; const A_124 = styled.div\\` margin-bottom: 1rem; display: flex; align-items: center; gap: 1rem; font-size: 18px; font-weight: 600; .span { font-weight: 600; }\\`;const A_125 = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; padding-bottom: 48px; .content { display: flex; flex-direction: column; width: 100%; padding: 0 64px; margin-top: 3rem; } .header { display: flex; align-items: center; margin-bottom: 1rem; .filters { gap: 1rem; display: flex; align-items: center; .sort { width: 286px; flex-direction: column; padding: 0.5rem; gap: 0; .title { display: none; } .option { border: none; width: 100%; padding: 0.5rem; } } } } @media only screen and (max-width: 768px) { .content { padding: 0 20px; } .header { flex-direction: column; align-items: flex-start; gap: 1rem; } }\\`;const A_126 = styled.div\\` height: 1px; width: 100%; background: #c7c7c7; margin: 3rem 0;\\`; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_127 = styled.div\\` display: flex; flex-direction: column; position: relative; width: 100%; justify-content: center; min-height: 400px; overflow: hidden; .background { position: absolute; pointer-events: none; height: 100%; left: 0; top: 0; } .content { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: center; padding: 64px; } .sub-title { letter-spacing: 1.12px; font-weight: 500; font-size: 14px; margin-top: 0; margin-bottom: 24px; text-transform: uppercase; } .title { letter-spacing: -0.4px; font-weight: 500; font-size: 40px; font-family: \"Lora\"; margin: 0; } .btns { display: flex; align-items: center; gap: 2rem; margin-top: 40px; a { padding: 12px 16px; width: 180px; display: flex; align-items: center; justify-content: center; font-weight: 500; border-radius: 6px; box-shadow: 0px -2px 0px 0px #464646 inset, 0px 0px 0px 1px #464646; text-decoration: none; color: #292929; transition: all 300ms; &:first-of-type { color: white; background: #dd3345; &:hover { } } &:hover { background: #292929; color: white; } } } @media only screen and (max-width: 768px) { .content { padding: 64px 20px; } .title { font-size: 36px; } .btns { flex-direction: column; gap: 1rem; margin-top: 24px; } .line-break { display: none; } } @media only screen and (max-width: 480px) { .btns a { width: 100%; padding: 12px 0; } }\\`; const svgContent = \\`<svg width=\"1320\" height=\"332\" viewBox=\"0 0 1320 332\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"1320\" height=\"1\" fill=\"#FEF6EE\"/><mask id=\"mask0_11854_22101\" style=\"mask-type:alpha\" maskUnits=\"userSpaceOnUse\" x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\"><rect x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\" fill=\"url(#paint0_radial_11854_22101)\"/></mask><g mask=\"url(#mask0_11854_22101)\"><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" fill=\"#FEF6EE\"/><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" fill=\"#FEF6EE\"/><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" fill=\"#F8D3B0\"/><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" fill=\"#F8D3B0\"/><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" fill=\"#F8D3B0\"/><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" fill=\"#F8D3B0\"/><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" fill=\"#F8D3B0\"/><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" fill=\"#F8D3B0\"/><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" fill=\"#F8D3B0\"/><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" fill=\"#F8D3B0\"/><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" fill=\"#FEF6EE\"/><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" fill=\"#FEF6EE\"/><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" fill=\"#FEF6EE\"/><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" fill=\"#F8D3B0\"/><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" fill=\"#F8D3B0\"/><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" fill=\"#F8D3B0\"/><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" fill=\"#F8D3B0\"/><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" fill=\"#F8D3B0\"/><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" fill=\"#F8D3B0\"/><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" fill=\"#F8D3B0\"/><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" fill=\"#F8D3B0\"/><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" stroke=\"#F4B37D\"/></g></g><defs><radialGradient id=\"paint0_radial_11854_22101\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(660 -158.5) rotate(90) scale(724.5 1018.83)\"><stop stop-color=\"#FCE9D5\"/><stop offset=\"0.855072\" stop-color=\"#FEF6EE\" stop-opacity=\"0\"/></radialGradient></defs></svg>\\`;const HomeBannerStyle = { backgroundImage: \\`url(\"data:image/svg+xml;charset=utf-8,\\${encodeURIComponent(svgContent)}\")\\`, backgroundSize: \"cover\", backgroundRepeat: \"no-repeat\", backgroundColor: \"#FEF6EE\"}; const A_128 = () => { const canDeploy = PotFactorySDK.canUserDeployPot(context.accountId); return <A_127 style={{ ...HomeBannerStyle }}> <div className=\"content\"> <h3 className=\"sub-title\">Explore Pots</h3> <h1 className=\"title\"> Donate to Matching Rounds <br className=\"line-break\" /> to Get Your Contributions Amplified. </h1> <div className=\"btns\"> {canDeploy && <a href={hrefWithParams(\\`?tab=deploypot\\`)}>Deploy Pot</a>} <a href=\"https://wtfisqf.com\" target=\"_blank\"> Learn More </a> </div> </div> </A_127>;}; const [pots, setPots] = useState(null); const [inProgressRounds, setInProgressRounds] = useState([]); const [filteredRounds, setFilteredRounds] = useState([]); const [completedRounds, setCompletedRounds] = useState([]); if (!pots) { PotFactorySDK.asyncGetPots().then((pots) => { pots.forEach(({ id }) => { PotSDK.asyncGetConfig(id).then((potConfig) => setPots((prevPot) => ({ ...prevPot, [id]: { ...potConfig, id } }))); }); }); } const compareFunction = (pots) => { const listOfPots = {}; const states = Object.keys(potsSort); pots.forEach((pot) => { Object.keys(potsSort).some((type) => { const { check, items } = potsSort[type]; if (check(pot)) { potsSort[type].items = [...items, pot]; return true; } }); }); const inProgressPots = []; Object.values(potsSort).forEach(({ items, time }) => { items.sort((a, b) => a[time] - b[time]); inProgressPots.push(...items); }); return inProgressPots; }; useEffect(() => { if (pots) { const potsVal = Object.values(pots); const completed = []; let inprogress = []; potsVal.forEach((round) => { if (filters.completed(round)) { completed.push(round); } else { inprogress.push(round); } }); inprogress = compareFunction(inprogress); setFilteredRounds(inprogress); setInProgressRounds(inprogress); setCompletedRounds(completed); } }, [pots]); const handleFilter = (selected) => { const selectedList = Object.values(selected)[0]; if (selectedList.length === 0) { return setFilteredRounds(inProgressRounds); } const filteredRounds = [...inProgressRounds].filter((round) => selectedList.some((key) => { return filters[key](round) === true; })); setFilteredRounds(filteredRounds); }; const handleSort = ({ val }) => { const sortedRounds = filteredRounds; switch (val) { case \"least_pots\": sortedRounds.sort((a, b) => Big(b.matching_pool_balance) - Big(a.matching_pool_balance)); break; case \"most_pots\": sortedRounds.sort((a, b) => Big(a.matching_pool_balance) - Big(b.matching_pool_balance)); break; case \"most_donations\": sortedRounds.sort((a, b) => Big(b.total_public_donations) - Big(a.total_public_donations)); break; case \"least_donations\": sortedRounds.sort((a, b) => Big(a.total_public_donations) - Big(b.total_public_donations)); break; } setFilteredRounds(sortedRounds); }; return <A_125> <A_128 /> <div className=\"content\"> <div className=\"header\"> <A_124 style={{ marginRight: \"auto\", marginBottom: 0 }}> Active Pots <span>{filteredRounds.length}</span> </A_124> <div className=\"filters\"> <Widget loading=\" \" code={props.alem.componentsCode.FilterDropdown} props={{ ...{ options: filterBy, onClick: handleFilter, multipleOptions: true, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.FilterDropdown} props={{ ...{ options: sortOptions, onClick: handleSort, label: \"Sort\", menuClass: \"sort\", labelIcon: \"right\", ...props } }} /> </div> </div> {filteredRounds.length === 0 && <div>No pots</div>} <Widget loading=\" \" code={props.alem.componentsCode.ListSection} props={{ ...{ items: filteredRounds, renderItem: (pot) => <PotCard potId={pot.id} key={pot.id} />, maxCols: 3, responsive: [{ breakpoint: 1114, items: 2 }, { breakpoint: 768, items: 1 }], ...props } }} /> <A_126 /> <A_124> Completed Pots <span>{completedRounds.length}</span> </A_124> <Widget loading=\" \" code={props.alem.componentsCode.ListSection} props={{ ...{ items: completedRounds, renderItem: (pot) => <PotCard potId={pot.id} key={pot.id} />, maxCols: 3, responsive: [{ breakpoint: 1114, items: 2 }, { breakpoint: 768, items: 1 }], ...props } }} /> </div> </A_125>; `, A_131: ` const Container = styled.div\\` display: flex; flex-direction: column; gap: 1.5rem;\\`;const Title = styled.div\\` font-size: 24px; font-weight: 600;\\`;const Funding = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; background: #fff; overflow: hidden; .header { border-bottom: 0.5px solid #7b7b7b; padding: 0.5rem 1rem; div { font-weight: 600; } @media screen and (max-width: 768px) { .tab { display: none; } .funding { display: block; } } } .funding-row { padding: 1rem; } .header, .funding-row { display: flex; justify-content: space-between; gap: 2rem; font-size: 14px; flex-wrap: wrap; @media screen and (max-width: 768px) { gap: 4px; } } .tab { display: flex; align-items: center; gap: 0.5rem; width: 156px; justify-content: left; &.sort { cursor: pointer; svg { transition: rotate 300ms; } } @media screen and (max-width: 768px) { white-space: nowrap; width: 60px; } } .funding { flex: 1; } .price { gap: 1rem; font-weight: 600; justify-content: left; svg { width: 1.5em; } } .date { justify-content: right; } @media screen and (max-width: 768px) { .price { gap: 0.5rem; } .date { width: 100%; justify-content: left; color: #7b7b7b; margin-left: 2.5rem; } }\\`;const FundingSrc = styled.div\\` display: flex; align-items: center; gap: 1rem; flex: 1; max-width: 100%; gap: 1rem; .profile-image { width: 24px; height: 24px; } .funding-src { display: flex; flex-direction: column; .pot-name { color: inherit; font-weight: inherit; display: none; } a { color: #292929; transition: 300ms; font-weight: 600; :hover { text-decoration: none; color: #dd3345; } } .type { color: #7b7b7b; } } @media screen and (max-width: 768px) { .funding-src .type { display: none; } .funding-src .pot-name { display: inline-block; } }\\`;const SearchBar = styled.div\\` display: flex; align-items: center; background: #f6f5f3; position: relative; svg { width: 18px; left: 1rem; top: 50%; transform: translateY(-50%); position: absolute; pointer-events: none; } input { width: 100%; height: 100%; padding: 1rem; padding-left: 50px; border: none; background: transparent; :focus { outline: none; } }\\`;const Stats = styled.div\\` display: flex; flex-wrap: wrap; margin: 24px 0; align-items: center; .item { display: flex; height: fit-content; gap: 8px; padding-right: 1rem; align-items: center; :nth-child(2) { border-right: 1px solid #7b7b7b; border-left: 1px solid #7b7b7b; padding-left: 1rem; } :nth-child(3) { padding-left: 1rem; } .item-value { font-weight: 600; } @media screen and (max-width: 768px) { display: none; } } .dropdown { margin-left: auto; .dropdown-menu-custom { left: auto; right: 0; } @media screen and (max-width: 480px) { margin-right: auto; margin-left: 0; } }\\`;const Sort = styled.div\\` display: none; justify-content: space-between; width: 100%; div { display: flex; align-items: center; font-weight: 500; cursor: pointer; gap: 8px; color: #7b7b7b; svg { transition: rotate 300ms; } &.active { color: #292929; } } @media screen and (max-width: 768px) { display: flex; }\\`;const DropdownLabelWrapper = styled.div\\` display: flex; gap: 10px; align-items: center; .label { font-weight: 500; } .count { display: flex; align-items: center; justify-content: center; border-radius: 50%; background: #ebebeb; }\\`;const ImgIcon = styled.img\\` width: 21px; height: 21px;\\`; const A_129 = (__props__) => <svg {...__props__} style={{ rotate: !__props__.active ? \"0deg\" : \"180deg\"}} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 6L1.0575 7.0575L5.25 2.8725V12H6.75V2.8725L10.935 7.065L12 6L6 0L0 6Z\" fill=\"#7B7B7B\" /> </svg>; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const PotIcon = __props__ => <svg {...__props__} viewBox=\"0 0 20 21\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M10 3C10.5523 3 11 2.55228 11 2C11 1.44772 10.5523 1 10 1C9.44772 1 9 1.44772 9 2C9 2.55228 9.44772 3 10 3ZM12 2C12 2.37912 11.8945 2.7336 11.7113 3.03569C14.6721 3.33449 17.0882 5.47841 17.7921 8.3C17.9279 8.84425 18 9.41371 18 10H16.3H3.7H2C2 9.41371 2.07208 8.84425 2.20786 8.3C2.9118 5.47841 5.3279 3.33449 8.28871 3.03569C8.10549 2.7336 8 2.37912 8 2C8 0.895431 8.89543 0 10 0C11.1046 0 12 0.895431 12 2ZM9 4.7C6.66751 4.7 4.68694 6.20674 3.97852 8.3H16.0215C15.3131 6.20674 13.3325 4.7 11 4.7H9ZM0 11H2H4H16H18H20V13H18V19C18 20.1046 17.1046 21 16 21H4C2.89543 21 2 20.1046 2 19V13H0V11ZM4 19V13H16V19H4Z\" fill=\"#7B7B7B\" /> </svg>; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const getPotDonations = (potId, potDetail, accountId, potDonations, setPotDonations) => { return PotSDK.asyncGetDonationsForDonor(potId, accountId).then((donations) => { donations = donations.filter((donations) => donations.donor_id === accountId); const updatedDonations = donations.map((donation) => ({ ...donation, base_currency: potDetail.base_currency, pot_name: potDetail.pot_name, pot_id: potId, type: donation.project_id ? \"matched\" : \"sponsorship\" })); if (potDonations[potId]) return \"\"; setPotDonations((prevpotDonations) => { return { ...prevpotDonations, [potId]: updatedDonations }; }); }).catch(() => { if (potDonations[potId]) return \"\"; setPotDonations((prevpotDonations) => { return { ...prevpotDonations, [potId]: [] }; }); });};const A_130 = (searchTerm, totalDonations) => { const filteredApplications = totalDonations.filter((item) => { const searchIn = [item.pot_name || \"\", item.recipient_id || \"\", item.project_id || \"\", item.donor_id || \"\", item.pot_id || \"\"]; return searchIn.some((item) => item.toLowerCase().includes(searchTerm.toLowerCase())); }); return filteredApplications;};const filterDonations = (sortVal, sortList, search, totalDonations) => { const displayedDonations = A_130(search, totalDonations); let filtered; if (sortVal && sortVal !== \"all\") { filtered = displayedDonations.filter((donation) => { return sortList[donation.type].val === sortVal; }); return filtered; } else { return displayedDonations; }};const getName = (donation) => { switch (donation.type) { case \"direct\": return donation.recipient_id; case \"sponsorship\": return donation.pot_name; case \"payout\": return donation.pot_name; case \"matched\": return donation.project_id; default: return donation.recipient_id; }};const addTrailingZeros = (number) => { if (number < 100 && number >= 0.1) return number.toFixed(1); return number;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const NearOutline = __props__ => { return <svg {...__props__} viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <circle cx=\"9\" cy=\"9\" r=\"6.75\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.9509 5.25C11.6728 5.25 11.4153 5.39375 11.2697 5.63062L9.70187 7.95812C9.67925 7.99164 9.66959 8.03224 9.67469 8.07235C9.67979 8.11246 9.69932 8.14935 9.72961 8.17612C9.75991 8.2029 9.79892 8.21775 9.83935 8.21788C9.87979 8.21802 9.91889 8.20344 9.94937 8.17687L11.4925 6.83875C11.5015 6.8306 11.5127 6.82527 11.5247 6.82339C11.5367 6.82151 11.549 6.82318 11.5601 6.82819C11.5712 6.8332 11.5806 6.84133 11.5871 6.85159C11.5936 6.86184 11.597 6.87378 11.5969 6.88594V11.0766C11.5969 11.0894 11.5929 11.102 11.5856 11.1125C11.5782 11.1231 11.5677 11.1311 11.5556 11.1355C11.5435 11.1398 11.5304 11.1404 11.5179 11.137C11.5055 11.1336 11.4945 11.1265 11.4862 11.1166L6.82187 5.53281C6.74688 5.44415 6.65345 5.37291 6.5481 5.32407C6.44274 5.27522 6.328 5.24994 6.21187 5.25H6.04906C5.83714 5.25 5.63389 5.33419 5.48404 5.48404C5.33419 5.63389 5.25 5.83714 5.25 6.04906V11.9509C5.25 12.125 5.30683 12.2943 5.41184 12.4331C5.51686 12.5719 5.66432 12.6726 5.83182 12.7199C5.99931 12.7672 6.17767 12.7586 6.33979 12.6952C6.50191 12.6319 6.63893 12.5174 6.73 12.3691L8.29781 10.0416C8.32044 10.008 8.3301 9.96745 8.32499 9.92733C8.31989 9.88722 8.30037 9.85034 8.27007 9.82356C8.23977 9.79678 8.20077 9.78194 8.16033 9.7818C8.11989 9.78166 8.08079 9.79624 8.05031 9.82281L6.50719 11.1612C6.49813 11.1693 6.48693 11.1746 6.47494 11.1764C6.46295 11.1782 6.4507 11.1765 6.43967 11.1714C6.42864 11.1664 6.41931 11.1583 6.41282 11.148C6.40633 11.1378 6.40296 11.1259 6.40312 11.1137V6.9225C6.4031 6.90963 6.40705 6.89707 6.41443 6.88652C6.42181 6.87598 6.43227 6.86798 6.44437 6.8636C6.45647 6.85922 6.46963 6.85869 6.48205 6.86207C6.49447 6.86546 6.50554 6.87259 6.51375 6.8825L11.1775 12.4669C11.3294 12.6462 11.5525 12.7497 11.7875 12.75H11.9503C12.1622 12.7501 12.3655 12.666 12.5154 12.5163C12.6654 12.3666 12.7497 12.1635 12.75 11.9516V6.04906C12.7499 5.83716 12.6657 5.63397 12.5159 5.48413C12.366 5.3343 12.1628 5.25008 11.9509 5.25Z\" fill=\"#292929\" /> </svg>;}; const { SUPPORTED_FTS } = constants; const PER_PAGE = 30; const [ftMetadata, setFtMetadata] = useState({}); const [currentPage, setCurrentPage] = useState(1); const [filter, setFilter] = useState({ date: false, price: false }); const [currentFilter, setCurrentFilter] = useState(\"date\"); const [sort, setSort] = useState(\"all\"); const [search, setSearch] = useState(\"\"); const [potDonations, setPotDonations] = useState({}); const [directDonations, setDirectDonations] = useState(null); const [filteredDonations, setFilteredDonations] = useState(null); const { accountId } = props.alem.useParams(); const pots = PotFactorySDK.getPots(); let donationsForDonor = DonateSDK.getDonationsForDonor(accountId); if (donationsForDonor && !directDonations) { donationsForDonor = donationsForDonor.map((donation) => ({ ...donation, type: \"direct\" })); setDirectDonations(donationsForDonor); } if (pots && !potDonations[pots[pots.length - 1].id]) { pots.forEach((pot) => { PotSDK.asyncGetConfig(pot.id).then((potDetail) => { getPotDonations(pot.id, potDetail, accountId, potDonations, setPotDonations); }); }); } const [totalDonations, sponsorships, matchingRoundDonations] = useMemo(() => { const potDonationsValue = Object.values(potDonations).flat(); const sponsorships = potDonationsValue.filter((donation) => donation.type === \"sponsorship\"); const matchingRoundDonations = potDonationsValue.filter((donation) => donation.type === \"matched\"); const allDonations = [...(directDonations || []), ...potDonationsValue]; allDonations.sort((a, b) => (b.donated_at || b.donated_at_ms) - (a.donated_at || a.donated_at_ms)); setFilteredDonations(allDonations); return [allDonations, sponsorships, matchingRoundDonations]; }, [potDonations, directDonations]); const handleSortChange = ({ val }) => { const filtered = filterDonations(val, sortList, search, totalDonations); setFilteredDonations(filtered); setSort(val); }; const [totalDonationAmountNear] = useMemo(() => { let total = Big(0); totalDonations.forEach((donation) => { if (donation.ft_id === \"near\" || donation.base_currency === \"near\") { total = total.plus(Big(donation.total_amount || donation.amount)); } }); const totalDonationAmountNear = SUPPORTED_FTS.NEAR.fromIndivisible(total.toString()); return [totalDonationAmountNear]; }, [totalDonations]); useEffect(() => { const metadata = {}; const ftIds = totalDonations.reduce((acc, donation) => { if (donation.ft_id && donation.ft_id !== \"near\") { acc.add(donation.ft_id); } return acc; }, new Set()); ftIds.forEach((ftId) => { Near.asyncView(ftId, \"ft_metadata\", {}).then((ftMetadata) => { metadata[ftId] = ftMetadata; if (Object.keys(metadata).length === ftIds.size) { setFtMetadata(metadata); } }).catch((e) => { console.error(\"error getting ft metadata: \", e); }); }); }, [totalDonations]); const getDate = (donation) => donation.donated_at_ms || donation.donated_at; const sortDonation = (type) => { setCurrentFilter(type); const sort = !filter[type]; setFilter({ ...filter, [type]: sort }); if (type === \"price\") { const sortedDonations = filteredDonations.sort((a, b) => sort ? b.total_amount - a.total_amount : a.total_amount - b.total_amount); setFilteredDonations(sortedDonations); } else if (type === \"date\") { const sortedDonations = filteredDonations.sort((a, b) => { return sort ? getDate(a) - getDate(b) : getDate(b) - getDate(a); }); setFilteredDonations(sortedDonations); } }; const sortList = { all: { label: \"All donations\", val: \"all\", count: totalDonations?.length }, direct: { label: \"Direct donations\", val: \"direct\", count: directDonations?.length }, matched: { label: \"Matched donations\", val: \"matched\", count: matchingRoundDonations?.length }, sponsorship: { label: \"Sponsorships\", val: \"sponsorship\", count: sponsorships?.length } }; const DropdownLabel = () => { const digit = sortList[sort].count.toString().length; return <DropdownLabelWrapper> <div className=\"label\">{sortList[sort].label}</div> <div className=\"count\" style={{ width: \\`\\${24 + (digit - 1) * 6}px\\`, height: \\`\\${24 + (digit - 1) * 6}px\\` }}> {sortList[sort].count} </div> </DropdownLabelWrapper>; }; return <Container> <Stats> {totalDonationAmountNear && <div className=\"item\"> <div className=\"item-value\"> {\" \"} {totalDonationAmountNear}N{A_236 && <span>~\\${(totalDonationAmountNear * A_236).toFixed(2)}</span>} </div> <div className=\"item-label\">Donated</div> </div>} <div className=\"dropdown\"> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ handleSortChange: handleSortChange, sortList: Object.values(sortList), FilterMenuCustomClass: \"dropdown-menu-custom\", showCount: true, sortVal: <DropdownLabel />, ...props } }} /> </div> </Stats> <Sort> <div onClick={() => sortDonation(\"date\")} className={\\`\\${currentFilter === \"date\" ? \"active\" : \"\"}\\`}> Sort Date {currentFilter === \"date\" && <A_129 active={!filter.date} />} </div> <div onClick={() => sortDonation(\"price\")} className={\\`\\${currentFilter === \"price\" ? \"active\" : \"\"}\\`}> Sort Amount {currentFilter === \"price\" && <A_129 active={filter.price} />} </div> </Sort> <Funding> <div className=\"header\"> <div className=\"funding tab\">Project Name</div> <div className=\"tab sort\" onClick={() => sortDonation(\"price\")}> Amount {currentFilter === \"price\" && <A_129 active={filter.price} />} </div> <div className=\"tab sort date\" onClick={() => sortDonation(\"date\")}> Date {currentFilter === \"date\" && <A_129 active={!filter.date} />} </div> </div> <SearchBar> <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.7549 14.2549H14.9649L14.6849 13.9849C15.6649 12.8449 16.2549 11.3649 16.2549 9.75488C16.2549 6.16488 13.3449 3.25488 9.75488 3.25488C6.16488 3.25488 3.25488 6.16488 3.25488 9.75488C3.25488 13.3449 6.16488 16.2549 9.75488 16.2549C11.3649 16.2549 12.8449 15.6649 13.9849 14.6849L14.2549 14.9649V15.7549L19.2549 20.7449L20.7449 19.2549L15.7549 14.2549ZM9.75488 14.2549C7.26488 14.2549 5.25488 12.2449 5.25488 9.75488C5.25488 7.26488 7.26488 5.25488 9.75488 5.25488C12.2449 5.25488 14.2549 7.26488 14.2549 9.75488C14.2549 12.2449 12.2449 14.2549 9.75488 14.2549Z\" fill=\"#C7C7C7\" /> </svg> <input className=\"\" placeholder=\"Search funding\" onChange={(e) => { if (currentPage !== 1) setCurrentPage(1); setSearch(e.target.value); const filtered = A_130(e.target.value, totalDonations); setFilteredDonations(filtered); }} type=\"text\" /> </SearchBar> {filteredDonations?.length > 0 ? filteredDonations.slice((currentPage - 1) * PER_PAGE, currentPage * PER_PAGE).map((donation) => { const { total_amount, amount, pot_id, recipient_id, project_id, paid_at, base_currency, ft_id, type, donated_at, donated_at_ms } = donation; const ftId = ft_id || base_currency; const donationAmount = parseFloat(Big(total_amount || amount).div(Big(10).pow(ftId === \"near\" ? 24 : ftMetadata[ftId]?.decimals || 24)).toFixed(2)); const isPot = type === \"sponsorship\"; const url = isPot ? \\`?tab=pot&potId=\\${pot_id}\\` : \\`?tab=project&projectId=\\${project_id || recipient_id}\\`; const name = _address(getName(donation), 15); return <div className=\"funding-row\"> <FundingSrc> {isPot ? <PotIcon className=\"profile-image\" /> : <ProfileImage accountId={recipient_id || project_id} style={{}} />} <div className=\"funding-src\"> <a href={hrefWithParams(url)} target=\"_blank\"> {isPot && <span className=\"pot-name\"> Sponsor :</span>} {name} </a> <div className=\"type\">{sortList[type].label?.slice(0, -1)}</div> </div> </FundingSrc> <div className=\"price tab\"> <div className=\"near-icon\"> {ftId === \"near\" ? <NearOutline /> : <ImgIcon src={ftMetadata[ftId]?.icon} />} </div> {addTrailingZeros(donationAmount)} </div> <div className=\"tab date\">{getTimePassed(donated_at_ms || donated_at || paid_at, true)} ago</div> </div>; }) : <div className=\"funding-row\">No Donations</div>} </Funding> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ data: filteredDonations, currentPage: currentPage, perPage: PER_PAGE, onPageChange: (page) => {setCurrentPage(page);}, ...props } }} /> </Container>; `, Pots: ` const Outer = styled.div\\` display: flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; @keyframes beacon { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.3); } } animation: beacon 1.5s infinite;\\`;const Inner = styled.div\\` width: 10px; height: 10px; border-radius: 50%;\\`; const Indicator = ({ animate, colorInner, colorOuter}) => { return <Outer style={{ backgroundColor: colorOuter, animationPlayState: animate ? \"running\" : \"paused\" }}> <Inner style={{ backgroundColor: colorInner }} /> </Outer>;}; const TagContainer = styled.div\\` display: flex; justify-content: center; align-items: center; text-align: center; padding: 6px 8px; border-radius: 4px;\\`;const TagText = styled.span\\` font-size: 14px;\\`; const Tag = __props__ => { const { backgroundColor, borderColor, textColor, text } = __props__; const textStyle = __props__.textStyle || {}; return <TagContainer style={{ backgroundColor: backgroundColor || \"#ffffff\", border: \\`1px solid \\${borderColor || \"#000000\"}\\`, boxShadow: \\`0px -0.699999988079071px 0px \\${borderColor} inset\\` }}> {__props__.preElements} <TagText style={{ color: textColor || \"#000000\", ...textStyle }}> {text} </TagText> </TagContainer>;}; const nearToUsd = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then(res => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\");const yoctosToUsd = amount => { return nearToUsd ? \"~\\$\" + formatWithCommas(new Big(amount).mul(nearToUsd).div(1e24).toFixed(2)) : \"0\";}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; function daysUntil(timestamp) { const now = Date.now(); const differenceInTime = timestamp - now; const differenceInDays = Math.ceil(differenceInTime / (1000 * 3600 * 24)); return \\`\\${differenceInDays} \\${differenceInDays === 1 ? \"day\" : \"days\"}\\`;} const tagsList = potConfig => { const { application_start_ms, application_end_ms, public_round_start_ms, public_round_end_ms, cooldown_end_ms, all_paid_out } = potConfig; const now = Date.now(); const applicationOpen = now >= application_start_ms && now < application_end_ms; const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; const cooldownPending = public_round_end_ms && now >= public_round_end_ms && !cooldown_end_ms; const cooldownOpen = now >= public_round_end_ms && now < cooldown_end_ms; const payoutsPending = cooldown_end_ms && now >= cooldown_end_ms && !all_paid_out; const payoutsCompleted = all_paid_out; const tags = [{ backgroundColor: \"#EFFEFA\", borderColor: \"#33DDCB\", textColor: \"#023131\", text: \"Sponsorship Open\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#CAFDF3\", colorInner: \"#33DDCB\", animate: true }, visibility: now < application_start_ms }, { backgroundColor: \"#EFFEFA\", borderColor: \"#33DDCB\", textColor: \"#023131\", text: daysUntil(application_end_ms) + \" left to apply\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#CAFDF3\", colorInner: \"#33DDCB\", animate: true }, visibility: applicationOpen }, { backgroundColor: \"#F7FDE8\", borderColor: \"#9ADD33\", textColor: \"#192C07\", text: daysUntil(public_round_end_ms) + \" left to donate\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#D7F5A1\", colorInner: \"#9ADD33\", animate: true }, visibility: publicRoundOpen }, { backgroundColor: \"#F5F3FF\", borderColor: \"#A68AFB\", textColor: \"#2E0F66\", text: \"Cooldown pending\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#EDE9FE\", colorInner: \"#A68AFB\", animate: true }, visibility: cooldownPending }, { backgroundColor: \"#F5F3FF\", borderColor: \"#A68AFB\", textColor: \"#2E0F66\", text: \"Challenge period\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#EDE9FE\", colorInner: \"#A68AFB\", animate: true }, visibility: cooldownOpen }, { backgroundColor: \"#F7FDE8\", borderColor: \"#9ADD33\", textColor: \"#192C07\", text: \"Payouts pending\", preElementsProps: { colorOuter: \"#D7F5A1\", colorInner: \"#9ADD33\", animate: true }, textStyle: { fontWeight: 500, marginLeft: \"8px\" }, visibility: payoutsPending }, { backgroundColor: \"#464646\", borderColor: \"#292929\", textColor: \"#FFF\", text: \"Payouts completed\", preElementsProps: { colorOuter: \"#656565\", colorInner: \"#A6A6A6\", animate: false }, textStyle: { fontWeight: 500, marginLeft: \"8px\" }, visibility: payoutsCompleted }]; return tags;}; const A_133 = styled.a\\` display: flex; flex-direction: column; min-width: 320px; min-height: 300px; border-radius: 8px; background: white; box-shadow: 0px -2px 0px 0px #464646 inset, 0px 0px 0px 1px #464646; padding-bottom: 5px; height: 100%; &:hover { text-decoration: none; cursor: pointer; }\\`;const CardSection = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 16px; padding: 32px; width: 100%; height: 100%;\\`;const A_134 = styled.div\\` color: #292929; font-size: 22px; font-weight: 600; line-height: 28px; word-wrap: break-word; > div { font-weight: inherit; display: flex; align-items: baseline; } .usd-amount { font-size: 14px; font-weight: 400; margin-left: 0.25rem; } .text { font-size: 14px; color: #7b7b7b; margin-left: 0.5rem; }\\`;const A_135 = styled.div\\` color: #525252; font-size: 16px; font-weight: 400; line-height: 28px; word-wrap: break-word; a { color: rgb(123, 123, 123); }\\`;const Subtitle = styled.span\\` color: #7b7b7b; font-size: 14px; font-weight: 600; line-height: 20px; word-wrap: break-word;\\`; const MAX_DESCRIPTION_LENGTH = 100;const MAX_TITLE_LENGTH = 36;const PotCard = ({ potId}) => { const potConfig = PotSDK.getConfig(potId); if (!potConfig) return <A_133 style={{ justifyContent: \"center\", alignItems: \"center\" }}> {potConfig == null ? <div className=\"spinner-border text-secondary\" role=\"status\" /> : <div>Pot {potId} not found.</div>} </A_133>; const { pot_name, pot_description, matching_pool_balance } = potConfig; const amountNear = yoctosToNear(matching_pool_balance, true); const amountUsd = yoctosToUsd(matching_pool_balance); const description = !pot_description ? \"No description\" : pot_description.length > MAX_DESCRIPTION_LENGTH ? \\`\\${pot_description.slice(0, MAX_DESCRIPTION_LENGTH)}...\\` : pot_description; const title = !pot_name ? \"No title\" : pot_name.length > MAX_TITLE_LENGTH ? \\`\\${pot_name.slice(0, MAX_TITLE_LENGTH)}...\\` : pot_name; const tags = tagsList(potConfig); return <A_133 href={hrefWithParams(\\`?tab=pot&potId=\\${potId}\\`)}> <CardSection> <A_134>{title}</A_134> <A_135> <Markdown text={description} /> </A_135> </CardSection> <CardSection style={{ background: \"#F6F5F3\", borderTop: \"1px #7B7B7B solid\", marginTop: \"auto\", height: \"fit-content\" }}> <A_134> <div> {amountNear} {amountUsd && <span className=\"usd-amount\">{amountUsd}</span>} <span className=\"text\">in pot</span> </div> </A_134> {tags.map((tag) => tag.visibility ? <Tag {...tag} preElements={<Indicator {...tag.preElementsProps || {}} />} key={tag.text} /> : \"\")} </CardSection> </A_133>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_136 = styled.div\\` display: grid; grid-template-columns: repeat(3, 1fr); > div { padding-top: 0rem; } @media screen and (max-width: 1400px) { grid-template-columns: repeat(2, 1fr); } @media screen and (max-width: 768px) { grid-template-columns: repeat(1, 1fr); }\\`;const NoResults = styled.div\\` display: flex; justify-content: space-between; align-items: center; padding: 68px 105px; border-radius: 12px; background: #f6f5f3; .text { font-family: \"Lora\"; max-width: 290px; font-size: 22px; font-style: italic; font-weight: 500; color: #292929; } img { width: 60%; } @media screen and (max-width: 768px) { flex-direction: column-reverse; padding: 24px 16px; .text { font-size: 16px; } img { width: 100%; } }\\`; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const pots = PotFactorySDK.getPots(); const { projectId } = props.alem.useParams(); const [potIds, setPotIds] = useState(null); const [loading, setLoading] = useState(true); const getApprovedApplications = potId => PotSDK.asyncGetApprovedApplications(potId).then(applications => { if (applications.some(app => app.project_id === projectId)) { setPotIds([...(potIds || []), potId]); } if (pots[pots.length - 1].id === potId) setLoading(false); }).catch(() => console.log(\\`Error fetching approved applications for \\${potId}\\`)); if (pots && loading) { pots.forEach(pot => { getApprovedApplications(pot.id); }); } return loading ? \"Loading...\" : potIds.length ? <A_136> {potIds.map(potId => <PotCard {...{ potId, tab: \"pots\" }} />)} </A_136> : <NoResults> <div className=\"text\">This project has not participated in any pots yet.</div> <img src=\"https://ipfs.near.social/ipfs/bafkreibcjfkv5v2e2n3iuaaaxearps2xgjpc6jmuam5tpouvi76tvfr2de\" alt=\"pots\" /> </NoResults>; `, Pagination: ` const A_148 = styled.div\\` display: flex; gap: 1rem; justify-content: center; list-style-type: none; li { display: flex; align-items: center; justify-content: center; &.disabled { pointer-events: none; .arrow::before { border-right: 0.12em solid rgba(0, 0, 0, 0.43); border-top: 0.12em solid rgba(0, 0, 0, 0.43); } &:hover { cursor: default; } } } .pagination-item { border: 1px solid transparent; background: #292929; border-radius: 2px; padding: 10px; font-size: 12px; color: white; cursor: pointer; transition: all 300ms; &.dots:hover { cursor: default; opacity: 1; } &:hover { opacity: 0.75; } &.selected { background: white; cursor: default; color: #292929; border-color: #292929; } } .arrow { cursor: pointer; &::before { position: relative; content: \"\"; display: inline-block; width: 0.4em; height: 0.4em; border-right: 0.12em solid rgba(0, 0, 0, 0.87); border-top: 0.12em solid rgba(0, 0, 0, 0.87); } &.left { transform: rotate(-135deg) translate(-50%); } &.right { transform: rotate(45deg); } }\\`; const props = props; const DOTS = \"...\"; const range = (start, end) => { let length = end - start + 1; return Array.from({ length }, (_, idx) => idx + start); }; const usePagination = ({ totalCount, perPage, siblingCount, currentPage }) => { const paginationRange = useMemo(() => { const totalPageCount = Math.ceil(totalCount / perPage); const totalPageNumbers = siblingCount + 3; if (totalPageNumbers >= totalPageCount || totalPageCount < 6) { return range(1, totalPageCount); } const leftSiblingIndex = Math.max(currentPage - siblingCount, 1); const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPageCount); const shouldShowLeftDots = leftSiblingIndex > 2; const shouldShowRightDots = rightSiblingIndex <= totalPageCount - 3; const firstPageIndex = 1; const lastPageIndex = totalPageCount; if (!shouldShowLeftDots && shouldShowRightDots) { let leftItemCount = 3 + siblingCount; let leftRange = range(1, leftItemCount); return [...leftRange, DOTS, totalPageCount]; } if (shouldShowLeftDots && !shouldShowRightDots) { let rightItemCount = 3 + siblingCount; let rightRange = range(totalPageCount - rightItemCount + 1, totalPageCount); return [firstPageIndex, DOTS, ...rightRange]; } if (shouldShowLeftDots && shouldShowRightDots) { let middleRange = range(leftSiblingIndex, rightSiblingIndex); return [firstPageIndex, DOTS, ...middleRange, DOTS, lastPageIndex]; } if (!shouldShowLeftDots && !shouldShowRightDots) { return range(1, totalPageCount); } }, [totalCount, perPage, siblingCount, currentPage]); return paginationRange; }; const { onPageChange, data, currentPage, perPage } = props; const siblingCount = props.siblingCount ?? 1; const showArrows = props.showArrows ?? false; const totalCount = data?.length; const paginationRange = usePagination({ currentPage, totalCount, siblingCount, perPage }) || []; if (currentPage === 0 || paginationRange.length < 2) { return \"\"; } const onNext = () => { onPageChange(currentPage + 1); }; const onPrevious = () => { onPageChange(currentPage - 1); }; let lastPage = paginationRange[paginationRange.length - 1]; return <A_148> {showArrows && <li className={\\`\\${currentPage === 1 ? \"disabled\" : \"\"}\\`} onClick={onPrevious}> <div className=\"arrow left\" /> </li>} {paginationRange?.length > 0 && paginationRange.map(pageNumber => { if (pageNumber === DOTS) { return <li className=\"pagination-item dots\">&#8230;</li>; } return <li key={pageNumber} className={\\`pagination-item \\${pageNumber === currentPage ? \"selected\" : \"\"}\\`} onClick={() => onPageChange(pageNumber)}> {pageNumber} </li>; })} {showArrows && <li className={\\`\\${currentPage === lastPage ? \"disabled\" : \"\"}\\`} onClick={onNext}> <div className=\"arrow right\" /> </li>} </A_148>; `, PotlockFunding: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_140 = styled.div\\` display: flex; flex-direction: column; gap: 1.5rem;\\`;const A_141 = styled.div\\` font-size: 24px; font-weight: 600;\\`;const PotlockFundingContainer = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; background: #fff; overflow: hidden; .header { border-bottom: 0.5px solid #7b7b7b; padding: 0.5rem 1rem; div { font-weight: 600; } @media screen and (max-width: 768px) { .tab { display: none; } .funding { display: block; } } } .funding-row { padding: 1rem; } .header, .funding-row { display: flex; justify-content: space-between; gap: 2rem; font-size: 14px; flex-wrap: wrap; @media screen and (max-width: 768px) { gap: 4px; } } .tab { display: flex; align-items: center; gap: 0.5rem; width: 156px; justify-content: left; &.sort { cursor: pointer; svg { transition: rotate 300ms; } } @media screen and (max-width: 768px) { white-space: nowrap; width: 60px; } } .funding { flex: 1; } .price { gap: 1rem; font-weight: 600; justify-content: left; svg { width: 1.5em; } } .date { justify-content: right; } @media screen and (max-width: 768px) { .price { gap: 0.5rem; } .date { width: 100%; justify-content: left; color: #7b7b7b; margin-left: 2.5rem; } }\\`;const A_142 = styled.div\\` display: flex; align-items: center; gap: 1rem; flex: 1; max-width: 100%; gap: 1rem; .profile-image { width: 24px; height: 24px; display: flex !important; } .funding-src { display: flex; flex-direction: column; .pot-name { color: inherit; font-weight: inherit; display: none; } a { color: #292929; transition: 300ms; font-weight: 600; :hover { text-decoration: none; color: #dd3345; } } .type { color: #7b7b7b; } } @media screen and (max-width: 768px) { .funding-src .type { display: none; } .funding-src .pot-name { display: inline-block; } }\\`;const A_143 = styled.div\\` display: flex; align-items: center; background: #f6f5f3; position: relative; svg { width: 18px; left: 1rem; top: 50%; transform: translateY(-50%); position: absolute; pointer-events: none; } input { width: 100%; height: 100%; padding: 1rem; padding-left: 50px; border: none; background: transparent; :focus { outline: none; } }\\`;const A_144 = styled.div\\` display: flex; flex-wrap: wrap; margin: 24px 0; align-items: center; .item { display: flex; height: fit-content; gap: 8px; padding-right: 1rem; align-items: center; :nth-child(2) { border-right: 1px solid #7b7b7b; border-left: 1px solid #7b7b7b; padding-left: 1rem; } :nth-child(3) { padding-left: 1rem; } .item-value { font-weight: 600; } @media screen and (max-width: 768px) { display: none; } } .dropdown { margin-left: auto; @media screen and (max-width: 480px) { margin-right: auto; margin-left: 0; } }\\`;const A_145 = styled.div\\` display: none; justify-content: space-between; width: 100%; div { display: flex; align-items: center; font-weight: 500; cursor: pointer; gap: 8px; color: #7b7b7b; svg { transition: rotate 300ms; } &.active { color: #292929; } } @media screen and (max-width: 768px) { display: flex; }\\`;const A_146 = styled.div\\` display: flex; gap: 10px; align-items: center; .label { font-weight: 500; } .count { display: flex; width: \\${({ digit}) => 24 + (digit - 1) * 6}px; height: \\${({ digit}) => 24 + (digit - 1) * 6}px; align-items: center; justify-content: center; border-radius: 50%; background: #ebebeb; }\\`;const A_147 = styled.img\\` width: 21px; height: 21px;\\`; const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const { donations: donations, directDonations: directDonations, matchingRoundDonations: matchingRoundDonations, potPayouts: potPayouts, sponsorships: sponsorships, projectId: projectId, totalDonationAmountNear: totalDonationAmountNear, uniqueDonors: uniqueDonors, totalMatched: totalMatched } = props; const [filter, setFilter] = useState({ date: false, price: false }); const [currentFilter, setCurrentFilter] = useState(\"date\"); const [sort, setSort] = useState(\"all\"); const [currentPage, setCurrentPage] = useState(1); const [totalDonations, setTotalDonations] = useState(donations); const [filteredDonations, setFilteredDonations] = useState(donations); const [search, setSearch] = useState(\"\"); const perPage = 30; useEffect(() => { setTotalDonations(donations); setFilteredDonations(donations); }, [donations]); const sortList = { all: { label: \"All donations\", val: \"all\", count: donations?.length }, direct: { label: \"Direct donations\", val: \"direct\", count: directDonations?.length }, matched: { label: \"Matched donations\", val: \"matched\", count: matchingRoundDonations?.length }, ...(projectId ? { payout: { label: \"Matching pool allocations\", val: \"payout\", count: potPayouts?.length } } : { sponsorship: { label: \"Sponsorships\", val: \"sponsorship\", count: sponsorships?.length } }) }; const searchDonations = (searchTerm) => { const filteredApplications = totalDonations.filter((item) => { const searchIn = [item.pot_name || \"\", item.recipient_id || \"\", item.project_id || \"\", item.donor_id || \"\", item.pot_id || \"\"]; return searchIn.some((item) => item.toLowerCase().includes(searchTerm.toLowerCase())); }); return filteredApplications; }; const getDate = (donation) => donation.donated_at_ms || donation.donated_at; const sortDonation = (type) => { setCurrentFilter(type); const sort = !filter[type]; setFilter({ ...filter, [type]: sort }); if (type === \"price\") { const sortedDonations = filteredDonations.sort((a, b) => sort ? b.total_amount - a.total_amount : a.total_amount - b.total_amount); setFilteredDonations(sortedDonations); } else if (type === \"date\") { const sortedDonations = filteredDonations.sort((a, b) => { return sort ? getDate(a) - getDate(b) : getDate(b) - getDate(a); }); setFilteredDonations(sortedDonations); } }; const filterDonations = (sortVal) => { const displayedDonations = searchDonations(search); let filtered; if (sortVal && sortVal !== \"all\") { filtered = displayedDonations.filter((donation) => { return sortList[donation.type].val === sortVal; }); return filtered; } else { return displayedDonations; } }; const getName = (donation) => { switch (donation.type) { case \"direct\": return projectId ? donation.donor_id : donation.recipient_id; case \"sponsorship\": return donation.pot_name; case \"payout\": return donation.pot_name; case \"matched\": return projectId ? donation.donor_id : donation.project_id; default: return projectId ? donation.donor_id : donation.recipient_id; } }; const stats = { ...(totalDonationAmountNear ? { Donated: <> {totalDonationAmountNear}N{A_236 && <span>~\\${(totalDonationAmountNear * A_236).toFixed(2)}</span>} </> } : {}), ...(uniqueDonors ? { [\\`Unique donor\\${uniqueDonors === 1 ? \"\" : \"s\"}\\`]: uniqueDonors } : {}), ...(uniqueDonors ? { \"Total Matched\": totalMatched + \"N\" } : {}) }; const NearIcon = (props) => <svg {...props} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" id=\"near-logo\"> <rect width=\"24\" height=\"24\" rx=\"12\" fill=\"#CECECE\" /> <path d=\"M15.616 6.61333L13.1121 10.3333C12.939 10.5867 13.2719 10.8933 13.5117 10.68L15.9756 8.53333C16.0422 8.48 16.1354 8.52 16.1354 8.61333V15.32C16.1354 15.4133 16.0155 15.4533 15.9623 15.3867L8.50388 6.45333C8.26415 6.16 7.91787 6 7.53163 6H7.26526C6.5727 6 6 6.57333 6 7.28V16.72C6 17.4267 6.5727 18 7.27858 18C7.71809 18 8.13097 17.7733 8.3707 17.3867L10.8746 13.6667C11.0477 13.4133 10.7148 13.1067 10.475 13.32L8.0111 15.4533C7.94451 15.5067 7.85128 15.4667 7.85128 15.3733V8.68C7.85128 8.58667 7.97114 8.54667 8.02442 8.61333L15.4828 17.5467C15.7225 17.84 16.0821 18 16.4551 18H16.7214C17.4273 18 18 17.4267 18 16.72V7.28C18 6.57333 17.4273 6 16.7214 6C16.2686 6 15.8557 6.22667 15.616 6.61333Z\" fill=\"black\" /> </svg>; const PotIcon = (potIconProps) => <svg {...potIconProps} width=\"20\" height=\"21\" viewBox=\"0 0 20 21\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M10 3C10.5523 3 11 2.55228 11 2C11 1.44772 10.5523 1 10 1C9.44772 1 9 1.44772 9 2C9 2.55228 9.44772 3 10 3ZM12 2C12 2.37912 11.8945 2.7336 11.7113 3.03569C14.6721 3.33449 17.0882 5.47841 17.7921 8.3C17.9279 8.84425 18 9.41371 18 10H16.3H3.7H2C2 9.41371 2.07208 8.84425 2.20786 8.3C2.9118 5.47841 5.3279 3.33449 8.28871 3.03569C8.10549 2.7336 8 2.37912 8 2C8 0.895431 8.89543 0 10 0C11.1046 0 12 0.895431 12 2ZM9 4.7C6.66751 4.7 4.68694 6.20674 3.97852 8.3H16.0215C15.3131 6.20674 13.3325 4.7 11 4.7H9ZM0 11H2H4H16H18H20V13H18V19C18 20.1046 17.1046 21 16 21H4C2.89543 21 2 20.1046 2 19V13H0V11ZM4 19V13H16V19H4Z\" fill=\"#7B7B7B\" /> </svg>; const Arrow = (props) => <svg {...props} style={{ rotate: !props.active ? \"0deg\" : \"180deg\" }} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 6L1.0575 7.0575L5.25 2.8725V12H6.75V2.8725L10.935 7.065L12 6L6 0L0 6Z\" fill=\"#7B7B7B\" /> </svg>; const [ftMetadata, setFtMetadata] = useState({}); useEffect(() => { const metadata = {}; const ftIds = totalDonations.reduce((acc, donation) => { if (donation.ft_id && donation.ft_id !== \"near\") { acc.add(donation.ft_id); } return acc; }, new Set()); ftIds.forEach((ftId) => { Near.asyncView(ftId, \"ft_metadata\", {}).then((ftMetadata) => { metadata[ftId] = ftMetadata; if (Object.keys(metadata).length === ftIds.size) { setFtMetadata(metadata); } }).catch((e) => { console.error(\"error getting ft metadata: \", e); }); }); }, []); return <A_140> {projectId && <A_141>Potlock Funding</A_141>} <A_144> {Object.keys(stats).map((k) => <div className=\"item\"> <div className=\"item-value\">{stats[k]}</div> <div className=\"item-label\">{k}</div> </div>)} <div className=\"dropdown\"> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ ...{ sortVal: <A_146 digit={sortList[sort].count.toString().length}> <div className=\"label\">{sortList[sort].label}</div> <div className=\"count\">{sortList[sort].count}</div> </A_146>, showCount: true, sortList: Object.values(sortList), FilterMenuCustomStyle: \\`left:auto; right:0;\\`, handleSortChange: ({ val }) => {const filtered = filterDonations(val);setFilteredDonations(filtered);setSort(val);} }, ...props } }} /> </div> </A_144> <A_145> <div onClick={() => sortDonation(\"date\")} className={\\`\\${currentFilter === \"date\" ? \"active\" : \"\"}\\`}> Sort Date {currentFilter === \"date\" && <Arrow active={!filter.date} />} </div> <div onClick={() => sortDonation(\"price\")} className={\\`\\${currentFilter === \"price\" ? \"active\" : \"\"}\\`}> Sort Amount {currentFilter === \"price\" && <Arrow active={filter.price} />} </div> </A_145> <PotlockFundingContainer> <div className=\"header\"> <div className=\"funding tab\">{projectId ? \"Funding Source\" : \"Project Name\"}</div> <div className=\"tab sort\" onClick={() => sortDonation(\"price\")}> Amount {currentFilter === \"price\" && <Arrow active={filter.price} />} </div> <div className=\"tab sort date\" onClick={() => sortDonation(\"date\")}> Date {currentFilter === \"date\" && <Arrow active={!filter.date} />} </div> </div> <A_143> <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.7549 14.2549H14.9649L14.6849 13.9849C15.6649 12.8449 16.2549 11.3649 16.2549 9.75488C16.2549 6.16488 13.3449 3.25488 9.75488 3.25488C6.16488 3.25488 3.25488 6.16488 3.25488 9.75488C3.25488 13.3449 6.16488 16.2549 9.75488 16.2549C11.3649 16.2549 12.8449 15.6649 13.9849 14.6849L14.2549 14.9649V15.7549L19.2549 20.7449L20.7449 19.2549L15.7549 14.2549ZM9.75488 14.2549C7.26488 14.2549 5.25488 12.2449 5.25488 9.75488C5.25488 7.26488 7.26488 5.25488 9.75488 5.25488C12.2449 5.25488 14.2549 7.26488 14.2549 9.75488C14.2549 12.2449 12.2449 14.2549 9.75488 14.2549Z\" fill=\"#C7C7C7\" /> </svg> <input className=\"\" placeholder=\"Search funding\" onChange={(e) => { if (currentPage !== 1) setCurrentPage(1); setSearch(e.target.value); const filtered = searchDonations(e.target.value); setFilteredDonations(filtered); }} type=\"text\" /> </A_143> {filteredDonations.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation) => { const { donor_id, total_amount, amount, pot_id, recipient_id, project_id, paid_at, base_currency, ft_id, type, donated_at, donated_at_ms } = donation; const ftId = ft_id || base_currency; const donationAmount = parseFloat(Big(total_amount || amount).div(Big(10).pow(ftId === \"near\" ? 24 : ftMetadata[ftId]?.decimals || 24)).toFixed(2)); const addTrailingZeros = (number) => { if (number < 100 && number >= 0.1) return number.toFixed(1); return number; }; const isPot = type === \"payout\" || type === \"sponsorship\"; const url = isPot ? \\`?tab=pot&potId=\\${pot_id}\\` : projectId ? \\`?tab=profile&accountId=\\${donor_id}\\` : \\`?tab=project&projectId=\\${project_id || recipient_id}\\`; const name = _address(getName(donation), 15); return <div className=\"funding-row\"> <A_142> {isPot ? <PotIcon className=\"profile-image\" /> : <ProfileImage accountId={projectId ? donor_id : recipient_id || project_id} fallbackUrl=\"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\" />} <div className=\"funding-src\"> <a href={hrefWithParams(url)} target=\"_blank\"> {isPot && <span className=\"pot-name\"> {projectId ? \"Matching Pool\" : \"Sponsor\"} :</span>} {name} </a> <div className=\"type\">{sortList[type].label?.slice(0, -1)}</div> </div> </A_142> <div className=\"price tab\"> <div className=\"near-icon\"> {ftId === \"near\" ? <NearIcon /> : <A_147 src={ftMetadata[ftId]?.icon} />} </div> {addTrailingZeros(donationAmount)} </div> <div className=\"tab date\">{getTimePassed(donated_at_ms || donated_at || paid_at, true)} ago</div> </div>; })} {filteredDonations.length === 0 && <div className=\"funding-row\">No Donations</div>} </PotlockFundingContainer> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: filteredDonations, currentPage, perPage: perPage, bgColor: \"#7B7B7B\" }, ...props } }} /> </A_140>; `, ExternalFunding: ` const A_149 = styled.div\\` display: flex; flex-direction: column; > .description { margin-top: 0.5rem; margin-bottom: 1.5rem; } .external-funding { display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; background: #fff; transition: all 300ms ease-in-out; &.hidden { visibility: hidden; height: 0; opacity: 0; transform: translateY(100px); } .header { border-bottom: 0.5px solid #7b7b7b; padding: 10px 20px; div { font-weight: 600; } @media screen and (max-width: 920px) { div { display: none; } .funding { display: block; } } } .funding-row { padding: 20px; flex-wrap: wrap; position: relative; } .header, .funding-row { display: flex; justify-content: space-between; gap: 2rem; font-size: 14px; div { text-transform: capitalize; width: 100%; max-width: 120px; text-align: left; &:last-of-type { justify-content: right; } } .investor { display: flex; flex-direction: column; div:first-of-type { font-weight: 600; } } .mobile-date { display: none; color: #7b7b7b; } .amount { font-weight: 600; display: flex; align-items: center; gap: 1rem; svg { display: none; rotate: 180deg; } } .description { flex: 1; max-width: 100%; } .toggle-check { position: absolute; width: 100%; left: 0; top: 0; height: 100%; opacity: 0; display: none; } .date { display: flex; align-items: center; } @media screen and (max-width: 920px) { gap: 8px; div { max-width: initial; } .date { display: none; } .mobile-date { display: block; } .description { flex-basis: 100%; order: 1; max-height: 0; overflow: hidden; transition: max-height 200ms ease-in-out; } div { width: fit-content; } .amount svg { display: block; } .toggle-check { display: block; } .toggle-check:checked + .description { max-height: 200px; } .toggle-check:checked ~ div svg { rotate: 0deg; } } } @media screen and (max-width: 920px) { .header div:not(:first-of-type) { display: none; } } }\\`;const A_150 = styled.div\\` font-size: 24px; font-weight: 600; display: flex; align-items: center; gap: 16px;\\`;const A_151 = styled.svg\\` width: 12px; transition: all 200ms;\\`; const {externalFunding: externalFunding} = props; const [showFundingTable, setShowFundingTable] = useState(true); const ArrowDown = arrowProps => <A_151 {...props} style={{ rotate: arrowProps.showFundingTable ? \"\" : \"180deg\" }} viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294983L0 6.29498L1.41 7.70498L6 3.12498L10.59 7.70498L12 6.29498L6 0.294983Z\" fill=\"#151A23\" /> </A_151>; const externalTableTabs = [\"funding Source\", \"description\", \"date\", \"amount\"]; return <A_149> <A_150 style={{ cursor: \"pointer\" }} onClick={() => setShowFundingTable(!showFundingTable)}> External Funding <ArrowDown showFundingTable={showFundingTable} /> </A_150> <div className=\"description\">This not related to the funding generated on this platform</div> <div className={\\` external-funding \\${showFundingTable ? \"\" : \"hidden\"} \\`}> <div className=\"header\"> {externalTableTabs.map(tab => <div className={tab} key={tab}> {tab} </div>)} </div> {externalFunding.map(({ investorName, description, date, amountReceived, denomination }) => <div className=\"funding-row\"> <div className=\"investor\"> <div>{investorName}</div> {date && <div className=\"date-mobile\">{date}</div>} </div> <input type=\"checkbox\" className=\"toggle-check\" /> <div className=\"description\">{description}</div> <div className=\"date\">{date ?? \"No specified date\"}</div> <div className=\"amount\"> {parseFloat(amountReceived).toLocaleString() + \" \" + denomination}{\" \"} <ArrowDown showFundingTable={showFundingTable} /> </div> </div>)} </div> </A_149>; `, FundingRaised: ` const A_137 = styled.div\\` display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 80px 1rem; border-radius: 12px; gap: 24px; background: #f6f5f3; .text { font-family: \"Lora\"; font-size: 22px; font-style: italic; font-weight: 500; color: #292929; } img { width: 100%; max-width: 604px; } @media screen and (max-width: 768px) { padding: 1.5rem 1rem; .text { font-size: 16px; } }\\`;const A_138 = styled.div\\` display: flex; flex-direction: column;\\`;const A_139 = styled.div\\` width: 100%; background: #c7c7c7; height: 1px; margin: 3rem 0;\\`; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const componentProps = props; const { donations, potPayouts, profile } = componentProps; const externalFunding = profile.plFundingSources ? JSON.parse(profile.plFundingSources) : []; const [totalDonationAmountNear, uniqueDonors, totalMatched] = useMemo(() => { if (donations) { let totalNear = Big(0); const uniqueDonors = [...new Set(donations.map((donation) => donation.donor_id))]; donations.forEach((donation) => { if (donation.ft_id === \"near\" || donation.base_currency === \"near\") { totalNear = totalNear.plus(Big(donation.total_amount || donation.amount)); } }); const totalDonationAmountNear = constants.SUPPORTED_FTS[\"NEAR\"].fromIndivisible(totalNear.toString()); let totalMatched = Big(0); if (potPayouts) { potPayouts.forEach((payout) => { totalMatched = totalMatched.plus(Big(payout.amount)); }); } totalMatched = constants.SUPPORTED_FTS[\"NEAR\"].fromIndivisible(totalMatched.toString()); return [totalDonationAmountNear, uniqueDonors?.length, totalMatched]; } return [\"\", 0, 0]; }, [donations, potPayouts]); return externalFunding.length === 0 && donations.length === 0 ? <A_137> <img src=\"https://ipfs.near.social/ipfs/bafkreif5awokaip363zk6zqrsgmpehs6rap3w67engc4lxdlk4x6iystru\" alt=\"pots\" /> <div className=\"text\">No funds have been raised for this project.</div> </A_137> : <A_138> {externalFunding.length > 0 && <Widget loading=\" \" code={props.alem.componentsCode.ExternalFunding} props={{ ...{ externalFunding: externalFunding, ...props } }} />} {externalFunding.length > 0 && donations.length > 0 && <A_139 />} {donations.length > 0 && <Widget loading=\" \" code={props.alem.componentsCode.PotlockFunding} props={{ ...{ ...{ totalDonationAmountNear, uniqueDonors, totalMatched }, ...props } }} />} </A_138>; `, Compose: ` const compProps = props; if (!context.accountId) { return \"\"; } const indexKey = compProps.indexKey ?? \"main\"; const draftKey = compProps.indexKey ?? \"draft\"; const draft = Storage.privateGet(draftKey) || compProps.initialText; const groupId = compProps.groupId; if (draft === null) { return \"\"; } const [initialText] = useState(draft); const composeData = () => { const data = { post: { main: JSON.stringify(Object.assign({ groupId }, state.content)) }, index: { post: JSON.stringify({ key: indexKey, value: { type: \"md\" } }) } }; const item = { type: \"social\", path: \\`\\${context.accountId}/post/main\\` }; const notifications = state.extractMentionNotifications(state.content.text, item); const hashtags = state.extractHashtags(state.content.text); return data; }; State.init({ onChange: ({ content }) => { State.update({ content }); Storage.privateSet(draftKey, content.text || \"\"); } }); return <> <div style={{ margin: \"0 -12px\" }}> <Widget src=\"mob.near/widget/MainPage.N.Common.Compose\" props={{ placeholder: \"What's happening?\", onChange: state.onChange, initialText, onHelper: ({ extractMentionNotifications, extractHashtags }) => { State.update({ extractMentionNotifications, extractHashtags }); }, composeButton: onCompose => <CommitButton disabled={!state.content} force className=\"post-btn\" data={composeData} onCommit={() => { onCompose(); }}> Post </CommitButton> }} /> </div> {state.content && <Widget key=\"post-preview\" src=\"mob.near/widget/MainPage.N.Post\" props={{ accountId: context.accountId, content: state.content, blockHeight: \"now\" }} />} </>; `, About: ` const getTeamMembersFromSocialProfileData = profileData => { if (!profileData) return []; const team = profileData.plTeam ? JSON.parse(profileData.plTeam) : profileData.team ? Object.entries(profileData.team).filter(([_, v]) => v !== null).map(([k, _]) => k) : []; return team;}; const A_153 = styled.div\\` display: flex; flex-direction: row; align-items: flex-start; justify-content: flex-start; @media screen and (max-width: 768px) { flex-direction: column; }\\`;const A_154 = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 600;\\`;const TeamMembersContainer = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 2rem; flex-wrap: wrap;\\`;const TeamMemberItem = styled.a\\` display: flex; flex-direction: column; justify-content: flex-start; gap: 8px; cursor: pointer; :hover { text-decoration: none; .profile-image img { filter: grayscale(0%); } } .profile-image { width: 180px; height: 180px; img { width: 100%; height: 100%; border-radius: 6px; filter: grayscale(100%); transition: 300ms ease-in-out; } } @media screen and (max-width: 768px) { .profile-image { width: 160px; height: 160px; } }\\`;const TeamMemberAccountId = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 400;\\`;const imageWidthPx = 129;const Col1 = styled.div\\` display: flex; width: 30%; margin-bottom: 1rem; @media screen and (max-width: 768px) { width: 100%; }\\`;const Col2 = styled.div\\` display: flex; width: 70%; @media screen and (max-width: 768px) { width: 100%; }\\`; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Team = (__props__) => { let { team } = __props__; team = team.filter((item) => item.length > 3); return <A_153> <Col1> <A_154>Team members</A_154> </Col1> <Col2> <TeamMembersContainer> {!team.length ? <div>No team members to display</div> : <TeamMembersContainer> {!team.length ? <div>No team members to display</div> : team.map((teamMember) => { const match = teamMember.match(/.near/i); if (match && match.length > 0) { return <TeamMemberItem href={hrefWithParams(\\`?tab=profile&accountId=\\${teamMember}\\`)} target=\"_blank\"> <ProfileImage {...{ accountId: teamMember, imageClassName: \"\", style: {}, thumbnail: false, tooltip: true }} /> <TeamMemberAccountId>@{teamMember}</TeamMemberAccountId> </TeamMemberItem>; } })} </TeamMembersContainer>} </TeamMembersContainer> </Col2> </A_153>;}; const AboutItem = ({ title, text}) => { const Container = styled.div\\` display: flex; flex-direction: row; align-items: flex-start; justify-content: flex-start; @media screen and (max-width: 768px) { flex-direction: column; align-items: flex-start; } \\`; const Col1 = styled.div\\` display: flex; width: 30%; margin-bottom: 1rem; @media screen and (max-width: 768px) { width: 100%; } \\`; const Col2 = styled.div\\` display: flex; flex-direction: column; width: 70%; p { margin: 0; } @media screen and (max-width: 768px) { width: 100%; } \\`; const Title = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 600; \\`; return <Container> <Col1> <Title>{title}</Title> </Col1> <Col2>{text}</Col2> </Container>;}; const A_155 = styled.div\\` max-width: 920px; display: flex; flex-direction: column; gap: 48px;\\`;const A_156 = styled.div\\` color: #2e2e2e; font-size: 40px; font-weight: 500; font-family: \"Lora\"; @media screen and (max-width: 768px) { font-size: 32px; }\\`;const A_157 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 24px;\\`;const GithubWrapper = styled.div\\` display: flex; flex-direction: column; gap: 1rem; a { transition: all 300ms; display: flex; align-items: center; gap: 1rem; svg { transition: all 300ms; } .url { color: #292929; width: fit-content; } :hover { text-decoration: none; transform: translateX(4px); svg { rotate: 45deg; } } }\\`;const SmartContractWrapper = styled.div\\` display: flex; flex-direction: column; gap: 1rem; .contract { display: flex; align-items: center; gap: 1rem; .text { display: flex; flex-direction: column; .chain { font-size: 14px; color: #7b7b7b; } } }\\`; const { projectId: projectId, accountId: accountId } = props; const profile = Social.getr(\\`\\${projectId}/profile\\`); if (!profile) { return \"\"; } const { name, description, plPublicGoodReason } = profile; const smartContracts = profile.plSmartContracts ? Object.entries(JSON.parse(profile.plSmartContracts)).reduce((accumulator, [chain, contracts]) => { const contractsForChain = Object.keys(contracts).map((contractAddress) => { return [chain, contractAddress]; }); return accumulator.concat(contractsForChain); }, []) : []; const githubRepos = profile.plGithubRepos ? JSON.parse(profile.plGithubRepos) : []; const Github = () => githubRepos.length > 0 ? <GithubWrapper> {githubRepos.map((url) => <a href={url} target=\"_blank\"> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1.5 0.5V2.5H10.09L0.5 12.09L1.91 13.5L11.5 3.91V12.5H13.5V0.5H1.5Z\" fill=\"#7B7B7B\" /> </svg> <div className=\"url\">{url}</div> </a>)} </GithubWrapper> : \"None provided\"; const SmartContracts = () => smartContracts.length > 0 ? <SmartContractWrapper> {smartContracts.map(([chain, contract]) => { return <div className=\"contract\"> <Widget loading=\" \" code={props.alem.componentsCode.CopyIcon} props={{ ...{ textToCopy: contract, ...props } }} /> <div className=\"text\"> <div className=\"address\">{contract}</div> <div className=\"chain\">{chain}</div> </div> </div>; })} </SmartContractWrapper> : \"None provided\"; const markdown = <Markdown text={description} />; const github = <Github />; const smartContractsItems = <SmartContracts />; const team = useMemo(() => { return <Team team={getTeamMembersFromSocialProfileData(profile)} />; }, []); return <A_155> <A_157> <A_156>About {name}</A_156> </A_157> <AboutItem title=\"Overview\" text={markdown} /> <AboutItem title=\"Why we are a public good\" text={plPublicGoodReason || \"None provided\"} /> {team} <AboutItem title=\"Github repo(s)\" text={github} /> <AboutItem title=\"Smart contracts\" text={smartContractsItems} /> </A_155>; `, FollowersList: ` const FollowButton = __props__ => { if (!__props__.accountId || !context.accountId || context.accountId === __props__.accountId) { return \"\"; } const followEdge = Social.keys(\\`\\${context.accountId}/graph/follow/\\${__props__.accountId}\\`, undefined, { values_only: true }); const inverseEdge = Social.keys(\\`\\${__props__.accountId}/graph/follow/\\${context.accountId}\\`, undefined, { values_only: true }); const loading = followEdge === null || inverseEdge === null; const follow = followEdge && Object.keys(followEdge).length; const inverse = inverseEdge && Object.keys(inverseEdge).length; const type = follow ? \"unfollow\" : \"follow\"; const data = { graph: { follow: { [__props__.accountId]: follow ? null : \"\" } }, index: { graph: JSON.stringify({ key: \"follow\", value: { type, accountId: __props__.accountId } }), notify: JSON.stringify({ key: __props__.accountId, value: { type } }) } }; return <CommitButton disabled={loading} className={\\`btn \\${loading || follow ? \"btn-outline-dark\" : \"btn-primary\"} rounded-5\\`} data={data}> {loading ? \"Loading\" : follow ? \"Following\" : inverse ? \"Follow back\" : \"Follow\"} </CommitButton>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const Preview = __props__ => { const accountId = __props__.accountId ?? context.accountId; const profile = Social.getr(\\`\\${accountId}/profile\\`); const name = profile.name; return <div className=\"profile d-inline-block\"> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${accountId}\\`)} className=\"text-decoration-none link-dark\"> <ProfileImage {...{ profile, accountId, className: \"float-start d-inline-block me-2\" }} /> <div className=\"profile-info d-inline-block\" style={{ maxWidth: \"16em\" }}> <div className=\"profile-name text-truncate\">{name || \"No-name profile\"}</div> <div className=\"profile-links d-flex\"> <div className=\"d-inline-block profile-account text-secondary text-truncate\">@{accountId}</div> </div> </div> </a> </div>;}; const A_159 = styled.div\\` display: flex; flex-direction: column; gap: 1rem; .profile-row { display: flex; width: 100%; justify-content: space-between; align-items: center; } .btn-primary { background-color: #dd3345; border: none; transition: all 300ms; :hover { opacity: 0.8; } }\\`; const {accountId: accountId, nav: nav} = props; const [followers, setFollowers] = useState(null); if (!accountId) { return \"\"; } const url = nav === \"followers\" ? \\`*/graph/follow/\\${accountId}\\` : \\`\\${accountId}/graph/follow/*\\`; let followersKeys = Social.keys(url, \"final\", { return_type: \"BlockHeight\", values_only: true }); if (!followers && followersKeys) { let followers = []; if (nav === \"followers\") { followers = Object.entries(followersKeys || {}); followers.sort((a, b) => b.graph.follow[accountId][1] - a.graph.follow[accountId][1]); } else { followers = Object.entries(followersKeys[accountId].graph.follow || {}); followers.sort((a, b) => b[1] - a[1]); } setFollowers(followers); } return <A_159> {followers === null ? \"Loading...\" : followers ? followers.map(([accountId], i) => <div className=\"profile-row\" key={i}> <div className=\"me-4\"> <Preview {...{ ...props, accountId }} /> </div> <div> <FollowButton accountId={accountId} /> </div> </div>) : \\`No \\${nav}\\`} </A_159>; `, DonationsInfo: ` const A_162 = styled.div\\` display: flex; flex-direction: column; padding: 24px; gap: 24px; border-radius: 10px; border: 1px solid #f4b37d; border-bottom-width: 3px; background: #fef6ee; margin-left: auto; height: fit-content; .donations-info { display: flex; gap: 4px; flex-direction: column; .amount { font-weight: 500; font-size: 2.5rem; line-height: 1; font-family: \"Lora\"; } .donors { font-size: 14px; span { font-weight: 600; } } } .btn-wrapper { display: flex; gap: 1.5rem; justify-content: space-between; > div, button { padding: 10px 0; width: 160px; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; } } @media only screen and (max-width: 480px) { width: 100%; .donations-info .amount { font-size: 2rem; } .btn-wrapper { > div, button { width: 100%; } } }\\`; const FollowContainer = styled.div\\` position: relative; cursor: pointer; border-radius: 6px; border: 1px solid #dd3345; font-size: 14px; font-weight: 600; color: #dd3345; word-wrap: break-word; transition: all 300ms; &::before { background: #dd3345; position: absolute; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; content: \"Unfollow\"; color: white; opacity: 0; transition: all 300ms; } :hover { background: #dd3345; color: white; \\${__props__ => __props__.buttonText === \"Following\" ? \\` ::before { opacity: 1; } \\` : \"\"} }\\`; const A_161 = ({ accountId, classname}) => { if (!accountId || !context.accountId || context.accountId === accountId) { return \"\"; } const followEdge = Social.keys(\\`\\${context.accountId}/graph/follow/\\${accountId}\\`, undefined, { values_only: true }); const inverseEdge = Social.keys(\\`\\${accountId}/graph/follow/\\${context.accountId}\\`, undefined, { values_only: true }); const loading = followEdge === null || inverseEdge === null; const follow = followEdge && Object.keys(followEdge).length; const inverse = inverseEdge && Object.keys(inverseEdge).length; const type = follow ? \"unfollow\" : \"follow\"; const socialArgs = { data: { [context.accountId]: { graph: { follow: { [accountId]: follow ? null : \"\" } }, index: { graph: JSON.stringify({ key: \"follow\", value: { type, accountId: accountId } }), notify: JSON.stringify({ key: accountId, value: { type } }) } } } }; const buttonText = loading ? \"Loading\" : follow ? \"Following\" : inverse ? \"Follow back\" : \"Follow\"; return <FollowContainer className={classname || \"\"} onClick={() => { const transactions = [{ contractName: \"social.near\", methodName: \"set\", deposit: Big(JSON.stringify(socialArgs).length * 0.00003).mul(Big(10).pow(24)), args: socialArgs }]; Near.call(transactions); }}> {buttonText} </FollowContainer>;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsdWithFallback = (amountNear, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas((amountNear * A_236).toFixed(2)) : formatWithCommas(amountNear.toString()) + (abbreviate ? \"N\" : \" NEAR\");}; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useDonationModal = () => useContext(\"donation-modal\"); const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const {projectId: projectId, donations: donations} = props; const { potId } = props.alem.useParams(); const { setDonationModalProps } = useDonationModal(); const [totalDonationAmountNear, uniqueDonors] = useMemo(() => { let totalNear = Big(0); const uniqueDonors = [...new Set(donations.map(donation => donation.donor_id))]; donations.forEach(donation => { if (donation.ft_id === \"near\" || donation.base_currency === \"near\") { totalNear = totalNear.plus(Big(donation.total_amount || donation.amount)); } }); const totalDonationAmountNear = constants.SUPPORTED_FTS[\"NEAR\"].fromIndivisible(totalNear.toString()); return [totalDonationAmountNear, uniqueDonors?.length]; }, [donations]); return <A_162> <div className=\"donations-info\"> <div className=\"amount\">{nearToUsdWithFallback(Number(totalDonationAmountNear))}</div> <div className=\"donors\"> Raised from <span> {uniqueDonors}</span> {uniqueDonors === 1 ? \"donor\" : \"donors\"} </div> </div> <div className=\"btn-wrapper\"> <Button type=\"primary\" text=\"Donate\" onClick={() => setDonationModalProps({ projectId, potId })} /> <A_161 accountId={projectId} /> </div> </A_162>; `, CopyIcon: ` const {textToCopy: textToCopy, customStyle: customStyle} = props; const [copied, setCopid] = useState(false); const Icon = styled.svg\\` cursor: pointer; height: 20px; transition: scale 200ms ease-in-out; \\${customStyle || \"\"} :hover { scale: 1.1; } \\`; return copied ? <svg width=\"18\" height=\"14\" viewBox=\"0 0 18 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M5.8002 10.9L1.6002 6.69999L0.200195 8.09999L5.8002 13.7L17.8002 1.69999L16.4002 0.299988L5.8002 10.9Z\" fill=\"#151A23\" /> </svg> : <Icon onClick={() => { clipboard.writeText(textToCopy); setCopid(true); setTimeout(() => { setCopid(false); }, 2000); }} viewBox=\"0 0 18 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.5 0H6.5C5.4 0 4.5 0.9 4.5 2V14C4.5 15.1 5.4 16 6.5 16H15.5C16.6 16 17.5 15.1 17.5 14V2C17.5 0.9 16.6 0 15.5 0ZM15.5 14H6.5V2H15.5V14ZM0.5 13V11H2.5V13H0.5ZM0.5 7.5H2.5V9.5H0.5V7.5ZM7.5 18H9.5V20H7.5V18ZM0.5 16.5V14.5H2.5V16.5H0.5ZM2.5 20C1.4 20 0.5 19.1 0.5 18H2.5V20ZM6 20H4V18H6V20ZM11 20V18H13C13 19.1 12.1 20 11 20ZM2.5 4V6H0.5C0.5 4.9 1.4 4 2.5 4Z\" fill=\"#7B7B7B\" /> </Icon>; `, BodyHeader: ` const CheckIcon = __props__ => <svg {...__props__} viewBox=\"0 0 18 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M5.8002 10.9L1.6002 6.69999L0.200195 8.09999L5.8002 13.7L17.8002 1.69999L16.4002 0.299988L5.8002 10.9Z\" fill=\"#151A23\" /> </svg>; const ReferrerIcon = __props__ => <svg {...__props__} viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M12.375 9.5625C14.6925 7.455 16.875 5.4825 16.875 3.7875C16.875 2.4 15.7875 1.3125 14.4 1.3125C13.62 1.3125 12.8625 1.68 12.375 2.25C11.88 1.68 11.13 1.3125 10.35 1.3125C8.9625 1.3125 7.875 2.4 7.875 3.7875C7.875 5.4825 10.0575 7.455 12.375 9.5625ZM10.35 2.8125C10.68 2.8125 11.0175 2.97 11.235 3.225L12.375 4.5675L13.515 3.225C13.7325 2.97 14.07 2.8125 14.4 2.8125C14.955 2.8125 15.375 3.2325 15.375 3.7875C15.375 4.6275 13.845 6.165 12.375 7.53C10.905 6.165 9.375 4.62 9.375 3.7875C9.375 3.2325 9.795 2.8125 10.35 2.8125Z\" fill=\"#7B7B7B\" /> <path d=\"M14.625 11.8125H13.125C13.125 10.9125 12.5625 10.1025 11.7225 9.7875L7.1025 8.0625H1.125V16.3125H5.625V15.2325L10.875 16.6875L16.875 14.8125V14.0625C16.875 12.8175 15.87 11.8125 14.625 11.8125ZM2.625 14.8125V9.5625H4.125V14.8125H2.625ZM10.8525 15.12L5.625 13.6725V9.5625H6.8325L11.1975 11.19C11.4525 11.2875 11.625 11.535 11.625 11.8125C11.625 11.8125 10.1325 11.775 9.9 11.7L8.115 11.1075L7.6425 12.5325L9.4275 13.125C9.81 13.2525 10.2075 13.32 10.6125 13.32H14.625C14.9175 13.32 15.18 13.4925 15.3 13.74L10.8525 15.12Z\" fill=\"#7B7B7B\" /> </svg>; const FollowContainer = styled.div\\` position: relative; cursor: pointer; border-radius: 6px; border: 1px solid #dd3345; font-size: 14px; font-weight: 600; color: #dd3345; word-wrap: break-word; transition: all 300ms; &::before { background: #dd3345; position: absolute; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; content: \"Unfollow\"; color: white; opacity: 0; transition: all 300ms; } :hover { background: #dd3345; color: white; \\${__props__ => __props__.buttonText === \"Following\" ? \\` ::before { opacity: 1; } \\` : \"\"} }\\`; const A_161 = ({ accountId, classname}) => { if (!accountId || !context.accountId || context.accountId === accountId) { return \"\"; } const followEdge = Social.keys(\\`\\${context.accountId}/graph/follow/\\${accountId}\\`, undefined, { values_only: true }); const inverseEdge = Social.keys(\\`\\${accountId}/graph/follow/\\${context.accountId}\\`, undefined, { values_only: true }); const loading = followEdge === null || inverseEdge === null; const follow = followEdge && Object.keys(followEdge).length; const inverse = inverseEdge && Object.keys(inverseEdge).length; const type = follow ? \"unfollow\" : \"follow\"; const socialArgs = { data: { [context.accountId]: { graph: { follow: { [accountId]: follow ? null : \"\" } }, index: { graph: JSON.stringify({ key: \"follow\", value: { type, accountId: accountId } }), notify: JSON.stringify({ key: accountId, value: { type } }) } } } }; const buttonText = loading ? \"Loading\" : follow ? \"Following\" : inverse ? \"Follow back\" : \"Follow\"; return <FollowContainer className={classname || \"\"} onClick={() => { const transactions = [{ contractName: \"social.near\", methodName: \"set\", deposit: Big(JSON.stringify(socialArgs).length * 0.00003).mul(Big(10).pow(24)), args: socialArgs }]; Near.call(transactions); }}> {buttonText} </FollowContainer>;}; const LinktreeContainer = styled.div\\` display: flex; flex-wrap: wrap; -webkit-box-pack: start; justify-content: flex-start; gap: 1rem;\\`;const LinktreeItemContainer = styled.a\\` display: flex; svg { width: 24px; height: 24px; path, rect { transition: all 300ms ease-in-out; } &#near-logo:hover path { fill: white; } :hover path, :hover rect { fill: #292929; } }\\`;const LinkText = styled.a\\` font-size: 14px; color: gray; font-weight: 400; margin-left: 16px; cursor: \\${__props__ => __props__.disabled ? \"not-allowed\" : \"pointer\"}; &:hover { text-decoration: none; }\\`; const WebsiteSvg = () => <A_163 viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 20C8.63333 20 7.34167 19.7375 6.125 19.2125C4.90833 18.6875 3.84583 17.9708 2.9375 17.0625C2.02917 16.1542 1.3125 15.0917 0.7875 13.875C0.2625 12.6583 0 11.3667 0 10C0 8.61667 0.2625 7.32083 0.7875 6.1125C1.3125 4.90417 2.02917 3.84583 2.9375 2.9375C3.84583 2.02917 4.90833 1.3125 6.125 0.7875C7.34167 0.2625 8.63333 0 10 0C11.3833 0 12.6792 0.2625 13.8875 0.7875C15.0958 1.3125 16.1542 2.02917 17.0625 2.9375C17.9708 3.84583 18.6875 4.90417 19.2125 6.1125C19.7375 7.32083 20 8.61667 20 10C20 11.3667 19.7375 12.6583 19.2125 13.875C18.6875 15.0917 17.9708 16.1542 17.0625 17.0625C16.1542 17.9708 15.0958 18.6875 13.8875 19.2125C12.6792 19.7375 11.3833 20 10 20ZM10 17.95C10.4333 17.35 10.8083 16.725 11.125 16.075C11.4417 15.425 11.7 14.7333 11.9 14H8.1C8.3 14.7333 8.55833 15.425 8.875 16.075C9.19167 16.725 9.56667 17.35 10 17.95ZM7.4 17.55C7.1 17 6.8375 16.4292 6.6125 15.8375C6.3875 15.2458 6.2 14.6333 6.05 14H3.1C3.58333 14.8333 4.1875 15.5583 4.9125 16.175C5.6375 16.7917 6.46667 17.25 7.4 17.55ZM12.6 17.55C13.5333 17.25 14.3625 16.7917 15.0875 16.175C15.8125 15.5583 16.4167 14.8333 16.9 14H13.95C13.8 14.6333 13.6125 15.2458 13.3875 15.8375C13.1625 16.4292 12.9 17 12.6 17.55ZM2.25 12H5.65C5.6 11.6667 5.5625 11.3375 5.5375 11.0125C5.5125 10.6875 5.5 10.35 5.5 10C5.5 9.65 5.5125 9.3125 5.5375 8.9875C5.5625 8.6625 5.6 8.33333 5.65 8H2.25C2.16667 8.33333 2.10417 8.6625 2.0625 8.9875C2.02083 9.3125 2 9.65 2 10C2 10.35 2.02083 10.6875 2.0625 11.0125C2.10417 11.3375 2.16667 11.6667 2.25 12ZM7.65 12H12.35C12.4 11.6667 12.4375 11.3375 12.4625 11.0125C12.4875 10.6875 12.5 10.35 12.5 10C12.5 9.65 12.4875 9.3125 12.4625 8.9875C12.4375 8.6625 12.4 8.33333 12.35 8H7.65C7.6 8.33333 7.5625 8.6625 7.5375 8.9875C7.5125 9.3125 7.5 9.65 7.5 10C7.5 10.35 7.5125 10.6875 7.5375 11.0125C7.5625 11.3375 7.6 11.6667 7.65 12ZM14.35 12H17.75C17.8333 11.6667 17.8958 11.3375 17.9375 11.0125C17.9792 10.6875 18 10.35 18 10C18 9.65 17.9792 9.3125 17.9375 8.9875C17.8958 8.6625 17.8333 8.33333 17.75 8H14.35C14.4 8.33333 14.4375 8.6625 14.4625 8.9875C14.4875 9.3125 14.5 9.65 14.5 10C14.5 10.35 14.4875 10.6875 14.4625 11.0125C14.4375 11.3375 14.4 11.6667 14.35 12ZM13.95 6H16.9C16.4167 5.16667 15.8125 4.44167 15.0875 3.825C14.3625 3.20833 13.5333 2.75 12.6 2.45C12.9 3 13.1625 3.57083 13.3875 4.1625C13.6125 4.75417 13.8 5.36667 13.95 6ZM8.1 6H11.9C11.7 5.26667 11.4417 4.575 11.125 3.925C10.8083 3.275 10.4333 2.65 10 2.05C9.56667 2.65 9.19167 3.275 8.875 3.925C8.55833 4.575 8.3 5.26667 8.1 6ZM3.1 6H6.05C6.2 5.36667 6.3875 4.75417 6.6125 4.1625C6.8375 3.57083 7.1 3 7.4 2.45C6.46667 2.75 5.6375 3.20833 4.9125 3.825C4.1875 4.44167 3.58333 5.16667 3.1 6Z\" fill=\"#7B7B7B\" /> </A_163>; const TwitterSvg = () => <A_163 xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 21 17\" fill=\"none\"> <path d=\"M20.92 2C20.15 2.35 19.32 2.58 18.46 2.69C19.34 2.16 20.02 1.32 20.34 0.31C19.51 0.81 18.59 1.16 17.62 1.36C16.83 0.5 15.72 0 14.46 0C12.11 0 10.19 1.92 10.19 4.29C10.19 4.63 10.23 4.96 10.3 5.27C6.74 5.09 3.57 3.38 1.46 0.79C1.09 1.42 0.88 2.16 0.88 2.94C0.88 4.43 1.63 5.75 2.79 6.5C2.08 6.5 1.42 6.3 0.84 6V6.03C0.84 8.11 2.32 9.85 4.28 10.24C3.65073 10.4122 2.9901 10.4362 2.35 10.31C2.62161 11.1625 3.15354 11.9084 3.87102 12.4429C4.5885 12.9775 5.45545 13.2737 6.35 13.29C4.83363 14.4904 2.954 15.1393 1.02 15.13C0.68 15.13 0.34 15.11 0 15.07C1.9 16.29 4.16 17 6.58 17C14.46 17 18.79 10.46 18.79 4.79C18.79 4.6 18.79 4.42 18.78 4.23C19.62 3.63 20.34 2.87 20.92 2Z\" fill=\"#7B7B7B\" /> </A_163>; const NearSvg = __props__ => <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" id=\"near-logo\" {...__props__}> <rect width=\"24\" height=\"24\" rx=\"12\" fill=\"#7B7B7B\" /> <path d=\"M15.616 6.61333L13.1121 10.3333C12.939 10.5867 13.2719 10.8933 13.5117 10.68L15.9756 8.53333C16.0422 8.48 16.1354 8.52 16.1354 8.61333V15.32C16.1354 15.4133 16.0155 15.4533 15.9623 15.3867L8.50388 6.45333C8.26415 6.16 7.91787 6 7.53163 6H7.26526C6.5727 6 6 6.57333 6 7.28V16.72C6 17.4267 6.5727 18 7.27858 18C7.71809 18 8.13097 17.7733 8.3707 17.3867L10.8746 13.6667C11.0477 13.4133 10.7148 13.1067 10.475 13.32L8.0111 15.4533C7.94451 15.5067 7.85128 15.4667 7.85128 15.3733V8.68C7.85128 8.58667 7.97114 8.54667 8.02442 8.61333L15.4828 17.5467C15.7225 17.84 16.0821 18 16.4551 18H16.7214C17.4273 18 18 17.4267 18 16.72V7.28C18 6.57333 17.4273 6 16.7214 6C16.2686 6 15.8557 6.22667 15.616 6.61333Z\" fill=\"#fff\" /> </svg>; const A_163 = styled.svg\\` width: 24px; height: 24px; path, rect { transition: all 300ms ease-in-out; } &#near-logo:hover path { fill: white; } :hover path, :hover rect { fill: #292929; }\\`; const GithubSvg = () => <A_163 xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" fill=\"none\"> <path d=\"M10 0C8.68678 0 7.38642 0.258658 6.17317 0.761205C4.95991 1.26375 3.85752 2.00035 2.92893 2.92893C1.05357 4.8043 0 7.34784 0 10C0 14.42 2.87 18.17 6.84 19.5C7.34 19.58 7.5 19.27 7.5 19V17.31C4.73 17.91 4.14 15.97 4.14 15.97C3.68 14.81 3.03 14.5 3.03 14.5C2.12 13.88 3.1 13.9 3.1 13.9C4.1 13.97 4.63 14.93 4.63 14.93C5.5 16.45 6.97 16 7.54 15.76C7.63 15.11 7.89 14.67 8.17 14.42C5.95 14.17 3.62 13.31 3.62 9.5C3.62 8.39 4 7.5 4.65 6.79C4.55 6.54 4.2 5.5 4.75 4.15C4.75 4.15 5.59 3.88 7.5 5.17C8.29 4.95 9.15 4.84 10 4.84C10.85 4.84 11.71 4.95 12.5 5.17C14.41 3.88 15.25 4.15 15.25 4.15C15.8 5.5 15.45 6.54 15.35 6.79C16 7.5 16.38 8.39 16.38 9.5C16.38 13.32 14.04 14.16 11.81 14.41C12.17 14.72 12.5 15.33 12.5 16.26V19C12.5 19.27 12.66 19.59 13.17 19.5C17.14 18.16 20 14.42 20 10C20 8.68678 19.7413 7.38642 19.2388 6.17317C18.7362 4.95991 17.9997 3.85752 17.0711 2.92893C16.1425 2.00035 15.0401 1.26375 13.8268 0.761205C12.6136 0.258658 11.3132 0 10 0Z\" fill=\"#7B7B7B\" /> </A_163>; const Linktree = ({ projectId, accountId}) => { const id = projectId || accountId || \"\"; const profile = Social.getr(\\`\\${id}/profile\\`); const linktree = profile?.linktree; if (!linktree) return \"\"; const itemIconUrls = { github: <GithubSvg />, twitter: <TwitterSvg />, website: <WebsiteSvg />, NEAR: <NearSvg /> }; const fullUrls = { twitter: handle => \\`https://twitter.com/\\${handle.trim()}\\`, github: username => \\`https://github.com/\\${username.trim()}\\`, website: url => url.includes(\"http\") ? url : \\`https://\\${url.trim()}\\` }; return <LinktreeContainer> {Object.entries(linktree).map(([k, v]) => { return k in itemIconUrls && v ? <LinktreeItemContainer key={k} href={fullUrls[k](v)} onClick={e => { if (!v) { e.preventDefault(); } }} target=\"_blank\"> {itemIconUrls[k]} </LinktreeItemContainer> : null; })} <LinktreeItemContainer target=\"_blank\" href={\\`https://near.social/mob.near/widget/ProfilePage?accountId=\\${projectId || accountId}\\`}> {itemIconUrls.NEAR} </LinktreeItemContainer> </LinktreeContainer>;}; const getTagsFromSocialProfileData = profileData => { if (!profileData) return []; const DEPRECATED_CATEGORY_MAPPINGS = { \"social-impact\": \"Social Impact\", \"non-profit\": \"NonProfit\", climate: \"Climate\", \"public-good\": \"Public Good\", \"de-sci\": \"DeSci\", \"open-source\": \"Open Source\", community: \"Community\", education: \"Education\" }; const tags = profileData.plCategories ? JSON.parse(profileData.plCategories) : profileData.category ? [profileData.category.text ?? DEPRECATED_CATEGORY_MAPPINGS[profileData.category] ?? \"\"] : []; return tags;}; const ProfileTags = ({ projectId, accountId}) => { const TagsContainer = styled.div\\` display: flex; gap: 12px; flex-wrap: wrap; max-width: 600px; \\`; const Tag = styled.span\\` color: #292929; font-size: 14px; font-weight: 500; padding: 4px 6px; border-radius: 2px; background: #ebebeb; box-shadow: 0px -1px 0px 0px #dbdbdb inset, 0px 0px 0px 0.5px #dbdbdb; \\`; const projectProfile = Social.getr(\\`\\${projectId || accountId}/profile\\`); const tags = accountId ? Object.keys(projectProfile.tags || {}) : getTagsFromSocialProfileData(projectProfile); if (!tags.length) return \"No tags\"; return <TagsContainer> {projectId && projectId.endsWith(\".sputnik-dao.near\") && <Tag>DAO</Tag>} {tags.map((tag, tagIndex) => <Tag key={tagIndex}>{tag}</Tag>)} </TagsContainer>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_164 = styled.div\\` display: flex; flex-direction: column; padding: 0 4rem; margin-bottom: 66px; width: 100%; gap: 0.5rem; @media screen and (max-width: 768px) { padding: 0 1rem; }\\`;const A_165 = styled.div\\` display: flex; width: 100%; flex-wrap: wrap; gap: 2rem; .follow-btn { padding: 10px 0px; width: 160px; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; height: fit-content; } @media only screen and (max-width: 480px) { flex-direction: column; .follow-btn { margin-left: auto; } }\\`;const Info = styled.div\\` display: flex; flex-direction: column; gap: 24px; flex: 1;\\`;const NameContainer = styled.div\\` display: flex; width: 100%; flex-wrap: wrap; flex-direction: row; align-items: end; gap: 1rem;\\`;const LinksWrapper = styled.div\\` display: flex; gap: 2rem; flex-wrap: wrap;\\`;const ReferralButton = styled.div\\` display: flex; gap: 0.5rem; align-items: center; cursor: pointer; div { font-size: 14px; font-weight: 500; } svg { width: 18px; } svg path { transition: fill 300ms ease-in-out; } :hover svg path { fill: #292929; }\\`;const Name = styled.div\\` font-size: 40px; font-weight: 500; color: #2e2e2e; line-height: 1; font-family: \"Lora\"; @media screen and (max-width: 768px) { font-size: 32px; line-height: 40px; }\\`;const AccountInfoContainer = styled.div\\` display: flex; gap: 0.5rem; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const AccountId = styled.div\\` font-size: 16px; font-weight: 400; @media screen and (max-width: 768px) { font-size: 14px; }\\`; const { profile: profile, accountId: accountId, projectId: projectId } = props; const name = profile.name; const policy = projectId ? Near.view(projectId, \"get_policy\", {}) : false; const isDao = !!policy; const [copid, setCopid] = useState(false); const userHasPermissions = useMemo(() => { if (!policy) return false; const userRoles = policy.roles.filter((role) => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(context.accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); return allowed; }, [policy]); const isOwner = projectId === context.accountId; const isPermissionedMember = isDao && userHasPermissions; const canEdit = isOwner || isPermissionedMember; const id = projectId || accountId || \"\"; return <A_164> <A_165> <Info> <NameContainer> <Name>{name.length > 25 ? name.slice(0, 25).trim() + \"...\" : name}</Name> <AccountInfoContainer> <AccountId>@ {id.length > 15 ? id.slice(0, 15).trim() + \"...\" : id}</AccountId> <Widget loading=\" \" code={props.alem.componentsCode.CopyIcon} props={{ ...{ textToCopy: id, customStyle: \"height: 18px;\", ...props } }} /> </AccountInfoContainer> {canEdit && <Button type=\"secondary\" text=\"Edit profile\" style={{ marginLeft: \"auto\" }} href={hrefWithParams(\\`?tab=editproject&projectId=\\${projectId}\\`)} />} {accountId === context.accountId && !projectId && <Button type=\"secondary\" text=\"Edit profile\" style={{ marginLeft: \"auto\" }} href={hrefWithParams(\\`?tab=editprofile\\`)} />} </NameContainer> <ProfileTags projectId={projectId} accountId={accountId} /> <LinksWrapper> <Linktree projectId={projectId} accountId={accountId} /> {projectId && context.accountId && <ReferralButton onClick={() => { clipboard.writeText(\\`https://bos.potlock.org/staging.potlock.near/widget/IndexLoader?tab=project&projectId=\\${projectId}&referrerId=\\${context.accountId}\\`); setCopid(true); setTimeout(() => { setCopid(false); }, 2000); }}> {copid ? <CheckIcon /> : <ReferrerIcon />} <div>Earn referral fees</div> </ReferralButton>} </LinksWrapper> </Info> {projectId ? <Widget loading=\" \" code={props.alem.componentsCode.DonationsInfo} props={{ ...{ projectId: projectId, ...props } }} /> : accountId !== context.accountId ? <A_161 accountId={accountId || \"\"} classname=\"follow-btn\" /> : \"\"} </A_165> </A_164>; `, Body: ` const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const A_160 = ({ navOptions, nav}) => { const getSelectedNavOption = () => { const navOption = navOptions.find((option) => option.id == nav); return navOption ?? navOptions[0]; }; const NavOptionsContainer = styled.div\\` display: flex; justify-content: flex-start; gap: 2rem; width: 100%; border-bottom: 1px solid #c7c7c7; padding: 0 4rem; .nav-option { font-size: 14px; color: #7b7b7b; padding: 10px 16px; font-weight: 500; white-space: nowrap; border-bottom: 2px solid transparent; transition: 300ms ease; &.disabled { pointer-events: none; cursor: not-allowed; } &.selected { color: #292929; border-bottom-color: #292929; } :hover { border-bottom-color: #292929; text-decoration: none; } } @media screen and (max-width: 768px) { padding: 0px 1rem; overflow-x: scroll; .nav-option.selected { order: -1; } } \\`; return <NavOptionsContainer> {navOptions.map((option) => { const selected = option.id == getSelectedNavOption().id; return option.label ? <a className={\\`nav-option \\${selected && \"selected\"} \\${option.disabled && \"disabled\"}\\`} href={option.href}> {option.label} </a> : \"\"; })} </NavOptionsContainer>;}; const A_233 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_234 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_235 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 10px; background: #ffffff; border: 1px solid #d0d5dd; /* box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); */ box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset; border-radius: 4px; color: #101828; width: 100%;\\`;const Placeholder = styled.span\\` color: #a0a3a8;\\`;const scaleOut = styled.keyframes\\` from { transform: scaleY(0); } to { transform: scaleY(1); }\\`;const SelectContent = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; gap: 0.5em; width: 100%; border: 1px solid #d0d5dd; border-radius: 4px; background: #ffffff; z-index: 3 !important;\\`;const Viewport = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; width: 100%;\\`;const Item = styled.button\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 0.5em; width: 100%; cursor: pointer; background: transparent; border: none; transition: background 0.2s ease-in-out; &:nth-child(n + 1) { border-top: 1px solid #d0d5dd; } &:hover { background: #d0d5dd; boder: none; } &:focus { outline: none; }\\`; const Select = (componentProps) => { const label = componentProps.label ?? \"Label\"; const noLabel = componentProps.noLabel ?? false; const placeholder = componentProps.placeholder ?? \"Select an option\"; const value = componentProps.value ?? \"\"; const options = componentProps.options ?? []; const onChange = componentProps.onChange ?? (() => {}); const validate = componentProps.validate ?? (() => {}); const error = componentProps.error ?? \"\"; return <A_233 style={componentProps.containerStyles || {}}> {noLabel ? <></> : <A_234>{label}</A_234>} <Select.Root value={value?.value} onValueChange={(value) => onChange(options.find((option) => option.value === value))}> <Select.Trigger asChild={true}> <A_235 style={componentProps.inputStyles || {}}> {componentProps.iconLeft && componentProps.iconLeft} <Select.Value aria-label={value.value} placeholder={<Placeholder>{placeholder}</Placeholder>} /> {} <Select.Icon> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1 1.5L6 6.5L11 1.5\" stroke=\"currentColor\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg> </Select.Icon> {} </A_235> </Select.Trigger> <Select.Content asChild={true}> <SelectContent> <Select.Viewport asChild={true}> <Viewport> {options.map(({ text, value }) => <Select.Item value={value} asChild={true}> <Item> <Select.ItemText>{text}</Select.ItemText> <Select.ItemIndicator> <svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" /> </svg> </Select.ItemIndicator> </Item> </Select.Item>)} </Viewport> </Select.Viewport> </SelectContent> </Select.Content> </Select.Root> </A_233>;}; const A_166 = styled.div\\` padding-top: 0; position: relative;\\`;const ProfileWraper = styled.div\\` display: flex; padding-left: 4rem; align-items: end; transform: translateY(-50%); position: relative; z-index: 6; @media screen and (max-width: 768px) { padding-left: 8px; }\\`;const ProfileStats = styled.div\\` position: relative; z-index: 1; display: flex; align-items: center; gap: 1.5rem; transform: translate(-25px, -20px); @media screen and (max-width: 768px) { transform: translate(-25px, 0px); gap: 10px; }\\`;const Verified = styled.div\\` opacity: 1; display: flex; align-items: center; font-size: 11px; letter-spacing: 0.88px; gap: 4px; padding: 3px; border-radius: 20px; background: #fff; text-transform: uppercase; overflow: hidden; &.not-verified { width: 10px; opacity: 0; } div { font-weight: 600; } svg { background: white; border-radius: 50%; } @media screen and (max-width: 768px) { div { display: none; } }\\`;const ProfileImageContainer = styled.div\\` background: white; border-radius: 50%; padding: 6px; position: relative; width: 120px; height: 120px; &.editable { &:after { content: \"\"; position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%; border-radius: 50%; background-color: rgba(45.9, 45.9, 45.9, 0); transition: background-color 0.3s; pointer-events: none; @media screen and (max-width: 768px) { height: 64px; } } &:hover { cursor: pointer; &:after { background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; } } } .profile-image { height: 100%; width: 100%; display: flex; border-radius: 50%; img { object-fit: cover; width: 100%; height: 100%; } } > svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0; /* Start with the image invisible */ transition: opacity 0.3s; z-index: 2; /* Ensure the image is on top */ pointer-events: none; } @media screen and (max-width: 768px) { width: 100px; height: 100px; }\\`;const BackgroundImageContainer = styled.div\\` position: relative; width: 100%; height: 318px; display: flex; &.editable { &:after { content: \"\"; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(45.9, 45.9, 45.9, 0); transition: background-color 0.3s; pointer-events: none; } &:hover { cursor: pointer; &:after { background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; } } } img { object-fit: cover; height: 100%; width: 100%; } svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0; transition: opacity 0.3s; z-index: 2; pointer-events: none; } @media screen and (max-width: 768px) { height: 264px; }\\`; const statuses = { Approved: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M10.7835 0.943599C10.3834 0.454917 9.6361 0.454917 9.236 0.943599L8.44604 1.90847C8.17411 2.24062 7.72052 2.36216 7.31895 2.21048L6.15238 1.76985C5.56155 1.54669 4.91436 1.92035 4.8122 2.5436L4.61051 3.77419C4.54108 4.19781 4.20904 4.52985 3.78542 4.59928L2.55484 4.80097C1.93158 4.90313 1.55792 5.55032 1.78108 6.14115L2.22171 7.30772C2.37339 7.70929 2.25185 8.16288 1.9197 8.43481L0.95483 9.22477C0.466148 9.62487 0.466147 10.3722 0.954829 10.7723L1.9197 11.5622C2.25185 11.8342 2.37339 12.2878 2.22171 12.6893L1.78108 13.8559C1.55792 14.4467 1.93158 15.0939 2.55484 15.1961L3.78542 15.3978C4.20904 15.4672 4.54108 15.7992 4.61051 16.2229L4.8122 17.4534C4.91436 18.0767 5.56155 18.4504 6.15238 18.2272L7.31895 17.7866C7.72052 17.6349 8.17411 17.7564 8.44604 18.0886L9.236 19.0535C9.6361 19.5421 10.3834 19.5421 10.7835 19.0535L11.5735 18.0886C11.8454 17.7564 12.299 17.6349 12.7006 17.7866L13.8671 18.2272C14.458 18.4504 15.1052 18.0767 15.2073 17.4534L15.409 16.2229C15.4784 15.7992 15.8105 15.4672 16.2341 15.3978L17.4647 15.1961C18.0879 15.0939 18.4616 14.4467 18.2384 13.8559L17.7978 12.6893C17.6461 12.2878 17.7677 11.8342 18.0998 11.5622L19.0647 10.7723C19.5534 10.3722 19.5534 9.62487 19.0647 9.22478L18.0998 8.43481C17.7677 8.16288 17.6461 7.70929 17.7978 7.30772L18.2384 6.14115C18.4616 5.55032 18.0879 4.90313 17.4647 4.80097L16.2341 4.59928C15.8105 4.52985 15.4784 4.19781 15.409 3.77419L15.2073 2.54361C15.1052 1.92035 14.458 1.54669 13.8671 1.76985L12.7006 2.21048C12.299 2.36216 11.8454 2.24062 11.5735 1.90847L10.7835 0.943599ZM13.5029 7.49591C13.3065 7.30179 12.9899 7.3036 12.7957 7.49996L8.92649 11.4119L7.25946 9.53742C7.07595 9.33107 6.75991 9.31256 6.55356 9.49607C6.34722 9.67958 6.3287 9.99562 6.51221 10.202L8.8858 12.8709L9.90713 11.8496L13.5073 8.20316C13.7013 8.00663 13.6994 7.69003 13.5029 7.49591Z\" fill=\"#0DBFAF\" /> </svg>, color: \"#0E615E\" }, Rejected: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM13.59 5L10 8.59L6.41 5L5 6.41L8.59 10L5 13.59L6.41 15L10 11.41L13.59 15L15 13.59L11.41 10L15 6.41L13.59 5Z\" fill=\"#F6767A\" /> </svg>, color: \"#ED464F\" }, Pending: { icon: <svg width=\"21\" height=\"20\" viewBox=\"0 0 21 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.1523 0C4.63234 0 0.152344 4.48 0.152344 10C0.152344 15.52 4.63234 20 10.1523 20C15.6723 20 20.1523 15.52 20.1523 10C20.1523 4.48 15.6723 0 10.1523 0ZM10.1523 18C5.73234 18 2.15234 14.42 2.15234 10C2.15234 5.58 5.73234 2 10.1523 2C14.5723 2 18.1523 5.58 18.1523 10C18.1523 14.42 14.5723 18 10.1523 18Z\" fill=\"#F4B37D\" /> <path d=\"M5.15234 11.5C5.98077 11.5 6.65234 10.8284 6.65234 10C6.65234 9.17157 5.98077 8.5 5.15234 8.5C4.32392 8.5 3.65234 9.17157 3.65234 10C3.65234 10.8284 4.32392 11.5 5.15234 11.5Z\" fill=\"#F4B37D\" /> <path d=\"M10.1523 11.5C10.9808 11.5 11.6523 10.8284 11.6523 10C11.6523 9.17157 10.9808 8.5 10.1523 8.5C9.32392 8.5 8.65234 9.17157 8.65234 10C8.65234 10.8284 9.32392 11.5 10.1523 11.5Z\" fill=\"#F4B37D\" /> <path d=\"M15.1523 11.5C15.9808 11.5 16.6523 10.8284 16.6523 10C16.6523 9.17157 15.9808 8.5 15.1523 8.5C14.3239 8.5 13.6523 9.17157 13.6523 10C13.6523 10.8284 14.3239 11.5 15.1523 11.5Z\" fill=\"#F4B37D\" /> </svg>, color: \"#EA6A25\" }, Graylisted: { icon: <svg width=\"21\" height=\"20\" viewBox=\"0 0 21 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9.15234 16H11.1523V14H9.15234V16ZM10.1523 0C4.63234 0 0.152344 4.48 0.152344 10C0.152344 15.52 4.63234 20 10.1523 20C15.6723 20 20.1523 15.52 20.1523 10C20.1523 4.48 15.6723 0 10.1523 0ZM10.1523 18C5.74234 18 2.15234 14.41 2.15234 10C2.15234 5.59 5.74234 2 10.1523 2C14.5623 2 18.1523 5.59 18.1523 10C18.1523 14.41 14.5623 18 10.1523 18ZM10.1523 4C7.94234 4 6.15234 5.79 6.15234 8H8.15234C8.15234 6.9 9.05234 6 10.1523 6C11.2523 6 12.1523 6.9 12.1523 8C12.1523 10 9.15234 9.75 9.15234 13H11.1523C11.1523 10.75 14.1523 10.5 14.1523 8C14.1523 5.79 12.3623 4 10.1523 4Z\" fill=\"#7B7B7B\" /> </svg>, color: \"#7B7B7B\" }, Blacklisted: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM2 10C2 5.58 5.58 2 10 2C11.85 2 13.55 2.63 14.9 3.69L3.69 14.9C2.63 13.55 2 11.85 2 10ZM10 18C8.15 18 6.45 17.37 5.1 16.31L16.31 5.1C17.37 6.45 18 8.15 18 10C18 14.42 14.42 18 10 18Z\" fill=\"#292929\" /> </svg>, color: \"#292929\" }}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const FollowStats = ({ projectId: _projectId, accountId: _accountId}) => { const projectId = _projectId; const accountId = projectId || _accountId; if (!accountId) { return \"\"; } const following = Social.keys(\\`\\${accountId}/graph/follow/*\\`, \"final\", { return_type: \"BlockHeight\", values_only: true }); const followers = Social.keys(\\`*/graph/follow/\\${accountId}\\`, \"final\", { return_type: \"BlockHeight\", values_only: true }); const numFollowing = following ? Object.keys(following[accountId].graph.follow || {}).length : 0; const numFollowers = followers ? Object.keys(followers || {}).length : 0; const profileLink = hrefWithParams(\\`?tab=\\${projectId ? \"project\" : \"profile\"}&\\${projectId ? \"projectId\" : \"accountId\"}=\\${projectId || accountId}\\`); const Container = styled.div\\` display: flex; align-items: center; font-size: 14px; gap: 2rem; a { gap: 8px; display: flex; } @media screen and (max-width: 768px) { gap: 1rem; } \\`; return <Container> <div> <a href={\\`\\${profileLink}&nav=followers\\`} className=\"text-dark\"> {numFollowers !== null ? <span style={{ fontWeight: 600 }}>{numFollowers}</span> : \"-\"} <span>Follower{numFollowers !== 1 && \"s\"}</span> </a> </div> <div> <a href={\\`\\${profileLink}&nav=following\\`} className=\"text-dark\"> {numFollowing !== null ? <span style={{ fontWeight: 600 }}>{numFollowing}</span> : \"-\"} <span>Following</span> </a> </div> </Container>;}; const CameraSvg = ({ height}) => <svg width={height || 48} height={height || 48} viewBox={\\`0 0 48 48\\`} fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <mask id=\"mask0_3178_2528\" style={{ maskType: \"alpha\" }} maskUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width={height || 48} height={height || 48}> <rect width={height || 48} height={height || 48} fill=\"#D9D9D9\" /> </mask> <g mask=\"url(#mask0_3178_2528)\"> <path d=\"M5 40.35C4.2 40.35 3.5 40.05 2.9 39.45C2.3 38.85 2 38.15 2 37.35V11.7C2 10.9333 2.3 10.2417 2.9 9.625C3.5 9.00833 4.2 8.7 5 8.7H12.35L16 4.35H29.7V7.35H17.4L13.75 11.7H5V37.35H39V16.65H42V37.35C42 38.15 41.6917 38.85 41.075 39.45C40.4583 40.05 39.7667 40.35 39 40.35H5ZM39 11.65V7.35H34.7V4.35H39V0H42V4.35H46.35V7.35H42V11.65H39ZM21.975 33C24.3917 33 26.4167 32.1833 28.05 30.55C29.6833 28.9167 30.5 26.8917 30.5 24.475C30.5 22.0583 29.6833 20.0417 28.05 18.425C26.4167 16.8083 24.3917 16 21.975 16C19.5583 16 17.5417 16.8083 15.925 18.425C14.3083 20.0417 13.5 22.0583 13.5 24.475C13.5 26.8917 14.3083 28.9167 15.925 30.55C17.5417 32.1833 19.5583 33 21.975 33ZM21.975 30C20.3917 30 19.0833 29.475 18.05 28.425C17.0167 27.375 16.5 26.0583 16.5 24.475C16.5 22.8917 17.0167 21.5833 18.05 20.55C19.0833 19.5167 20.3917 19 21.975 19C23.5583 19 24.875 19.5167 25.925 20.55C26.975 21.5833 27.5 22.8917 27.5 24.475C27.5 26.0583 26.975 27.375 25.925 28.425C24.875 29.475 23.5583 30 21.975 30Z\" fill=\"white\" /> </g> </svg>; const BannerHeader = (__props__) => { const { showFollowers, registration, projectId, profileImageOnChange, bgImageOnChange } = __props__; const accountId = __props__.accountId || projectId || context.accountId; if (!accountId) { return \"No account ID\"; } const editable = bgImageOnChange && profileImageOnChange; const profile = __props__.profile ?? Social.getr(\\`\\${accountId}/profile\\`); const image = profile.image; const backgroundImage = __props__.backgroundImage || profile.backgroundImage; const profileImage = __props__.profileImage || image; const imageStyle = __props__.imageStyle ?? {}; const backgroundStyle = __props__.backgroundStyle ?? {}; const containerStyle = __props__.containerStyle ?? {}; const nadaBotVerified = Near.view(\"v1.nadabot.near\", \"is_human\", { account_id: accountId }); return <A_166 style={{ ...containerStyle }}> <BackgroundImageContainer className={editable ? \"editable\" : \"\"} style={{ height: backgroundStyle.height ? backgroundStyle.height : \"\" }}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: backgroundImage, alt: \"profile background\", style: { ...backgroundStyle, pointerEvents: \"none\" }, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\" }, ...props } }} /> <CameraSvg height={48} /> {editable && <Files multiple={false} accepts={[\"image/*\"]} minFileSize={1} style={{ zIndex: 4, top: 0, width: \"100%\", height: backgroundStyle.height ?? \"100%\", position: \"absolute\" }} clickable onChange={bgImageOnChange} />} </BackgroundImageContainer> <ProfileWraper> <ProfileImageContainer className={editable ? \"editable\" : \"\"}> <CameraSvg height={24} /> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ style: { ...imageStyle }, className: \"profile-image\", image: profileImage, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\", ...props } }} /> {editable && <Files multiple={false} accepts={[\"image/*\"]} minFileSize={1} style={{ position: \"absolute\", width: \"100%\", height: \"100%\", left: 0, top: 0 }} clickable onChange={profileImageOnChange}> </Files>} </ProfileImageContainer> {showFollowers && <ProfileStats> {registration ? <Verified> {statuses[registration.status].icon} <div style={{ color: statuses[registration.status].color }}> {registration.status}</div> </Verified> : nadaBotVerified ? <Verified> {statuses.Approved.icon} <div style={{ color: statuses.Approved.color }}> Verified</div> </Verified> : <div style={{ width: \"10px\" }} />} <FollowStats accountId={projectId || accountId} projectId={projectId} /> </ProfileStats>} </ProfileWraper> {__props__.children && __props__.children} </A_166>; }; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const A_167 = styled.div\\` display: flex; flex-direction: column;\\`;const A_168 = styled.div\\` display: flex; flex-direction: column; justify-content: flex-start; overflow: hidden;\\`;const Details = styled.div\\` display: flex; flex-direction: column; gap: 2rem; width: 100%; padding: 3rem 4rem 24px; @media screen and (max-width: 768px) { padding: 24px 1rem; }\\`;const SidebarContainer = styled.div\\` width: 15%; padding-left: 1rem; @media screen and (max-width: 768px) { width: fit-content; > div:first-of-type { display: none; } }\\`;const A_169 = styled.div\\` color: #525252; font-size: 16px; font-weight: 600; line-height: 20px; word-wrap: break-word; margin-bottom: 8px;\\`;const A_170 = styled.div\\` display: flex; flex-direction: row; align-items: center;\\`; const props = props; const { projectId } = props; const { accountId: _accountId } = props.alem.useParams(); const accountId = _accountId ?? context.accountId; const { PROJECT_STATUSES, SUPPORTED_FTS: { NEAR } } = constants; const [statusReview, setStatusReview] = useState({ modalOpen: false, notes: \"\", newStatus: \"\" }); const listsContractId = ListsSDK.getContractId(); const userIsRegistryAdmin = ListsSDK.isRegistryAdmin(context.accountId); const registration = ListsSDK.getRegistration(null, projectId); const handleUpdateStatus = () => { Near.call([{ contractName: listsContractId, methodName: \"update_registration\", args: { registration_id: registration.id, status: statusReview.newStatus, notes: statusReview.notes }, deposit: NEAR.toIndivisible(0.01).toString() }]); }; const SelectedNavComponent = useMemo(() => { return props.navOptions.find((option) => option.id == props.nav).source; }, []); return <A_167> <BannerHeader showFollowers accountId={projectId || accountId} projectId={projectId} registration={registration} /> <A_168> <Widget loading=\" \" code={props.alem.componentsCode.BodyHeader} props={{ ...{ accountId: accountId, projectId: projectId, profile: props.profile, ...props } }} /> {userIsRegistryAdmin && projectId && <Select {...{ noLabel: true, options: PROJECT_STATUSES.map((status) => ({ value: status, text: status })), value: { text: props.registration.status, value: props.registration.status }, onChange: (status) => { if (status.value != registration.status) { setStatusReview({ ...statusReview, newStatus: status.value, modalOpen: true }); } }, containerStyles: { padding: \"16px 24px\" } }} />} <A_160 navOptions={props.navOptions} nav={props.nav} /> <Details> {SelectedNavComponent && <SelectedNavComponent accountId={accountId} projectId={projectId} accounts={[projectId || accountId]} donations={props.donations} {...props} />} </Details> </A_168> {statusReview.modalOpen && <ModalOverlay onOverlayClick={() => setStatusReview({ ...statusReview, modalOpen: false })}> <> <A_169>Enter Notes for changing status to {statusReview.newStatus}</A_169> <TextArea {...{ noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Your notes here...\", value: statusReview.notes, onChange: (notes) => setStatusReview({ ...statusReview, notes }), validate: () => {} }} /> <A_170 style={{ justifyContent: \"flex-end\", marginTop: \"12px\" }}> <Button type=\"primary\" text=\"Submit\" onClick={handleUpdateStatus} /> </A_170> </> </ModalOverlay>} </A_167>; `, ProjectBanner: ` const A_171 = { Graylisted: { background: \"#C7C7C7\", text: \"GRAYLISTED: needs further review, unsure if project qualifies on public goods\", textColor: \"#525252\", toggleColor: \"#FFFFFF\" }, Rejected: { background: \"#DD3345\", text: \"REJECTED: this project was denied on public goods registry\", textColor: \"#FEF6EE\", toggleColor: \"#C7C7C7\" }, Pending: { background: \"#F0CF1F\", text: \"PENDING: this project has yet to be approved\", textColor: \"#292929\", toggleColor: \"#7B7B7B\" }, Blacklisted: { background: \"#292929\", text: \"BLACKLISTED: this project has been removed for violating terms\", textColor: \"#F6F5F3\", toggleColor: \"#C7C7C7\" }, Unregistered: { background: \"#DD3345\", text: \"UNREGISTERED: This account has not registered as a public good\", textColor: \"#F6F5F3\", toggleColor: \"#C7C7C7\" }}; const A_172 = styled.div\\` width: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 12px; backdrop-filter: blur(150px);\\`;const A_173 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const BannerText = styled.div\\` text-align: center; font-size: 22px; font-weight: 600; letter-spacing: 0.015em; text-transform: uppercase; @media screen and (max-width: 768px) { font-size: 12px; }\\`;const Toggle = styled.span\\` align-items: center; gap: 0.5rem; font-weight: 600; font-size: 22px; white-space: nowrap; margin-left: 0.5rem; svg { width: 12px; transition: all 300ms ease-in-out; } &.active svg { rotate: 180deg; } @media screen and (max-width: 768px) { font-size: 12px; svg { width: 8px; } }\\`;const Notes = styled.div\\` overflow: hidden; transition: all 300ms ease-in-out; font-size: 12px; font-style: italic; max-height: 0; text-transform: uppercase; max-width: 1270px; &.active { max-height: 80px; margin-top: 12px; }\\`; const {registration: registration} = props; const [toggle, setToggle] = useState(false); const registrationStatus = registration ? A_171[registration.status] : A_171.Unregistered; return <A_172 style={{ background: registrationStatus.background }}> <A_173> <BannerText onClick={() => registration.admin_notes ? setToggle(!toggle) : \"\"} style={{ color: registrationStatus.textColor, cursor: registration.admin_notes ? \"pointer\" : \"default\" }}> {registrationStatus.text} {registration.admin_notes && <Toggle className={\\`\\${toggle ? \"active\" : \"\"}\\`} style={{ color: registrationStatus.toggleColor }}> (See {toggle ? \"Less\" : \"Why\"}) <svg className={\\`\\${toggle ? \"active\" : \"\"}\\`} viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.59 0.294922L6 4.87492L1.41 0.294922L0 1.70492L6 7.70492L12 1.70492L10.59 0.294922Z\" fill=\"#C7C7C7\" style={{ fill: registrationStatus.toggleColor, stroke: registrationStatus.toggleColor }} /> </svg> </Toggle>} </BannerText> </A_173> {registration.admin_notes && <Notes className={\\`\\${toggle ? \"active\" : \"\"}\\`} style={{ color: registrationStatus.toggleColor }}> Admin notes: {registration.admin_notes} </Notes>} </A_172>; `, ProjectPage: ` const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_152 = styled.div\\` display: flex; justify-content: space-between; align-items: center; padding: 68px 105px; border-radius: 12px; background: #f6f5f3; .text { font-family: \"Lora\"; max-width: 290px; font-size: 22px; font-style: italic; font-weight: 500; color: #292929; } img { width: 60%; } @media screen and (max-width: 768px) { flex-direction: column-reverse; padding: 24px 16px; .text { font-size: 16px; } img { width: 100%; } }\\`; const MergedIndexFeed = (compProps) => { if (!compProps.index) { return \"props.index is not defined\"; } const indices = JSON.parse(JSON.stringify(Array.isArray(compProps.index) ? compProps.index : [compProps.index])); const filter = compProps.filter; const renderItem = compProps.renderItem ?? ((item) => <div key={JSON.stringify(item)}> #{item.blockHeight}: {JSON.stringify(item)} </div>); const cachedRenderItem = (item, i) => { const key = JSON.stringify(item); if (!(key in state.cachedItems)) { state.cachedItems[key] = renderItem(item, i); } return state.cachedItems[key]; }; const initialRenderLimit = compProps.initialRenderLimit ?? 10; const addDisplayCount = compProps.nextLimit ?? initialRenderLimit; const reverse = !!compProps.reverse; const computeFetchFrom = (items, limit, desc) => { if (!items || items.length < limit) { return false; } const blockHeight = items[items.length - 1].blockHeight; return desc ? blockHeight - 1 : blockHeight + 1; }; const mergeItems = (iIndex, oldItems, newItems, desc) => { const index = indices[iIndex]; const items = [...new Set([...newItems.map((item) => ({ ...item, action: index.action, key: index.key, index: iIndex })), ...oldItems].map((i) => JSON.stringify(i)))].map((i) => JSON.parse(i)); items.sort((a, b) => a.blockHeight - b.blockHeight); if (desc) { items.reverse(); } return items; }; const jIndices = JSON.stringify(indices); if (jIndices !== state.jIndices) { State.update({ jIndices, feeds: indices.map(() => ({})), items: [], displayCount: initialRenderLimit, cachedItems: {} }); } let stateChanged = false; for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; let feedChanged = false; index.options = index.options || {}; index.options.limit = Math.min(Math.max(initialRenderLimit + addDisplayCount * 2, index.options.limit), 100); const desc = index.options.order === \"desc\"; const initialItems = Social.index(index.action, index.key, index.options, index.cacheOptions); if (initialItems === null) { continue; } const jInitialItems = JSON.stringify(initialItems); const nextFetchFrom = computeFetchFrom(initialItems, index.options.limit, desc); if (feed.jInitialItems !== jInitialItems) { feed.jInitialItems = jInitialItems; feedChanged = true; if (nextFetchFrom !== feed.initialNextFetchFrom) { feed.fetchFrom = false; feed.items = mergeItems(iIndex, [], initialItems, desc); feed.initialNextFetchFrom = nextFetchFrom; feed.nextFetchFrom = nextFetchFrom; } else { feed.items = mergeItems(iIndex, feed.items, initialItems, desc); } } feed.usedCount = 0; if (feedChanged) { state.feeds[iIndex] = feed; stateChanged = true; } } const filteredItems = []; while (filteredItems.length < state.displayCount) { let bestItem = null; for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; const desc = index.options.order === \"desc\"; if (!feed.items) { continue; } const item = feed.items[feed.usedCount]; if (!item) { continue; } if (bestItem === null || (desc ? item.blockHeight > bestItem.blockHeight : item.blockHeight < bestItem.blockHeight)) { bestItem = item; } } if (!bestItem) { break; } state.feeds[bestItem.index].usedCount++; if (filter) { if (filter.ignore) { if (bestItem.accountId in filter.ignore) { continue; } } if (filter.require) { if (!(bestItem.accountId in filter.require)) { continue; } } } filteredItems.push(bestItem); } for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; const desc = index.options.order === \"desc\"; let feedChanged = false; if ((feed.items.length || 0) - feed.usedCount < addDisplayCount * 2 && !feed.fetchFrom && feed.nextFetchFrom && feed.nextFetchFrom !== feed.fetchFrom) { feed.fetchFrom = feed.nextFetchFrom; feedChanged = true; } if (feed.fetchFrom) { const limit = addDisplayCount; const newItems = Social.index(index.action, index.key, Object.assign({}, index.options, { from: feed.fetchFrom, subscribe: undefined, limit })); if (newItems !== null) { feed.items = mergeItems(iIndex, feed.items, newItems, desc); feed.fetchFrom = false; feed.nextFetchFrom = computeFetchFrom(newItems, limit, desc); feedChanged = true; } } if (feedChanged) { state.feeds[iIndex] = feed; stateChanged = true; } } if (stateChanged) { State.update(); } const makeMoreItems = () => { State.update({ displayCount: state.displayCount + addDisplayCount }); }; const loader = <div className=\"loader\" key={\"loader\"}> <span className=\"spinner-grow spinner-grow-sm me-1\" role=\"status\" aria-hidden=\"true\" /> Loading ... </div>; const fetchMore = compProps.manual && (state.feeds.some((f) => !!f.fetchFrom) && filteredItems.length < state.displayCount ? loader : state.displayCount < filteredItems.length && <div key={\"loader more\"}> <a href=\"javascript:void\" onClick={(e) => makeMoreItems()}> {compProps.loadMoreText ?? \"Load more...\"} </a> </div>); const items = filteredItems ? filteredItems.slice(0, state.displayCount) : []; if (reverse) { items.reverse(); } const renderedItems = items.length === 0 ? <A_152> <div className=\"text\">This {compProps.tab === \"profile\" ? \"user\" : \"project\"} has not posted yet.</div> <img src=\"https://ipfs.near.social/ipfs/bafkreicwz5cuku3kxxp3wzldslpfzmfqgukpm2wwy7t7zuevkdp4gbl2uq\" alt=\"pots\" /> </A_152> : items.map(cachedRenderItem); return compProps.manual ? <> {reverse && fetchMore} {renderedItems} {!reverse && fetchMore} </> : <InfiniteScroll pageStart={0} loadMore={makeMoreItems} threshold={compProps.threshold ?? 250} hasMore={state.displayCount <= filteredItems.length} loader={loader}> {renderedItems} </InfiniteScroll>;}; const Post = postProps => { return <Widget loading={<div className=\"w-100\" style={{ height: \"200px\" }} />} src=\"mob.near/widget/MainPage.N.Post\" props={postProps} />;}; const Feed = (compProps) => { const post = compProps.post === undefined ?? true; const hashtags = compProps.hashtags || []; const indexKey = compProps.indexKey ?? \"main\"; const index = [{ action: \"post\", key: indexKey, options: { limit: 10, order: \"desc\", accountId: compProps.accounts }, cacheOptions: { ignoreCache: true } }, { action: \"repost\", key: indexKey, options: { limit: 10, order: \"desc\", accountId: compProps.accounts }, cacheOptions: { ignoreCache: true } }]; const isPremiumFeed = compProps.isPremiumFeed; const commentAccounts = compProps.commentAccounts; const renderedPosts = {}; const makePostItem = (a) => ({ type: \"social\", path: \\`\\${a.accountId}/post/main\\`, blockHeight: a.blockHeight }); const renderPost = (a) => { if (a.value.type !== \"md\") { return false; } const item = JSON.stringify(makePostItem(a)); if (item in renderedPosts) { return false; } renderedPosts[item] = true; const postProps = { accountId: a.accountId, blockHeight: a.blockHeight, isPremiumFeed, commentAccounts, indexKey, groupId: compProps.groupId, permissions: compProps.permissions }; return <div key={JSON.stringify(a)}> <Post {...postProps} /> </div>; }; const repostSvg = <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 2 24 24\" stroke=\"currentColor\" strokeWidth=\"1\"> <path fill-rule=\"evenodd\" d=\"M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z\" transform=\"rotate(180, 12, 12), translate(0, 4)\" /> <path fill-rule=\"evenodd\" d=\"M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z\" transform=\"translate(0, 4)\" /> </svg>; const extractParentPost = (item) => { if (!item || item.type !== \"social\" || !item.path || !item.blockHeight) { return undefined; } const accountId = item.path.split(\"/\")[0]; const parentPost = { accountId, blockHeight: item.blockHeight }; return \\`\\${accountId}/post/main\\` === item.path ? parentPost : undefined; }; const renderRepost = (a) => { if (a.value.type !== \"repost\") { return false; } const post = extractParentPost(a.value.item); if (!post) { return false; } const item = JSON.stringify(makePostItem(post)); if (item in renderedPosts) { return false; } renderedPosts[item] = true; const profileLineProps = { accountId: a.accountId, hideImage: true, hideAccountId: true, tooltip: true }; const postProps = { accountId: post.accountId, blockHeight: post.blockHeight, reposted: true, isPremiumFeed, commentAccounts, indexKey, groupId: compProps.groupId, permissions: compProps.permissions }; const postLoading = <div className=\"w-100\" style={{ height: \"200px\" }} />; return <div key={JSON.stringify(a)}> <div className=\"text-muted\" style={{ fontSize: \"13px\", fontWeight: 700, marginLeft: \"24px\", marginBottom: \"-24px\", paddingTop: \"4px\", position: \"relative\", zIndex: 1 }}> {repostSvg}{\" \"} <span style={{ marginLeft: \"8px\" }}> Reposted by <Widget loading={a.accountId} src=\"mob.near/widget/N.ProfileLine\" /> </span> </div> <Widget loading={postLoading} src=\"mob.near/widget/MainPage.N.Post\" props={postProps} /> </div>; }; const renderItem = (item) => { return item.action === \"post\" ? renderPost(item) : renderRepost(item); }; const Container = styled.div\\` display: flex; flex-direction: column; .post-btn { background: rgb(46, 46, 46); border-radius: 6px; padding: 12px 16px; border: none; color: white; } \\`; return <Container> {post && <Widget loading=\" \" code={props.alem.componentsCode.Compose} props={{ ...{ ...props } }} />} <MergedIndexFeed index={index} renderItem={renderItem} filter={compProps.filter} threshold={800} /> </Container>; }; const A_158 = styled.div\\` display: flex; flex-direction: column; .tab-content { padding: 1rem 0; }\\`;const Nav = styled.div\\` .nav-pills { background: #fbfbfb; font-weight: 500; --bs-nav-pills-border-radius: 0; --bs-nav-link-color: #000; --bs-nav-pills-link-active-color: #000; --bs-nav-pills-link-active-bg: #fbfbfb; --bs-nav-link-padding-y: 0.75rem; border-bottom: 1px solid #eee; padding-top: 3px; } .nav-link.active { border-bottom: 3px solid #dd3345; } .nav-item:not(:has(> .disabled)):hover { background: #dd334456; .nav-link { color: #dd3345; } } margin: 0 -12px;\\`; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const FollowTabs = (__props__) => { const { accountId, projectId, nav } = __props__; const profileLink = hrefWithParams(\\`?tab=profile&accountId=\\${accountId}\\`); return <A_158> <Nav> <ul className=\"nav nav-pills nav-fill\" role=\"tablist\"> <li className=\"nav-item\" role=\"presentation\"> <a href={\\`\\${profileLink}&nav=followers\\`} className={\\`btn nav-link \\${nav === \"followers\" ? \"active\" : \"\"}\\`} role=\"tab\"> Followers </a> </li> <li className=\"nav-item\" role=\"presentation\"> <a href={\\`\\${profileLink}&nav=following\\`} className={\\`btn nav-link \\${nav === \"following\" ? \"active\" : \"\"}\\`} role=\"tab\"> Following </a> </li> </ul> </Nav> <div className=\"tab-content\"> <div className=\"tab-pane fade in show active\" role=\"tabpanel\"> <Widget loading=\" \" code={props.alem.componentsCode.FollowersList} props={{ ...{ accountId: projectId || accountId, nav: nav, ...props } }} /> </div> </div> </A_158>; }; const ProjectOptions = (projectId) => [{ label: \"Home\", id: \"home\", disabled: false, source: (componentProps) => <Widget loading=\" \" code={props.alem.componentsCode.About} props={{ ...{ ...componentProps, ...props } }} />, href: hrefWithParams(\\`?tab=project&projectId=\\${projectId}&nav=home\\`) }, { label: \"Social Feed\", id: \"feed\", disabled: false, source: (componentProps) => <Feed {...componentProps} />, href: hrefWithParams(\\`?tab=project&projectId=\\${projectId}&nav=feed\\`) }, { label: \"Pots\", id: \"pots\", disabled: false, source: (componentProps) => <Widget loading=\" \" code={props.alem.componentsCode.Pots} props={{ ...{ ...componentProps, ...props } }} />, href: hrefWithParams(\\`?tab=project&projectId=\\${projectId}&nav=pots\\`) }, { label: \"Funding Raised\", id: \"funding\", disabled: false, source: (componentProps) => <Widget loading=\" \" code={props.alem.componentsCode.FundingRaised} props={{ ...{ ...componentProps, ...props } }} />, href: hrefWithParams(\\`?tab=project&projectId=\\${projectId}&nav=funding\\`) }, { label: \"\", id: \"followers\", disabled: false, source: FollowTabs }, { label: \"\", id: \"following\", disabled: false, source: FollowTabs }]; const loadingSkeleton = styled.keyframes\\` 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; }\\`;const SkeletonContainer = styled.div\\` display: flex; flex-direction: column; position: relative; width: 100%; animation-name: \\${loadingSkeleton}; animation-duration: 1s; animation-iteration-count: infinite;\\`;const LoadingBackground = styled.div\\` position: relative; background: #eee; width: 100%; height: 318px; @media screen and (max-width: 768px) { height: 264px; }\\`;const LoadingProfileImg = styled.div\\` width: \\${__props__ => __props__.imageStyle?.width ?? \"128px\"}; height: \\${__props__ => __props__.imageStyle?.height ?? \"128px\"}; z-index: 1; padding: 6px; transform: translateY(-50%); position: relative; margin-left: 4rem; background: white; border-radius: 50%; @media screen and (max-width: 768px) { margin-left: 1rem; } div { background: #eee; width: 100%; height: 100%; border-radius: 50%; }\\`;const BannerSkeleton = () => <SkeletonContainer> <LoadingBackground /> <LoadingProfileImg> <div /> </LoadingProfileImg> </SkeletonContainer>; const A_174 = styled.div\\` /* margin-top: calc(-1 * var(--body-top-padding, 0)); */\\`; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const getProjectByProjectId = projectId => { return ListsSDK.getRegistration(null, projectId);}; const { projectId } = props.alem.useParams(); const accountId = context.accountId; const project = getProjectByProjectId(projectId); const pots = PotFactorySDK.getPots(); const registration = ListsSDK.getRegistration(null, projectId); const account = fetch(\"https://api3.nearblocks.io/v1/account/\" + projectId); if (registration === null || account === null) return <BannerSkeleton />; const isObjectNotEmpty = (obj) => Object.keys(obj).length > 0; const addressExist = account?.body?.account[0]; if (!isObjectNotEmpty(addressExist || {}) && !registration) return <div style={{ marginTop: \"1rem\", fontSize: \"1.5rem\" }}>Account does not exist.</div>; const [directDonations, setDirectDonations] = useState(null); const [matchingRoundDonations, setMatchingRoundDonations] = useState({}); const [potPayouts, setPotPayouts] = useState({}); const getProjectRoundDonations = (potId, potDetail) => { return PotSDK.asyncGetDonationsForProject(potId, projectId).then((donations) => { const updatedDonations = donations.map((donation) => ({ ...donation, base_currency: potDetail.base_currency, pot_name: potDetail.pot_name, pot_id: potId, type: \"matched\" })); setMatchingRoundDonations((prevmMatchingRoundDonations) => { return { ...prevmMatchingRoundDonations, [potId]: updatedDonations }; }); }).catch(() => { setMatchingRoundDonations((prevmMatchingRoundDonations) => { return { ...prevmMatchingRoundDonations, [potId]: [] }; }); }); }; let donationsForRecipient = DonateSDK.getDonationsForRecipient(projectId); if (donationsForRecipient && !directDonations) { donationsForRecipient = donationsForRecipient.map((donation) => ({ ...donation, type: \"direct\" })); setDirectDonations(donationsForRecipient); } if (pots && !matchingRoundDonations[pots[pots.length - 1].id]) { pots.forEach((pot) => { PotSDK.asyncGetConfig(pot.id).then((potDetail) => { const payout = potDetail.payouts.filter((pay) => projectId === pay.project_id)[0]; if (payout.paid_at) setPotPayouts((prevPayout) => ({ ...prevPayout, [pot.id]: { ...payout, pot_id: pot.id, pot_name: potDetail.pot_name, base_currency: potDetail.base_currency, type: \"payout\" } })); getProjectRoundDonations(pot.id, potDetail); }); }); } const allDonations = useMemo(() => { const RoundDonationsValue = Object.values(matchingRoundDonations).flat(); let payouts = Object.values(potPayouts); const allDonations = [...(directDonations || []), ...RoundDonationsValue, ...payouts]; allDonations.sort((a, b) => { const b_donated_at = b.donated_at_ms || b.donated_at || b.paid_at; const a_donated_at = a.donated_at_ms || a.donated_at || a.paid_at; return b_donated_at - a_donated_at; }); return allDonations; }, [matchingRoundDonations, directDonations, potPayouts]); const profile = Social.getr(\\`\\${projectId}/profile\\`); if (project === null) return <BannerSkeleton />; if (project == undefined) { return <div style={{ marginTop: \"1rem\", fontSize: \"1.5rem\" }}>Project not found</div>; } const { nav } = props.alem.useParams(); return <A_174> {registration.status !== \"Approved\" && <Widget loading=\" \" code={props.alem.componentsCode.ProjectBanner} props={{ ...{ registration: registration, ...props } }} />} <Widget loading=\" \" code={props.alem.componentsCode.Body} props={{ ...{ ...{ project, profile, registration, projectId, post: projectId === accountId, nav: nav ?? props.nav ?? \"home\", donations: allDonations, directDonations: directDonations, matchingRoundDonations: Object.values(matchingRoundDonations).flat(), potPayouts: Object.values(potPayouts), navOptions: ProjectOptions(projectId) }, ...props } }} /> </A_174>; `, DonationStats: ` const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const A_175 = styled.div\\` display: flex; flex-direction: row; align-items: center; gap: 1.5rem; margin-top: 1rem; padding: 0 40px; @media screen and (max-width: 768px) { gap: 1rem; padding: 0 8px; }\\`;const StatsTitle = styled.div\\` display: flex; flex-direction: row; align-items: baseline; gap: 8px; font-weight: 600; font-size: 20px; color: #dd3345;\\`;const StatsSubTitle = styled.div\\` color: #656565; font-size: 14px; font-weight: 400;\\`; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const [donated, setDonated] = useState(null); const [donations, setDonations] = useState(null); const data = DonateSDK.getConfig(); useEffect(() => { if (!donated) { const lastDonationAmount = data.net_donations_amount ? yoctosToUsdWithFallback(data.net_donations_amount, true) : null; const totalDonations = data.total_donations_count; setDonated(lastDonationAmount); setDonations(totalDonations); } }, [data, donated]); return <A_175> <StatsTitle> {donated || \"-\"} <StatsSubTitle>Donated</StatsSubTitle> </StatsTitle> <StatsTitle> {donations || \"-\"} <StatsSubTitle>Donations</StatsSubTitle> </StatsTitle> </A_175>; `, NewHero: ` const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useDonationModal = () => useContext(\"donation-modal\"); const svgContent = \\`<svg width=\"1320\" height=\"332\" viewBox=\"0 0 1320 332\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"1320\" height=\"1\" fill=\"#FEF6EE\"/><mask id=\"mask0_11854_22101\" style=\"mask-type:alpha\" maskUnits=\"userSpaceOnUse\" x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\"><rect x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\" fill=\"url(#paint0_radial_11854_22101)\"/></mask><g mask=\"url(#mask0_11854_22101)\"><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" fill=\"#FEF6EE\"/><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" fill=\"#FEF6EE\"/><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" fill=\"#F8D3B0\"/><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" fill=\"#F8D3B0\"/><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" fill=\"#F8D3B0\"/><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" fill=\"#F8D3B0\"/><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" fill=\"#F8D3B0\"/><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" fill=\"#F8D3B0\"/><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" fill=\"#F8D3B0\"/><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" fill=\"#F8D3B0\"/><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" fill=\"#FEF6EE\"/><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" fill=\"#FEF6EE\"/><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" fill=\"#FEF6EE\"/><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" fill=\"#F8D3B0\"/><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" fill=\"#F8D3B0\"/><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" fill=\"#F8D3B0\"/><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" fill=\"#F8D3B0\"/><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" fill=\"#F8D3B0\"/><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" fill=\"#F8D3B0\"/><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" fill=\"#F8D3B0\"/><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" fill=\"#F8D3B0\"/><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" stroke=\"#F4B37D\"/></g></g><defs><radialGradient id=\"paint0_radial_11854_22101\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(660 -158.5) rotate(90) scale(724.5 1018.83)\"><stop stop-color=\"#FCE9D5\"/><stop offset=\"0.855072\" stop-color=\"#FEF6EE\" stop-opacity=\"0\"/></radialGradient></defs></svg>\\`;const HomeBannerStyle = { backgroundImage: \\`url(\"data:image/svg+xml;charset=utf-8,\\${encodeURIComponent(svgContent)}\")\\`, backgroundSize: \"cover\", backgroundRepeat: \"no-repeat\", backgroundColor: \"#FEF6EE\"}; const A_176 = styled.div\\` display: flex; flex-direction: column;\\`;const HeroContainer = styled.div\\` display: flex; flex-direction: column; position: relative; width: 100%; justify-content: center; border: 1px solid #f8d3b0; border-radius: 12px; overflow: hidden; .background { position: absolute; pointer-events: none; left: 0px; width: 100%; top: 0px; min-height: 600px; } .content { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: center; padding: 64px 40px; } .sub-title { color: #dd3345; font-weight: 600; font-size: 16px; margin-top: 0; margin-bottom: 12px; } .title { letter-spacing: -0.4px; font-weight: 500; font-size: 40px; font-family: \"Lora\"; margin: 0; } .btns { display: flex; align-items: center; gap: 2rem; margin-top: 40px; font-size: 14px; a, button { padding: 12px 16px; display: flex; align-items: center; justify-content: center; font-weight: 500; border-radius: 6px; box-shadow: 0px 0px 0px 1px #292929, 0px -2px 0px 0px #292929 inset; border: none; text-decoration: none; color: #292929; transition: all 300ms; &:hover { background: #292929; color: white; } } button { color: white; background: #dd3345; &:hover { } } } @media only screen and (max-width: 768px) { .content { padding: 48px 20px; } .title { font-size: 36px; } .btns { flex-direction: column; gap: 1rem; margin-top: 24px; } .line-break { display: none; } } @media only screen and (max-width: 480px) { .btns a, button { width: 100%; padding: 12px 0; } }\\`;const A_177 = styled.div\\` width: 100%; height: 1px; background: #ebebeb; margin-top: 1rem;\\`; const { projectsData: projectsData } = props; const { allProjects, approvedProjects } = projectsData; const { setDonationModalProps } = useDonationModal(); const getRandomProject = () => { if (approvedProjects) { const randomIndex = Math.floor(Math.random() * approvedProjects.length); return approvedProjects[randomIndex]?.registrant_id; } }; const openDonateRandomlyModal = () => { setDonationModalProps({ projectId: getRandomProject() }); }; const accountId = context.accountId; const isRegisteredProject = useMemo(() => { if (allProjects) { return allProjects.find((registration) => registration.registrant_id === accountId); } }, [allProjects]); return <A_176> <HeroContainer style={{ ...HomeBannerStyle }}> <div className=\"content\"> <h3 className=\"sub-title\">Transforming Funding for Public Goods</h3> <h1 className=\"title\"> Discover impact projects, donate directly, & <br className=\"line-break\" /> participate in funding rounds. </h1> <div className=\"btns\"> <button onClick={openDonateRandomlyModal} className=\"donate-btn\"> Donate Randomly </button> <a href={isRegisteredProject ? \\`?tab=project&projectId=\\${accountId}\\` : \"?tab=createproject\"}> {isRegisteredProject ? \"View Your Project\" : \"Register Your Project\"} </a> </div> </div> </HeroContainer> <Widget loading=\" \" code={props.alem.componentsCode.DonationStats} props={{ ...{ ...props } }} /> <A_177 /> </A_176>; `, FilterDropdown: ` const DropdownRight = __props__ => <svg {...__props__} viewBox=\"0 0 18 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 12H6V10H0V12ZM0 0V2H18V0H0ZM0 7H12V5H0V7Z\" fill=\"#7B7B7B\" /> </svg>; const A_179 = styled.div\\` display: flex; position: relative; flex-direction: column; align-items: flex-end; font-size: 14px;\\`;const A_180 = styled.div\\` font-weight: 500; display: flex; align-items: center; gap: 0.5rem; cursor: pointer; width: fit-content; padding: 0.5rem 1rem; border-radius: 6px; border: 1px solid #7b7b7b; svg { width: 18px; } &.active { color: #fff; background: #292929; }\\`;const Menu = styled.div\\` position: absolute; top: calc(100% + 0.5rem); right: 0; transition: all 300ms ease-in-out; display: flex; flex-wrap: wrap; gap: 8px; border-radius: 8px; padding: 1rem; background: #fff; width: 500px; box-shadow: 0px 0px 0px 1px rgba(123, 123, 123, 0.09), 0px 3px 3px -1px rgba(123, 123, 123, 0.16), 0px 9px 9px -3px rgba(123, 123, 123, 0.1), 0px 17px 14px -5px rgba(123, 123, 123, 0.08); opacity: 0; visibility: hidden; transform: translateY(100px); z-index: 1; &.active { opacity: 1; visibility: visible; transform: translateY(0); } .title { width: 100%; &:not(:first-of-type) { margin-top: 2rem; } } .option { display: flex; align-items: center; gap: 10px; border-radius: 8px; border: 1px solid #dbdbdb; padding: 8px 12px; cursor: pointer; transition: all 300ms ease-in-out; svg { display: none; width: 14px; } :hover { border: 1px solid #f4b37d; background: #fef6ee; color: #ea6a25; } &.selected { border: 1px solid #f4b37d; background: #fef6ee; color: #ea6a25; svg { display: block; } } } @media only screen and (max-width: 768px) { width: 200px !important; left: 0; right: auto; }\\`;const A_181 = styled.div\\` font-weight: 600; font-size: 12px; display: flex; line-height: 1; width: 18px; height: 18px; align-items: center; justify-content: center; border-radius: 50%; background: #ebebeb; &.active { background: #464646; color: #f6f5f3; }\\`;const Screen = styled.div\\` position: fixed; width: 100%; height: 100%; left: 0; top: 0;\\`; const DropdownCenter = __props__ => <svg {...__props__} viewBox=\"0 0 18 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7 12H11V10H7V12ZM0 0V2H18V0H0ZM3 7H15V5H3V7Z\" fill=\"#7B7B7B\" /> </svg>; const props = props; const { onClick, menuClass, label, multipleOptions, defaultSelected } = props; const labelIcon = props.labelIcon ?? \"center\"; const options = props.options ?? {}; const [toggleMenu, setToggleMenu] = useState(false); const [selected, setSelected] = useState(defaultSelected || {}); const icons = { center: <DropdownCenter />, right: <DropdownRight /> }; function findIndexWithAll(listOfLists, target) { for (let i = 0; i < listOfLists.length; i++) { const indexInList = listOfLists[i].indexOf(target); if (indexInList !== -1) { return { listIndex: i, itemIndex: indexInList }; } } return { listIndex: -1, itemIndex: -1 }; } const handleSelect = ({ val, type, label }) => { let selectedUpdated = { ...selected }; const selectedList = selected[type] || []; if (!multipleOptions) { selectedUpdated = { val, label }; } else if (selectedList.includes(val)) { selectedUpdated[type] = selectedList.filter(item => item !== val); } else { selectedUpdated[type] = [...selectedList, val]; } const { listIndex, itemIndex } = findIndexWithAll(Object.values(selectedUpdated), \"all\"); const types = Object.keys(selectedUpdated); if (val === \"all\") { selectedUpdated = { [type]: [val] }; } else if (listIndex !== -1) { selectedUpdated[types[listIndex]].splice(itemIndex, 1); } setSelected(selectedUpdated); onClick(selectedUpdated); setToggleMenu(false); }; const count = Object.values(selected).reduce((total, list) => total + list.length, 0); return <A_179> {toggleMenu && <Screen onClick={() => setToggleMenu(false)} />} <A_180 className={toggleMenu ? \"active\" : \"\"} onClick={() => setToggleMenu(!toggleMenu)}> {label || \"Filter\"} {multipleOptions && <A_181 className={toggleMenu ? \"active\" : \"\"}>{count}</A_181>} <div>{icons[labelIcon]}</div> </A_180> <Menu className={\\`\\${toggleMenu ? \"active\" : \"\"} \\${menuClass ?? \"\"}\\`}> {Object.keys(options)?.map(menuLabel => <> <div className=\"title\">Filter by {menuLabel.includes(\"no label\") ? \"\" : menuLabel}</div> {(options[menuLabel] || [])?.map(({ label, val }) => <div className={\\`option \\${multipleOptions && (selected[menuLabel] || [])?.includes(val) && \"selected\"}\\`} key={val} onClick={() => handleSelect({ label, val, type: menuLabel })}> <svg viewBox=\"0 0 14 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M4.59625 8.90631L1.46875 5.77881L0.403748 6.83631L4.59625 11.0288L13.5962 2.02881L12.5387 0.971313L4.59625 8.90631Z\" fill=\"#F4B37D\" /> </svg> {label} </div>)} </>)} </Menu> </A_179>; `, A_184: ` const A_182 = styled.div\\` display: flex; position: relative; flex-direction: row; align-items: center; flex: 1; border-radius: 6px; border: 1px solid #7b7b7b; padding: 0.5rem; padding-left: 2.5rem; font-size: 14px;\\`;const A_183 = styled.div\\` display: flex; position: absolute; left: 14px; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; pointer-events: none; svg { height: 100%; }\\`;const SearchBarInput = styled.input\\` background: none; width: 100%; outline: none; border: none; &:focus { outline: none; border: none; }\\`;const FilterButton = styled.div\\` font-weight: 500; font-size: 14px; display: flex; align-items: center; gap: 0.5rem; cursor: pointer; width: fit-content; padding: 0.54rem 1rem; border-radius: 6px; border: 1px solid #7b7b7b; transition: all 200ms ease-in-out; &.active { color: #fff; background: #292929; }\\`;const FilterIcon = styled.div\\` display: flex; width: 16px; height: 16px; align-items: center; justify-content: center;\\`;const FilterMenu = styled.div\\` position: absolute; background: #fff; font-size: 14px; top: 110%; right: 0; padding: 8px; display: flex; flex-direction: column; gap: 8px; border-radius: 6px; border: 1px solid rgba(41, 41, 41, 0.36); box-shadow: 0px 12px 20px -4px rgba(123, 123, 123, 0.32), 0px 4px 8px -3px rgba(123, 123, 123, 0.2), 0px 0px 2px 0px rgba(123, 123, 123, 0.36); z-index: 2; opacity: 0; visibility: hidden; transform: translateY(100px); transition: all 200ms ease-in-out; &.active { opacity: 1; visibility: visible; transform: translateY(0); } @media screen and (max-width: 768px) { left: 0; background: #fff; }\\`;const FilterItem = styled.div\\` cursor: pointer; padding: 8px; display: flex; gap: 12px; white-space: nowrap; &:hover { background: #292929; color: #fff; border-radius: 6px; }\\`; const props = props; const { title, numItems, itemName, sortList, sortVal, handleSortChange, setSearchTerm, FilterMenuClass } = props; const onSearchChange = event => { setSearchTerm(event.target.value); }; const [openFilter, setOpenFilter] = useState(false); return <> <A_182> <A_183> <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M12.7549 11.2559H11.9649L11.6849 10.9859C12.6649 9.8459 13.2549 8.3659 13.2549 6.75586C13.2549 3.16586 10.3449 0.255859 6.75488 0.255859C3.16488 0.255859 0.254883 3.16586 0.254883 6.75586C0.254883 10.3459 3.16488 13.2559 6.75488 13.2559C8.3649 13.2559 9.8449 12.6659 10.9849 11.6859L11.2549 11.9659V12.7559L16.2549 17.7459L17.7449 16.2559L12.7549 11.2559ZM6.75488 11.2559C4.26488 11.2559 2.25488 9.2459 2.25488 6.75586C2.25488 4.26586 4.26488 2.25586 6.75488 2.25586C9.2449 2.25586 11.2549 4.26586 11.2549 6.75586C11.2549 9.2459 9.2449 11.2559 6.75488 11.2559Z\" fill=\"#7B7B7B\" /> </svg> </A_183> <SearchBarInput placeholder={\\`Search (\\${numItems}) \\${numItems === 1 ? itemName : itemName + \"s\"}\\`} onChange={onSearchChange} type=\"text\" /> </A_182> <div style={{ position: \"relative\" }} onClick={() => setOpenFilter(!openFilter)}> <FilterButton className={openFilter ? \"active\" : \"\"}> {sortVal || title} <FilterIcon> <svg width=\"18\" height=\"12\" viewBox=\"0 0 18 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 12H6V10H0V12ZM0 0V2H18V0H0ZM0 7H12V5H0V7Z\" fill=\"#7B7B7B\" /> </svg> </FilterIcon> </FilterButton> <FilterMenu onClick={e => e.stopPropagation()} className={\\`\\${FilterMenuClass || \"\"} \\${openFilter ? \"active\" : \"\"} \\`}> {(sortList || []).map(filter => <FilterItem key={filter} onClick={() => { setOpenFilter(false); handleSortChange(filter); }}> {filter} </FilterItem>)} </FilterMenu> </div> </>; `, ListSection: ` const {shouldShuffle: shouldShuffle, items: items, renderItem: renderItem} = props; const responsive = props.responsive || []; const { Feed } = VM.require(\"devs.near/widget/Feed\"); if (!Feed) { return <p>Loading...</p>; } const _items = useMemo(() => { if (shouldShuffle) { return [...items].sort(() => Math.random() - 0.5); } return items; }, [items, shouldShuffle]); const PAGE_SIZE = 9; const Grid = styled.div\\` display: grid; width: 100%; padding-top: 20px; padding-bottom: 32px; gap: 31px; /* For mobile devices (1 column) */ @media screen and (max-width: 739px) { grid-template-columns: repeat(1, 1fr); \\${props.tab !== \"pot\" && \"padding-top: 40px;\"} } /* For tablet devices (2 columns) */ @media screen and (min-width: 740px) and (max-width: 1023px) { grid-template-columns: repeat(2, 1fr); } /* For desktop devices (3 columns) */ @media screen and (min-width: 1024px) { grid-template-columns: repeat(\\${!props.maxCols || props.maxCols > 2 ? \"3\" : \"2\"}, 1fr); } \\${responsive.map(view => \\` @media screen and (max-width: \\${view.breakpoint}px) { grid-template-columns: repeat(\\${view._items}, 1fr); } \\`)} \\`; return <Feed items={_items} Item={renderItem} Layout={Grid} perPage={PAGE_SIZE} />; `, AllProjects: ` const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const A_178 = { Category: [{ label: \"Desci\", val: \"Desci\" }, { label: \"Open Source\", val: \"Open Source\" }, { label: \"Non Profit\", val: \"Non Profit\" }, { label: \"Social Impact\", val: \"Social Impact\" }, { label: \"Climate\", val: \"Climate\" }, { label: \"Public Good\", val: \"Public Good\" }, { label: \"Community\", val: \"Community\" }, { label: \"Education\", val: \"Education\" }], Status: [{ label: \"All\", val: \"all\" }, { label: \"Approved\", val: \"Approved\" }, { label: \"Pending\", val: \"Pending\" }, { label: \"Rejected\", val: \"Rejected\" }, { label: \"Graylisted\", val: \"Graylisted\" }, { label: \"Blacklisted\", val: \"Blacklisted\" }]}; const getTagsFromSocialProfileData = profileData => { if (!profileData) return []; const DEPRECATED_CATEGORY_MAPPINGS = { \"social-impact\": \"Social Impact\", \"non-profit\": \"NonProfit\", climate: \"Climate\", \"public-good\": \"Public Good\", \"de-sci\": \"DeSci\", \"open-source\": \"Open Source\", community: \"Community\", education: \"Education\" }; const tags = profileData.plCategories ? JSON.parse(profileData.plCategories) : profileData.category ? [profileData.category.text ?? DEPRECATED_CATEGORY_MAPPINGS[profileData.category] ?? \"\"] : []; return tags;}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsd = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then(res => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\");const yoctosToUsd = amount => { return nearToUsd ? \"~\\$\" + formatWithCommas(new Big(amount).mul(nearToUsd).div(1e24).toFixed(2)) : \"0\";}; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const getTeamMembersFromSocialProfileData = profileData => { if (!profileData) return []; const team = profileData.plTeam ? JSON.parse(profileData.plTeam) : profileData.team ? Object.entries(profileData.team).filter(([_, v]) => v !== null).map(([k, _]) => k) : []; return team;}; const A_185 = styled.div\\` display: flex; flex-direction: column; padding: 48px 40px 0; @media screen and (max-width: 768px) { padding: 40px 20px 0; }\\`;const A_186 = styled.div\\` display: flex; flex-direction: column; gap: 20px; width: 100%;\\`;const ProjectsContainer = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; overflow-y: hidden; padding-top: 5px; > div { width: 100%; }\\`;const FilterWrapper = styled.div\\` display: flex; gap: 1rem; align-items: center; width: 100%; .filter-menu { left: 0; right: auto; } .left-side-menu { left: auto; right: 0; } @media screen and (max-width: 768px) { flex-wrap: wrap; > div:nth-of-type(2) { width: 100%; order: -1; flex: auto; } .filter-menu { width: 250px !important; } .left-side-menu { right: auto; left: 0; } }\\`;const A_187 = styled.div\\` color: #292929; font-size: 14px; font-style: normal; font-weight: 500; line-height: 24px; letter-spacing: 1.12px; text-transform: uppercase;\\`; const createDebounce = (cb, timeout) => { let timer; const _timeout = timeout || 1000; return args => { clearTimeout(timer); timer = setTimeout(() => { cb(args); }, _timeout); };}; const { projectsData: projectsData } = props; const isRegistryAdmin = ListsSDK.isRegistryAdmin(context.accountId); const [totalDonation, setTotalDonation] = useState(0); const [totalDonated, setTotalDonated] = useState(\"0\"); const [projects, setProjects] = useState([]); const [filteredProjects, setFilteredProjects] = useState([]); const [sort, setSort] = useState(\"Sort\"); useEffect(() => { if (projects.length === 0 && projectsData) { const { allProjects, approvedProjects } = projectsData; setProjects(allProjects); setFilteredProjects(approvedProjects); } }, [projectsData]); if (!projects) { return \"\"; } const donateConfig = DonateSDK.getConfig(); if (donateConfig && !totalDonated && !totalDonation) { const lastDonationAmount = yoctosToUsd(donateConfig.net_donations_amount); setTotalDonated(lastDonationAmount); setTotalDonation(donateConfig.total_donations_count); } const handleSortChange = (sortType) => { setSort(sortType); const projects = [...filteredProjects]; switch (sortType) { case \"All\": break; case \"Newest to Oldest\": projects.sort((a, b) => b.submitted_ms - a.submitted_ms); setFilteredProjects(projects); break; case \"Oldest to Newest\": projects.sort((a, b) => a.submitted_ms - b.submitted_ms); setFilteredProjects(projects); break; default: break; } }; const searchByWords = (searchTerm) => { searchTerm = searchTerm.toLowerCase().trim(); let results = []; projects.forEach((project) => { const { registrant_id, status } = project; const data = Social.getr(\\`\\${registrant_id}/profile\\`); if (registrant_id.includes(searchTerm) || status.toLowerCase().includes(searchTerm)) { results.push(project); } else if (data) { if (data.description.toLowerCase().includes(searchTerm) || data.name.toLowerCase().includes(searchTerm) || getTagsFromSocialProfileData(data).join(\"\").toLowerCase().includes(searchTerm) || getTeamMembersFromSocialProfileData(data).join(\"\").toLowerCase().includes(searchTerm)) { results.push(project); } } }); setFilteredProjects(results); }; const checkAllTrue = (arr) => arr.every((item) => item === true); const filterProjects = (filters) => projects.filter((item) => { const filterVals = Object.keys(filters).map((type) => { if (filters[type].length === 0) return true; if (type === \"Category\") { const data = Social.getr(\\`\\${item.registrant_id}/profile\\`); const tagsForProfile = getTagsFromSocialProfileData(data); return filters[type].some((tag) => tagsForProfile.includes(tag)); } if (type === \"Status\") { if (filters[type].includes(\"all\")) return true; return filters[type].includes(item.status); } return true; }); return checkAllTrue(filterVals); }); const handleTag = (selectedFilters) => { const projectFilterBySearch = filterProjects(selectedFilters); setFilteredProjects(projectFilterBySearch); }; const onSearchChange = createDebounce((searchTerm) => searchByWords(searchTerm), 1000); const SORT_FILTERS = { NEW_TO_OLD: \"Newest to Oldest\", OLD_TO_NEW: \"Oldest to Newest\" }; return <A_185> <A_186> <A_187> All projects <span style={{ color: \"#DD3345\", marginLeft: \"8px\", fontWeight: 600 }}>{filteredProjects.length}</span> </A_187> <FilterWrapper> <Widget loading=\" \" code={props.alem.componentsCode.FilterDropdown} props={{ ...{ ...{ onClick: handleTag, multipleOptions: true, options: A_178, defaultSelected: { Status: [\"Approved\"] }, menuClass: \"filter-menu\" }, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.A_184} props={{ ...{ ...{ title: sort, numItems: filteredProjects.length, itemName: \"project\", sortList: Object.values(SORT_FILTERS), FilterMenuClass: \\`left-side-menu\\`, setSearchTerm: onSearchChange, handleSortChange: (filter) => {handleSortChange(filter);} }, ...props } }} /> </FilterWrapper> </A_186> <ProjectsContainer> {filteredProjects.length ? <Widget loading=\" \" code={props.alem.componentsCode.ListSection} props={{ ...{ items: filteredProjects, renderItem: (project) => <Widget loading=\" \" code={props.alem.componentsCode.A_197} props={{ ...{ projectId: project.registrant_id, ...props } }} />, ...props } }} /> : <div style={{ alignSelf: \"flex-start\", margin: \"24px 0px\" }}>No results</div>} </ProjectsContainer> </A_185>; `, A_197: ` const useDonationModal = () => useContext(\"donation-modal\"); const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const routesPath = { CREATE_PROJECT_TAB: \"createproject\", EDIT_PROJECT_TAB: \"editproject\", PROJECTS_LIST_TAB: \"projects\", PROJECT_DETAIL_TAB: \"project\", CART_TAB: \"cart\", FEED_TAB: \"feed\", POTS_TAB: \"pots\", DEPLOY_POT_TAB: \"deploypot\", POT_DETAIL_TAB: \"pot\", DONORS_TAB: \"donors\", PROFILE_TAB: \"profile\", EDIT_PROFILE_TAB: \"editprofile\"}; const getTagsFromSocialProfileData = profileData => { if (!profileData) return []; const DEPRECATED_CATEGORY_MAPPINGS = { \"social-impact\": \"Social Impact\", \"non-profit\": \"NonProfit\", climate: \"Climate\", \"public-good\": \"Public Good\", \"de-sci\": \"DeSci\", \"open-source\": \"Open Source\", community: \"Community\", education: \"Education\" }; const tags = profileData.plCategories ? JSON.parse(profileData.plCategories) : profileData.category ? [profileData.category.text ?? DEPRECATED_CATEGORY_MAPPINGS[profileData.category] ?? \"\"] : []; return tags;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const ipfsUrlFromCid = cid => { const { IPFS_BASE_URL } = constants; return \\`\\${IPFS_BASE_URL}\\${cid}\\`;}; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const CardContainer = styled.div\\` display: flex; flex-direction: column; max-width: 420px; min-height: 405px; width: 100%; height: 100%; overflow: hidden; border-radius: 12px; background: white; box-shadow: 0px -2px 0px #dbdbdb inset; border: 1px solid #dbdbdb; margin-left: auto; margin-right: auto; transition: all 300ms; &:hover { transform: translateY(-1rem); }\\`;const A_190 = styled.div\\` padding-left: 16px; &:hover { text-decoration: none; cursor: pointer; } @media screen and (max-width: 768px) { border-radius: 0; }\\`;const backgroundStyleHeightPx = 168;const A_191 = styled.div\\` svg { position: absolute; top: \\${backgroundStyleHeightPx / 2}px; left: 50%; transform: translate(-50%, -50%); opacity: 0; transition: opacity 0.3s; z-index: 2; pointer-events: none; }\\`;const A_192 = styled.div\\` transform: translateY(138px); width: 40px; height: 40px; position: absolute; img { width: 40px; height: 40px; } &:hover { cursor: pointer; &:after { /* Dark overlay with 40% opacity on hover */ background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; /* Make the image visible on hover */ } }\\`;const A_193 = styled.div\\` display: flex; flex-direction: column; margin-top: 176px; padding: 16px 24px; gap: 16px; flex: 1;\\`;const A_194 = styled.div\\` font-size: 16px; font-weight: 600; color: #2e2e2e; width: 100%;\\`;const A_195 = styled.div\\` font-size: 16px; font-weight: 400; color: #2e2e2e; word-wrap: break-word;\\`;const DonationsInfoContainer = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 16px 24px; width: 100%; border-top: 1px #f0f0f0 solid;\\`;const DonationsInfoItem = styled.div\\` display: flex; flex-direction: row; gap: 8px; align-items: center;\\`;const DonationButton = styled.button\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: #fef6ee; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: #2e2e2e; font-size: 14px; line-height: 16px; font-weight: 600; &:hover { text-decoration: none; cursor: pointer; }\\`;const Amount = styled.div\\` font-size: 17px; font-weight: 600; color: #292929; line-height: 24px;\\`;const AmountDescriptor = styled.div\\` font-size: 11px; font-weight: 400; color: #525252; word-wrap: break-word; text-transform: uppercase; line-height: 16px; letter-spacing: 1.1px;\\`;const Tags = styled.div\\` display: flex; gap: 8px; flex-wrap: wrap;\\`;const A_196 = styled.span\\` box-shadow: 0px -0.699999988079071px 0px rgba(123, 123, 123, 0.36) inset; padding: 4px 8px; border-radius: 4px; border: 1px solid rgba(123, 123, 123, 0.36); color: #2e2e2e;\\`;const MatchingSection = styled.div\\` display: flex; padding: 8px 24px; align-items: center; justify-content: space-between; background: #ebebeb; border-radius: 0px 0px 12px 12px;\\`;const MatchingTitle = styled.div\\` color: #292929; font-size: 11px; font-weight: 400; line-height: 18px; letter-spacing: 1.1px; text-transform: uppercase; word-wrap: break-word;\\`;const MatchingAmount = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`; const loadingSkeleton = styled.keyframes\\` 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; }\\`;const CardSkeletonContainer = styled.div\\` display: flex; flex-direction: column; height: 447px; width: 100%; max-width: 400px; border-radius: 12px; background: white; box-shadow: 0px -2px 0px #dbdbdb inset; border: 1px solid #dbdbdb; margin-left: auto; margin-right: auto; animation-name: \\${loadingSkeleton}; animation-duration: 1s; animation-iteration-count: infinite;\\`;const HeaderSkeleton = styled.div\\` display: block; width: 100%; height: 168px; background: #eee;\\`;const ProfileImageSkeleton = styled.div\\` background: #e0e0e0; margin-left: 32px; transform: translateY(148px); width: 40px; height: 40px; position: absolute; border-radius: 999px;\\`;const TitleSkeleton = styled.div\\` width: 120px; height: 24px; background: #eee; margin-left: 24px; margin-top: 24px;\\`;const DescriptionSkeleton = styled.div\\` width: 83%; height: 48px; background: #eee; margin-left: 24px; margin-top: 24px;\\`;const TagSkeleton = styled.div\\` background: #eee; border-radius: 4px; height: 34px; width: 110px; margin: 24px;\\`;const FooterItemSkeleton = styled.div\\` width: 150px; height: 40px; background: #eee; @media screen and (max-width: 390px) { width: 100px; }\\`;const DonationsInfoContainerSkeleton = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 16px 24px; width: 100%; border-top: 1px #f0f0f0 solid;\\`;const DonationsInfoItemSkeleton = styled.div\\` display: flex; flex-direction: row; gap: 8px; align-items: center;\\`;const CardSkeleton = () => <CardSkeletonContainer> <HeaderSkeleton /> <ProfileImageSkeleton /> <TitleSkeleton /> <DescriptionSkeleton /> <TagSkeleton /> <DonationsInfoContainerSkeleton> <DonationsInfoItemSkeleton> <FooterItemSkeleton /> </DonationsInfoItemSkeleton> <DonationsInfoItemSkeleton> <FooterItemSkeleton /> </DonationsInfoItemSkeleton> </DonationsInfoContainerSkeleton> </CardSkeletonContainer>; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const navigate = { to: (route, params) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } if (routeContext.routes.includes(route)) { routeContext.updateRouteParameters({ ...routeContext, activeRoute: route, routeParams: params || {} }); } }, back: () => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } const updatedHistory = routeContext.history; if (updatedHistory) { updatedHistory.pop(); const routeProps = updatedHistory.at(-1); if (routeProps.route) { routeContext.updateRouteParameters({ ...routeContext, history: updatedHistory, activeRoute: routeProps.route, routeParams: routeProps.routeParams }); } } }}; const RouteLink = ({ to, href, target, params, label, className, style, onClick, children}) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"RouteLink component is being used without Router on top of it.\"); } const onClickHandler = () => { if (onClick) { onClick(); } if (routeContext.routeType === \"ContentBased\") { navigate.to(to, params); } }; if (routeContext.routeType === \"URLBased\") { let strParams = \"\"; if (params) { Object.keys(params).forEach(paramKey => { strParams += \\`&\\${paramKey}=\\${params[paramKey]}\\`; }); } const Link = styled(\"Link\")\\`\\`; return <Link onClick={onClickHandler} className={className} style={{ cursor: \"pointer\", textDecoration: \"none\", ...style }} target={target} href={href ? href : \\`?\\${routeContext.routeParameterName || \"path\"}=\\${to}\\${strParams}\\`}> {label || children} </Link>; } return <a style={{ cursor: \"pointer\", textDecoration: \"none\", ...style }} className={className} onClick={onClickHandler}> {label || children} </a>;}; const props = props; const { payoutDetails, allowDonate: _allowDonate } = props; const { potId } = props.alem.useParams(); const { setDonationModalProps } = useDonationModal(); const [ready, isReady] = useState(false); const projectId = props.project.registrant_id || props.projectId; const profile = Social.getr(\\`\\${projectId}/profile\\`); const allowDonate = _allowDonate ?? true; const MAX_DESCRIPTION_LENGTH = 80; const { name, description } = profile; const donationsForProject = potId && !payoutDetails ? PotSDK.getDonationsForProject(potId, projectId) : !potId ? DonateSDK.getDonationsForRecipient(projectId) : []; useEffect(() => { if (profile !== null && !ready) { isReady(true); } }, [profile, donationsForProject]); const totalAmountNear = useMemo(() => { if (payoutDetails) return payoutDetails.totalAmount; if (!donationsForProject) return \"0\"; let totalDonationAmountNear = new Big(0); for (const donation of donationsForProject) { if (donation.ft_id === \"near\" || donation.base_currency === \"near\" || potId) { totalDonationAmountNear = totalDonationAmountNear.plus(new Big(donation.total_amount)); } } return totalDonationAmountNear.toString(); }, [donationsForProject, payoutDetails]); const getImageSrc = (image) => { const defaultImageUrl = \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\"; if (!image) return defaultImageUrl; const { url, ipfs_cid } = image; if (ipfs_cid) { return ipfsUrlFromCid(ipfs_cid); } else if (url) { return url; } return defaultImageUrl; }; const backgroundImageStyle = { objectFit: \"cover\", left: 0, top: 0, height: \"168px\", borderRadius: \"6px 6px 0px 0px\", pointerEvents: \"none\" }; const profileImageStyle = { width: \"40px\", height: \"40px\", position: \"absolute\", bottom: \"-10px\", left: \"14px\", pointerEvents: \"none\" }; const tags = getTagsFromSocialProfileData(profile); if (!ready) return <CardSkeleton />; return <> <RouteLink to={routesPath.PROJECT_DETAIL_TAB} params={{ projectId, ...(potId ? { potId } : {}) }}> <CardContainer> <A_190 className=\"pt-0 position-relative\"> <A_191> {profile.backgroundImage?.nft ? <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: profile.backgroundImage, alt: \"background\", className: \"position-absolute w-100\", style: backgroundImageStyle, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\" }, ...props } }} /> : <img className=\"position-absolute w-100\" style={backgroundImageStyle} src={getImageSrc(profile.backgroundImage)} alt=\"background\" />} </A_191> <A_192 className=\"profile-picture d-inline-block\"> {profile.image?.nft ? <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: profile.image, alt: \"avatar\", className: \"rounded-circle w-100 img-thumbnail d-block\", style: profileImageStyle, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\" }, ...props } }} /> : <img className=\"rounded-circle w-100 img-thumbnail d-block\" style={profileImageStyle} src={getImageSrc(profile.image)} alt=\"avatar\" />} </A_192> </A_190> <A_193> <A_194>{_address(name, 30) || _address(projectId, 30)}</A_194> <A_195> {description.length > MAX_DESCRIPTION_LENGTH ? description.slice(0, MAX_DESCRIPTION_LENGTH) + \"...\" : description} </A_195> {!tags.length ? \"No tags\" : <Tags> {tags.map((tag, tagIndex) => <A_196 key={tagIndex}>{tag}</A_196>)} </Tags>} </A_193> <DonationsInfoContainer> <DonationsInfoItem> <Amount>{totalAmountNear ? yoctosToUsdWithFallback(totalAmountNear) : \"-\"}</Amount> <AmountDescriptor>Raised</AmountDescriptor> </DonationsInfoItem> {payoutDetails && <DonationsInfoItem> <Amount>{payoutDetails.donorCount}</Amount> <AmountDescriptor>{payoutDetails.donorCount === 1 ? \"Donor\" : \"Donors\"}</AmountDescriptor> </DonationsInfoItem>} {allowDonate && context.accountId && <DonationButton onClick={(e) => { e.preventDefault(); setDonationModalProps({ projectId }); }} disabled={!context.accountId}> Donate </DonationButton>} </DonationsInfoContainer> {payoutDetails && <MatchingSection> <MatchingTitle>Estimated matched amount</MatchingTitle> <MatchingAmount>{yoctosToNear(payoutDetails.matchingAmount) || \"- N\"}</MatchingAmount> </MatchingSection>} </CardContainer> </RouteLink> </>; `, FeaturedProjects: ` const loadingSkeleton = styled.keyframes\\` 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; }\\`;const CardSkeletonContainer = styled.div\\` display: flex; flex-direction: column; height: 447px; width: 100%; max-width: 400px; border-radius: 12px; background: white; box-shadow: 0px -2px 0px #dbdbdb inset; border: 1px solid #dbdbdb; margin-left: auto; margin-right: auto; animation-name: \\${loadingSkeleton}; animation-duration: 1s; animation-iteration-count: infinite;\\`;const HeaderSkeleton = styled.div\\` display: block; width: 100%; height: 168px; background: #eee;\\`;const ProfileImageSkeleton = styled.div\\` background: #e0e0e0; margin-left: 32px; transform: translateY(148px); width: 40px; height: 40px; position: absolute; border-radius: 999px;\\`;const TitleSkeleton = styled.div\\` width: 120px; height: 24px; background: #eee; margin-left: 24px; margin-top: 24px;\\`;const DescriptionSkeleton = styled.div\\` width: 83%; height: 48px; background: #eee; margin-left: 24px; margin-top: 24px;\\`;const TagSkeleton = styled.div\\` background: #eee; border-radius: 4px; height: 34px; width: 110px; margin: 24px;\\`;const FooterItemSkeleton = styled.div\\` width: 150px; height: 40px; background: #eee; @media screen and (max-width: 390px) { width: 100px; }\\`;const DonationsInfoContainerSkeleton = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 16px 24px; width: 100%; border-top: 1px #f0f0f0 solid;\\`;const DonationsInfoItemSkeleton = styled.div\\` display: flex; flex-direction: row; gap: 8px; align-items: center;\\`;const CardSkeleton = () => <CardSkeletonContainer> <HeaderSkeleton /> <ProfileImageSkeleton /> <TitleSkeleton /> <DescriptionSkeleton /> <TagSkeleton /> <DonationsInfoContainerSkeleton> <DonationsInfoItemSkeleton> <FooterItemSkeleton /> </DonationsInfoItemSkeleton> <DonationsInfoItemSkeleton> <FooterItemSkeleton /> </DonationsInfoItemSkeleton> </DonationsInfoContainerSkeleton> </CardSkeletonContainer>; const ContainerHeader = styled.div\\` display: flex; flex-direction: column; width: 100%; gap: 48px; padding: 48px 40px 0; @media screen and (max-width: 768px) { padding: 40px 8px 0; }\\`;const A_188 = styled.div\\` display: flex; flex-direction: column; gap: 20px; width: 100%;\\`;const A_189 = styled.div\\` color: #292929; font-size: 14px; font-style: normal; font-weight: 500; line-height: 24px; letter-spacing: 1.12px; text-transform: uppercase;\\`;const ProjectList = styled.div\\` display: grid; gap: 31px; /* For mobile devices (1 column) */ @media screen and (max-width: 739px) { grid-template-columns: repeat(1, 1fr); } /* For tablet devices (2 columns) */ @media screen and (min-width: 740px) and (max-width: 1023px) { grid-template-columns: repeat(2, 1fr); } /* For desktop devices (3 columns) */ @media screen and (min-width: 1024px) { grid-template-columns: repeat(3, 1fr); }\\`;const OnBottom = styled.div\\` width: 100%; display: flex; align-items: center; justify-content: center; padding: 20px 0;\\`; const { projectsData: projectsData } = props; const [projects, setProjects] = useState([]); useEffect(() => { if (projectsData) { const { featuredProjects } = projectsData; setProjects(featuredProjects); } }, [projectsData]); const LoadingCards = () => <> <CardSkeleton /> <CardSkeleton /> <CardSkeleton /> </>; const projectCards = useMemo(() => <> {projects.map((project) => { return <Widget loading=\" \" code={props.alem.componentsCode.A_197} props={{ ...{ projectId: project.registrant_id, ...props } }} />; })} </>, [projects]); return <ContainerHeader> <A_188> <A_189>Featured projects</A_189> </A_188> <ProjectList>{projects.length === 0 ? <LoadingCards /> : <>{projectCards}</>}</ProjectList> <OnBottom></OnBottom> </ContainerHeader>; `, ProjectsPage: ` const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const [projectsData, setProjectsDate] = useState(null); if (!projectsData) { ListsSDK.asyncGetRegistrations().then((allProjects) => { if (!allProjects) { return { allProjects: [], featuredProjects: [] }; } allProjects.sort((a, b) => b.submitted_ms - a.submitted_ms); const featuredProjectIds = [\"v1.foodbank.near\", \"potlock.near\", \"yearofchef.near\"]; const featuredProjects = allProjects.filter((project) => featuredProjectIds.includes(project.registrant_id)); const approvedProjects = allProjects.filter((project) => project.status === \"Approved\"); setProjectsDate({ allProjects, approvedProjects, featuredProjects }); }); } return <> <Widget loading=\" \" code={props.alem.componentsCode.NewHero} props={{ ...{ projectsData: projectsData, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.FeaturedProjects} props={{ ...{ projectsData: projectsData, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.AllProjects} props={{ ...{ projectsData: projectsData, ...props } }} /> </>; `, Router: ` const props = props; const { routes, type, parameterName, alem, alemRoutes, initialRoute } = props; useEffect(() => { routes.forEach(route => { if (!route.component) { console.error(\\`Routes: Invalid component for route \"\\${route.path}\"\\`); } if (!route.path) { console.error(\"Routes: Invalid path:\", route.path); } }); }, [routes]); const { routeParameterName, routeType, activeRoute } = alemRoutes; const routeParamName = parameterName || routeParameterName; const checkIfPathIsIncludedToRoutes = routePath => { let pathFound = false; if (routes) { routes.forEach(routeItem => { if (pathFound) return; if (!pathFound) { pathFound = routeItem.path === routePath; } }); } return pathFound; }; useEffect(() => { const bosProps = alem.rootProps; if (routes) { let currentUrlPath = bosProps[routeParamName] && checkIfPathIsIncludedToRoutes(bosProps[routeParamName]) ? bosProps[routeParamName] : routes[0].path; const _routes = routes.map(route => route.path); const _type = type || \"URLBased\"; let _activeRoute = initialRoute || alemRoutes.activeRoute || currentUrlPath; let _activeRouteParams = null; let _history = null; if (alem.keepRoute && type === \"ContentBased\") { const storedRouteProps = Storage.privateGet(\"alem::keep-route\"); if (storedRouteProps) { _history = storedRouteProps; const lastHistory = storedRouteProps[storedRouteProps.length - 1]; _activeRoute = lastHistory.route || null; _activeRouteParams = lastHistory.routeParams || null; } } if (!_activeRoute) { _activeRoute = routes[0].path; } if (!alemRoutes.routesInitialized) { alemRoutes.updateRouteParameters({ routes: _routes, routeType: _type, activeRoute: _activeRoute, routeParams: _activeRouteParams, history: _history, routeParameterName: routeParamName }); } } }, [routeType]); const Component = routes.find(route => route.path === activeRoute)?.component || routes[0].component || <></>; return <Component />; `, ModalSuccess: ` const useDonationModal = () => useContext(\"donation-modal\"); const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const AlertBanner = styled.div\\` display: flex; padding: 0.75rem 1rem; color: #ed464f; gap: 1rem; align-items: center; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; margin-top: 1.5rem; div { font-weight: 500; } .icon { width: 22px; }\\`;const NadabotBanner = styled.div\\` display: flex; align-items: center; padding: 0.75rem 1rem; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; flex-wrap: wrap; margin-top: 1.5rem; .label { display: flex; align-items: center; font-weight: 500; gap: 1rem; img { width: 24px; height: 24px; } } .verify { color: #dd3345; font-weight: 500; margin-left: auto; &:hover { text-decoration: none; } } @media only screen and (max-width: 480px) { flex-direction: column; align-items: flex-start; gap: 0px; .labe { align-items: flex-start; } .verify { margin-left: 40px; } }\\`;const VerifyInfoWrapper = styled.div\\` display: flex; align-items: center; gap: 14px; padding: 1rem; border-radius: 6px; border: 1px solid #ecc113; background: #fbf9c6; box-shadow: 0px 2px 1px 1px rgba(255, 255, 255, 0.8) inset, 0px -2px 4px 0px rgba(15, 15, 15, 0.15) inset; font-size: 14px; color: #3f2209; margin-top: 1.5rem; .icon { width: 17px; display: flex; height: fit-content; svg { width: 100%; } } .text { flex: 1; line-height: 150%; } a { font-weight: 500; color: #dd3345; :hover { text-decoration: none; } } @media only screen and (max-width: 480px) { flex-wrap: wrap; a { width: 100%; text-align: center; } }\\`; const VerifyInfo = () => <VerifyInfoWrapper> <div className=\"icon\"> <svg width=\"18\" height=\"16\" viewBox=\"0 0 18 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0.75 15.125H17.25L9 0.875L0.75 15.125ZM9.75 12.875H8.25V11.375H9.75V12.875ZM9.75 9.875H8.25V6.875H9.75V9.875Z\" fill=\"#ECC113\" /> </svg> </div> <div className=\"text\"> Your contribution won't be matched unless verified as human before the matching round ends. </div> <a href=\"https://app.nada.bot/\" target=\"_blank\"> Verify you’re human </a> </VerifyInfoWrapper>; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsd = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then(res => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\");const yoctosToUsd = amount => { return nearToUsd ? \"~\\$\" + formatWithCommas(new Big(amount).mul(nearToUsd).div(1e24).toFixed(2)) : \"0\";}; const Column = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start;\\`;const A_198 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const ModalMain = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; gap: 2rem; padding: 40px 32px;\\`;const A_199 = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; background: #f6f5f3; gap: 12px; padding: 28px 36px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px;\\`;const A_200 = styled.div\\` display: flex; flex-direction: column; align-items: center; gap: 1rem;\\`;const HeaderIcon = styled.div\\` padding: 12px; width: 48px; height: 48px; border-radius: 50%; background: #dd3345; box-shadow: 0px 0px 0px 6px #fee6e5; svg { width: 100%; height: 100%; }\\`;const TwitterShare = styled.a\\` display: flex; gap: 8px; color: white; border-radius: 4px; padding: 6px 1rem; background: rgb(41, 41, 41); align-items: center; font-size: 14px; cursor: pointer; :hover { text-decoration: none; }\\`;const ModalMiddel = styled.div\\` display: flex; flex-direction: column; gap: 4px; .amount-wrapper { display: flex; align-items: center; gap: 8px; justify-content: center; img, svg { width: 20px; height: 20px; } img { border-radius: 50%; } }\\`;const A_201 = styled.div\\` font-size: 22px; font-weight: 500; letter-spacing: -0.33px; text-transform: uppercase;\\`;const AmountUsd = styled.div\\` color: #7b7b7b; font-size: 22px;\\`;const ProjectName = styled.div\\` display: flex; align-items: center; gap: 3px; font-size: 14px; div { color: #7b7b7b; } a { color: #525252; &:hover { text-decoration: none; } }\\`;const H1 = styled.h1\\` color: #292929; font-size: 24px; font-weight: 600; line-height: 32px; word-wrap: break-word;\\`;const A_202 = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const UserChip = styled.div\\` display: flex; flex-direction: row; padding: 2px 12px; gap: 4px; border-radius: 32px; background: #ebebeb;\\`;const A_203 = styled.a\\` display: flex; flex-direction: row; padding: 2px 12px; gap: 4px; border-radius: 32px; background: #ebebeb; &:hover { text-decoration: none; }\\`;const ShareText = styled.div\\` color: #7b7b7b; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const SocialIcon = styled.svg\\` width: 24px; height: 24px; cursor: pointer; path { transition: 300ms; } :hover path { fill: #dd3345; }\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const useRoutes = () => { const contextData = useContext(\"alemRoutes\"); if (!contextData) { console.error(\"useRoutes: You need to call \\`RouterProvider()\\` first.\"); } const data = { routesInitialized: contextData.routesInitialized, activeRoute: contextData.activeRoute, routeParameterName: contextData.routeParameterName, routes: contextData.routes, routeType: contextData.routeType, routeParams: contextData.routeParams, history: contextData.history }; return data;}; const getLocation = () => { const routes = useRoutes(); return { pathname: routes.activeRoute, routes: routes.routes, isRoutesReady: routes.routes && routes.routes.length > 0 };}; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const navigate = { to: (route, params) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } if (routeContext.routes.includes(route)) { routeContext.updateRouteParameters({ ...routeContext, activeRoute: route, routeParams: params || {} }); } }, back: () => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } const updatedHistory = routeContext.history; if (updatedHistory) { updatedHistory.pop(); const routeProps = updatedHistory.at(-1); if (routeProps.route) { routeContext.updateRouteParameters({ ...routeContext, history: updatedHistory, activeRoute: routeProps.route, routeParams: routeProps.routeParams }); } } }}; const DEFAULT_GATEWAY = \"https://bos.potlock.org/\"; const POTLOCK_TWITTER_ACCOUNT_ID = \"PotLock_\"; const DEFAULT_SHARE_HASHTAGS = [\"BOS\", \"PublicGoods\", \"Donations\"]; const params = props.alem.useParams(); const { transactionHashes } = params; const { setSuccessfulDonation: _setSuccessfulDonation, successfulDonation: _successfulDonation } = useDonationModal(); const { NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, ownerId } = constants; State.init({ showBreakdown: false }); const onClose = () => { _setSuccessfulDonation(null); const location = getLocation(); delete params.transactionHashes; navigate.to(location.pathname, params); }; const [successfulDonation, setSuccessfulDonation] = useState(_successfulDonation); const [ftMetadata, setFtMetadata] = useState(null); const { recipientProfile, successfulApplication } = state; const successfulDonationVals = successfulDonation ? Object.values(successfulDonation) : []; const recipientProfileVals = recipientProfile ? Object.values(recipientProfile) : []; const getProfileDataForSuccessfulDonation = (donationsObj) => { const donations = Object.values(donationsObj); donations.forEach((donation) => { const { donor_id, recipient_id, project_id } = donation; Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${recipient_id || project_id}/profile/**\\`] }).then((recipientData) => { Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${donor_id}/profile/**\\`] }).then((donorData) => { State.update({ recipientProfile: { ...recipientProfile, [recipient_id || project_id]: recipientData[recipient_id || project_id]?.profile || {} }, donorProfile: donorData[donor_id]?.profile || {} }); }); }); }); }; if (successfulDonation && !recipientProfile) { getProfileDataForSuccessfulDonation(successfulDonation); } const getTotalAmount = () => { let totalAmount = Big(0); if (successfulDonation) { successfulDonationVals.forEach((donation) => totalAmount = totalAmount.plus(Big(donation.total_amount))); } return totalAmount.toString(); }; const totalAmount = getTotalAmount(); if (transactionHashes && !successfulDonation) { const transactionHashesList = transactionHashes.split(\",\"); for (let i = 0; i < transactionHashesList.length; i++) { const txHash = transactionHashesList[i]; const body = JSON.stringify({ jsonrpc: \"2.0\", id: \"dontcare\", method: \"tx\", params: [txHash, context.accountId] }); asyncFetch(\"https://rpc.mainnet.near.org\", { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body }).then((res) => { const methodName = res.body.result.transaction.actions[0].FunctionCall.method_name; const successVal = res.body.result.status?.SuccessValue; const receiver_id = res.body.result.transaction.receiver_id; const result = JSON.parse(Buffer.from(successVal, \"base64\").toString(\"utf-8\")); const args = JSON.parse(Buffer.from(res.body.result.transaction.actions[0].FunctionCall.args, \"base64\").toString(\"utf-8\")); const recipientId = methodName === \"donate\" ? result.project_id ? result.project_id : result.recipient_id : methodName === \"ft_transfer_call\" ? JSON.parse(args.msg).recipient_id : \"\"; if (recipientId) { if (methodName === \"donate\") { setSuccessfulDonation((prev) => ({ ...prev, [recipientId]: { ...result, potId: receiver_id } })); } else if (methodName === \"apply\") { State.update({ successfulApplication: result }); } else if (methodName === \"ft_transfer_call\" && recipientId) { asyncFetch(\\`https://near-mainnet.api.pagoda.co/eapi/v1/nep141/metadata/\\${receiver_id}\\`, { headers: { \"Content-Type\": \"application/json\", \"x-api-key\": \"dce81322-81b0-491d-8880-9cfef4c2b3c2\" } }).then((data) => { const metadata = data.body.metadata; setFtMetadata(metadata); setSuccessfulDonation((prev) => ({ ...prev, [recipientId]: { project_id: recipientId, total_amount: result } })); }).catch((err) => console.log(err)); } } }).catch((err) => console.log(err)); } } useEffect(() => { if (successfulDonation && !ftMetadata) { Near.asyncView(successfulDonationVals[0]?.ft_id, \"ft_metadata\", {}).then((metaDate) => setFtMetadata(metaDate)); } }, [successfulDonation]); const twitterIntent = useMemo(() => { if (!recipientProfile) return; const twitterIntentBase = \"https://twitter.com/intent/tweet?text=\"; const recipient_id = successfulDonationVals[0].recipient_id || successfulDonationVals[0].project_id; const profile = recipientProfileVals[0]; const singlePorject = profile ? profile.linktree?.twitter ? \\`@\\${profile.linktree.twitter}\\` : profile.name : recipient_id; const tag = \\`\\${singlePorject}\\` + (successfulDonationVals.length > 1 ? \\` and \\${successfulDonationVals?.length - 1} other\\${successfulDonationVals?.length === 2 ? \"\" : \"s\"}\\` : \"\"); let url = DEFAULT_GATEWAY + (successfulDonationVals[0].potId ? \\`\\${ownerId}/widget/Index?tab=pot&potId=\\${successfulDonationVals[0].potId}&referrerId=\\${context.accountId}\\` : \\`\\${ownerId}/widget/Index?tab=project&projectId=\\${recipient_id}&referrerId=\\${context.accountId}\\`); let text = \\`I just donated to \\${tag} on @\\${POTLOCK_TWITTER_ACCOUNT_ID}! Support public goods at \\`; text = encodeURIComponent(text); url = encodeURIComponent(url); return twitterIntentBase + text + \\`&url=\\${url}\\` + \\`&hashtags=\\${DEFAULT_SHARE_HASHTAGS.join(\",\")}\\`; }, [successfulDonation, recipientProfile]); const isUserHumanVerified = Near.view(NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, { account_id: context.accountId }); const needsToVerify = isUserHumanVerified === false; return <ModalOverlay onOverlayClick={onClose} contentStyle={{ padding: \"0px\" }}> <> {successfulApplication ? <> <ModalMain> <H1>Thank you for applying!</H1> <A_202>Your application status: {successfulApplication.status}</A_202> </ModalMain> </> : successfulDonation ? <ModalMain> <A_200> <HeaderIcon> <svg width=\"18\" height=\"14\" viewBox=\"0 0 18 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M5.79499 10.875L1.62499 6.70498L0.204987 8.11498L5.79499 13.705L17.795 1.70498L16.385 0.294983L5.79499 10.875Z\" fill=\"white\" /> </svg> </HeaderIcon> <div>Donation Successful</div> <TwitterShare href={twitterIntent} target=\"_blank\"> <div>Share to</div> <div className=\"icon\"> <svg width=\"15\" height=\"14\" viewBox=\"0 0 15 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M14.25 13.4035L8.97064 5.70858L8.97965 5.71579L13.7398 0.200562H12.1491L8.27135 4.68956L5.19196 0.200562H1.02012L5.9489 7.38477L5.94831 7.38416L0.75 13.4035H2.34071L6.65183 8.40919L10.0782 13.4035H14.25ZM4.56169 1.40082L11.969 12.2032H10.7084L3.29515 1.40082H4.56169Z\" fill=\"#DBDBDB\" /> </svg> </div> </TwitterShare> </A_200> <ModalMiddel> <div className=\"amount-wrapper\"> {ftMetadata?.icon ? <img src={ftMetadata.icon} /> : <svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg>} <A_201> {totalAmount ? parseFloat(Big(totalAmount).div(Big(10).pow(ftMetadata?.decimals || 24)).toFixed(2)) : \"-\"} {ftMetadata?.symbol || \"NEAR\"} </A_201> {totalAmount && yoctosToUsd(totalAmount) && !ftMetadata && <AmountUsd>{yoctosToUsd(totalAmount)} </AmountUsd>} </div> <ProjectName> <div>Has been donated to</div> <a href={hrefWithParams(\\`?tab=project&projectId=\\${successfulDonationVals[0]?.recipient_id || successfulDonationVals[0]?.project_id}\\`)} target=\"_blank\"> {recipientProfileVals[0]?.name || successfulDonationVals[0]?.recipient_id || successfulDonationVals[0]?.project_id || \"-\"} </a> {successfulDonationVals.length > 1 && <div>and {successfulDonationVals.length - 1} others</div>} </ProjectName> </ModalMiddel> <Widget loading=\" \" code={props.alem.componentsCode.BreakdownSummary} props={{ ...{ ...{ referrerId: successfulDonationVals[0]?.referrer_id, totalAmount: Big(totalAmount).div(Big(10).pow(ftMetadata?.decimals || 24)).toFixed(2), bypassProtocolFee: !successfulDonationVals[0]?.protocol_fee || successfulDonationVals[0]?.protocol_fee === \"0\", ftIcon: ftMetadata?.icon }, ...props } }} /> {needsToVerify && !successfulDonationVals[0]?.recipient_id && <VerifyInfo />} </ModalMain> : null} </> </ModalOverlay>; `, BreakdownSummary: ` const A_237 = (__props__) => <svg {...__props__} style={{ width: 16}} viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg>; const A_211 = styled.svg\\` width: 16px; height: 16px;\\`;const BreakdownSummaryContent = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; .breakdown-details { display: flex; flex-direction: column; width: 100%; margin-top: 8px; gap: 12px; border: 1px #dbdbdb solid; border-radius: 8px; transition: all 300ms ease-in-out; &.hidden { visibility: hidden; height: 0; opacity: 0; transform: translateY(100px); } }\\`;const A_212 = styled.div\\` display: flex; flex-direction: row; justify-content: flex-start; align-items: center; width: 100%;\\`;const BreakdownTitle = styled.div\\` color: #2e2e2e; font-size: 14px; font-weight: 500; word-wrap: break-word;\\`;const ChevronIcon = styled.svg\\` width: 1rem; height: 1rem; margin-left: 8px; transition: all 300ms ease-in-out;\\`;const BreakdownDetails = styled.div\\` display: flex; flex-direction: column; width: 100%; margin-top: 8px; gap: 12px; padding: 16px; border-radius: 8px; border: 1px #dbdbdb solid; background: #fafafa; transition: all 300ms ease-in-out;\\`;const BreakdownItem = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 0 16px; gap: 16px; :first-of-type { padding-top: 1rem; } :last-of-type { padding-bottom: 1rem; }\\`;const A_213 = styled.div\\` color: #7b7b7b; font-size: 14px; font-weight: 400; word-wrap: break-word;\\`;const A_214 = styled.div\\` display: flex; flex-direction: row; align-items: center; font-weight: 500; gap: 8px;\\`;const BreakdownAmount = styled.div\\` color: #2e2e2e; font-size: 14px; line-height: 20px; font-weight: 500; word-wrap: break-word;\\`;const A_215 = styled.img\\` width: 16px; height: 16px;\\`; const basisPointsToPercent = basisPoints => { return basisPoints / 100;}; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const {referrerId: referrerId, totalAmount: totalAmount, bypassProtocolFee: bypassProtocolFee, recipientId: recipientId, potRferralFeeBasisPoints: potRferralFeeBasisPoints, ftIcon: ftIcon, bypassChefFee: bypassChefFee, chef: chef, chefFeeBasisPoints: chefFeeBasisPoints} = props; const donationContractConfig = DonateSDK.getConfig(); State.init({ showBreakdown: true }); if (!donationContractConfig) return \"\"; const { protocol_fee_basis_points } = donationContractConfig; const protocolFeeBasisPoints = props.protocolFeeBasisPoints ?? protocol_fee_basis_points; const referralFeeBasisPoints = potRferralFeeBasisPoints || props.referralFeeBasisPoints; const TOTAL_BASIS_POINTS = 10_000; let projectAllocationBasisPoints = TOTAL_BASIS_POINTS - (bypassProtocolFee || !protocolFeeBasisPoints ? 0 : protocolFeeBasisPoints) - (bypassChefFee || !chefFeeBasisPoints ? 0 : chefFeeBasisPoints); if (referrerId) { projectAllocationBasisPoints -= referralFeeBasisPoints; } const projectAllocationPercent = basisPointsToPercent(projectAllocationBasisPoints); const projectAllocationAmount = parseFloat(totalAmount) * projectAllocationBasisPoints / TOTAL_BASIS_POINTS; const protocolFeePercent = basisPointsToPercent(protocolFeeBasisPoints); const protocolFeeAmount = parseFloat(totalAmount) * protocolFeeBasisPoints / TOTAL_BASIS_POINTS; const referrerFeePercent = basisPointsToPercent(referralFeeBasisPoints); const referrerFeeAmount = parseFloat(totalAmount) * referralFeeBasisPoints / TOTAL_BASIS_POINTS; const chefFeePercent = basisPointsToPercent(chefFeeBasisPoints); const chefFeeAmount = parseFloat(totalAmount) * chefFeeBasisPoints / TOTAL_BASIS_POINTS; const fees = [{ label: \"Protocol fee\", percentage: protocolFeePercent, amount: protocolFeeAmount, show: !bypassProtocolFee }, { label: \"Referrer fee\", percentage: referrerFeePercent, amount: referrerFeeAmount, show: referrerId }, { label: \"Chef fee\", percentage: chefFeePercent, amount: chefFeeAmount, show: !bypassChefFee && chefFeeBasisPoints }, { label: \"On-Chain Storage\", percentage: \"\", amount: \"<0.01\", show: true }, { label: \"Project allocation\", percentage: projectAllocationPercent, amount: projectAllocationAmount, show: true }]; return <BreakdownSummaryContent style={props.containerStyle || {}}> <A_212 style={props.headerStyle || {}}> <BreakdownTitle> Breakdown</BreakdownTitle> </A_212> <div className={\\`breakdown-details \\${!state.showBreakdown ? \"hidden\" : \"\"}\\`} active={state.showBreakdown}> {fees.map(({ show, amount, label, percentage }) => { return show ? <BreakdownItem key={label}> <A_213> {label} {percentage ? \\`(\\${percentage}%)\\` : \"\"}{\" \"} </A_213> <A_214> <BreakdownAmount>{typeof amount === \"string\" ? amount : amount}</BreakdownAmount> {ftIcon ? <A_215 src={ftIcon} alt=\"ft-icon\" /> : <A_237 />} </A_214> </BreakdownItem> : \"\"; })} </div> </BreakdownSummaryContent>; `, NftImage: ` const props = props; const nft = props.nft ?? { contractId: props.contractId, tokenId: props.tokenId }; const contractId = nft.contractId; const tokenId = nft.tokenId; const className = props.className ?? \"img-fluid\"; const style = props.style; const alt = props.alt; const thumbnail = props.thumbnail; const fallbackUrl = props.fallbackUrl; const loadingUrl = props.loadingUrl ?? \"https://ipfs.near.social/ipfs/bafkreidoxgv2w7kmzurdnmflegkthgzaclgwpiccgztpkfdkfzb4265zuu\"; State.init({ contractId, tokenId, imageUrl: null }); if (contractId !== state.contractId || tokenId !== tokenId) { State.update({ contractId, tokenId, imageUrl: null }); } const nftMetadata = nft.contractMetadata ?? Near.view(contractId, \"nft_metadata\"); const tokenMetadata = nft.tokenMetadata ?? Near.view(contractId, \"nft_token\", { token_id: tokenId }).metadata; let imageUrl = null; if (nftMetadata && tokenMetadata) { let tokenMedia = tokenMetadata.media || \"\"; imageUrl = tokenMedia.startsWith(\"https://\") || tokenMedia.startsWith(\"http://\") || tokenMedia.startsWith(\"data:image\") ? tokenMedia : nftMetadata.base_uri ? \\`\\${nftMetadata.base_uri}/\\${tokenMedia}\\` : tokenMedia.startsWith(\"Qm\") || tokenMedia.startsWith(\"ba\") ? \\`https://ipfs.near.social/ipfs/\\${tokenMedia}\\` : tokenMedia; if (!tokenMedia && tokenMetadata.reference) { if (nftMetadata.base_uri === \"https://arweave.net\" && !tokenMetadata.reference.startsWith(\"https://\")) { const res = fetch(\\`\\${nftMetadata.base_uri}/\\${tokenMetadata.reference}\\`); imageUrl = res.body.media; } else if (tokenMetadata.reference.startsWith(\"https://\") || tokenMetadata.reference.startsWith(\"http://\")) { const res = fetch(tokenMetadata.reference); imageUrl = JSON.parse(res.body).media; } else if (tokenMetadata.reference.startsWith(\"ar://\")) { const res = fetch(\\`\\${\"https://arweave.net\"}/\\${tokenMetadata.reference.split(\"//\")[1]}\\`); imageUrl = JSON.parse(res.body).media; } } if (!imageUrl) { imageUrl = false; } } const rex = /^(?:https?:\\\\/\\\\/)(?:[^\\\\/]+\\\\/ipfs\\\\/)?(Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,})(?:\\\\.[^\\\\/]+)?(\\\\/.*)?$/g; rex.lastIndex = 0; const replaceIpfs = imageUrl => { if (state.oldUrl !== imageUrl && imageUrl) { const match = rex.exec(imageUrl); if (match) { const newImageUrl = \\`https://ipfs.near.social/ipfs/\\${match[1]}\\${match[2] || \"\"}\\`; if (newImageUrl !== imageUrl) { State.update({ oldUrl: imageUrl, imageUrl: newImageUrl }); return; } } } if (state.imageUrl !== false) { State.update({ imageUrl: false }); } }; const thumb = imageUrl => thumbnail && imageUrl && !imageUrl.startsWith(\"data:image/\") ? \\`https://i.near.social/\\${thumbnail}/\\${imageUrl}\\` : imageUrl; const img = state.imageUrl !== null ? state.imageUrl : imageUrl; const src = img !== false ? img : fallbackUrl; return <img className={className} style={style} src={src !== null ? thumb(src) : loadingUrl} alt={alt} onError={() => replaceIpfs(img)} />; `, Image: ` const props = props; const image = props.image; const className = props.className; const style = props.style; const alt = props.alt; const fallbackUrl = props.fallbackUrl; const thumbnail = props.thumbnail; State.init({ image }); if (JSON.stringify(image) !== JSON.stringify(state.image)) { State.update({ image, imageUrl: null }); } function toUrl(image) { return (image.ipfs_cid ? \\`https://ipfs.near.social/ipfs/\\${image.ipfs_cid}\\` : image.url) || fallbackUrl; } const thumb = (imageUrl) => thumbnail && imageUrl && !imageUrl.startsWith(\"data:image/\") ? \\`https://i.near.social/\\${thumbnail}/\\${imageUrl}\\` : imageUrl; return image.nft.contractId && image.nft.tokenId ? <Widget loading=\" \" code={props.alem.componentsCode.NftImage} props={{ ...{ ...{ className, style, alt, nft: image.nft, thumbnail, fallbackUrl }, ...props } }} /> : <img className={className} style={style} src={state.imageUrl ? thumb(state.imageUrl) : thumb(toUrl(image))} alt={alt} onError={() => { if (state.imageUrl !== fallbackUrl) { State.update({ imageUrl: fallbackUrl }); } }} />;`, OverlayTrigger: ` const props = props; const showTimer = 250; const hideTimer = 300; const handleOnMouseEnter = () => { clearTimeout(state.debounce); State.update({ debounce: setTimeout(() => State.update({ show: true }), showTimer) }); }; const handleOnMouseLeave = () => { clearTimeout(state.debounce); State.update({ debounce: setTimeout(() => State.update({ show: false }), hideTimer) }); }; State.init({ show: false }); const overlayClassName = props.overlayClassName ?? \"border m-3 p-3 rounded-4 bg-white shadow\"; const overlayStyle = props.overlayStyle ?? { maxWidth: \"24em\", zIndex: 1070 }; const overlay = <div className={overlayClassName} style={overlayStyle} onMouseEnter={handleOnMouseEnter} onMouseLeave={handleOnMouseLeave}> {props.popup} </div>; return <OverlayTrigger show={state.show} trigger={[\"hover\", \"focus\"]} delay={{ show: showTimer, hide: hideTimer }} placement=\"auto\" overlay={overlay}> <span className=\"d-inline-flex\" onMouseEnter={handleOnMouseEnter} onMouseLeave={handleOnMouseLeave}> {props.children} </span> </OverlayTrigger>; `, A_238: ` const FilterButton = styled.div\\` white-space: nowrap; display: flex; cursor: pointer; gap: 12px; align-items: center; font-size: 14px; font-weight: 500; line-height: 20px; border: 1px solid #292929; padding: 0.5rem 1rem; border-radius: 6px; color: #292929; * { font-weight: 500; }\\`;const FilterIcon = styled.div\\` display: flex; width: 16px; height: 16px; align-items: center; justify-content: center;\\`;const FilterMenu = styled.div\\` position: absolute; background: #fff; top: 140%; right: 0; padding: 8px; display: flex; flex-direction: column; gap: 8px; border-radius: 6px; border: 1px solid rgba(41, 41, 41, 0.36); box-shadow: 0px 12px 20px -4px rgba(123, 123, 123, 0.32), 0px 4px 8px -3px rgba(123, 123, 123, 0.2), 0px 0px 2px 0px rgba(123, 123, 123, 0.36); z-index: 3;\\`;const FilterItem = styled.div\\` cursor: pointer; padding: 8px; display: flex; justify-content: space-between; align-items: center; gap: 12px; white-space: nowrap; transition: all 300ms ease-in-out; &:hover { color: #fff; background: #292929; border-radius: 6px; .count { color: #fff; } } .count { color: #7b7b7b; }\\`;const Screen = styled.div\\` position: fixed; width: 100%; height: 100%; left: 0; top: 0;\\`; const componentProps = props; const [openFilter, setOpenFilter] = useState(false); const { sortList, sortVal, title, handleSortChange, FilterMenuCustomClass, showCount } = componentProps; const menuStyle = componentProps.menuStyle || {}; const buttonStyle = componentProps.buttonStyle || {}; return <> {openFilter && <Screen onClick={() => setOpenFilter(false)} />} <div style={{ position: \"relative\" }} onClick={() => setOpenFilter(!openFilter)}> <FilterButton style={buttonStyle || {}}> {sortVal || title || \"\"} <FilterIcon> <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M8 3.88667L10.1133 6L11.0533 5.06L8 2L4.94 5.06L5.88667 6L8 3.88667ZM8 12.1133L5.88667 10L4.94667 10.94L8 14L11.06 10.94L10.1133 10L8 12.1133Z\" fill=\"#7B7B7B\" /> </svg> </FilterIcon> </FilterButton> {openFilter && <FilterMenu onClick={e => e.stopPropagation()} className={FilterMenuCustomClass || \"\"} style={menuStyle}> {sortList.map(option => <FilterItem key={option.val} onClick={() => { setOpenFilter(false); handleSortChange(option); }}> {option.label} <div className=\"count\">{showCount ? option.count : \"\"}</div> </FilterItem>)} </FilterMenu>} </div> </>; `, ModalDonation: ` const useCart = () => useContext(\"cart-context\"); const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useDonationModal = () => useContext(\"donation-modal\"); const A_204 = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem 2rem; gap: 1.5rem; @media only screen and (max-width: 480px) { padding: 1.5rem 1.125rem; }\\`;const ContentScrollable = styled.div\\` display: flex; flex-direction: column; gap: 1.5rem; overflow-y: scroll; height: 450px;\\`;const A_205 = styled.div\\` font-weight: 500; margin-bottom: 4px;\\`;const Amout = styled.div\\` display: flex; align-items: center; gap: 0.5rem; cursor: pointer; span { font-weight: 600; } div { font-weight: 600; font-size: 1rem; } .usd-amount { color: #7b7b7b; } .toggle-icon { width: 8px; display: flex; margin-left: auto; overflow: hidden; svg { width: 100%; transition: all 300ms ease-in-out; } } img, svg { width: 20px; }\\`;const SvgIcon = styled.svg\\` width: 16px;\\`;const FeesRemoval = styled.div\\` display: flex; flex-direction: column; gap: 1rem; .check { display: flex; align-items: center; flex-wrap: wrap; } .label { margin-right: 4px; margin-left: 8px; } .address { font-weight: 600; gap: 4px; display: flex; align-items: center; color: #292929; transition: all 300ms; &:hover { color: #dd3345; text-decoration: none; } } .profile-image { width: 17px; height: 17px; display: flex !important; } @media only screen and (max-width: 480px) { .address { margin-left: 34px; width: 100%; } }\\`;const ButtonWrapper = styled.div\\` display: flex; margin-top: 4rem; margin-bottom: 0.5rem; gap: 1rem; justify-content: flex-end; @media only screen and (max-width: 480px) { margin-top: 2rem; }\\`;const A_206 = styled.div\\` padding: 8px 0; display: flex; flex-direction: column; border-radius: 8px; border: 1px solid #ebebeb; background: rgba(235, 235, 235, 0.24); transition: all 300ms ease-in-out; &.hidden { max-height: 0; overflow: hidden; opacity: 0; } .project { display: flex; align-items: center; gap: 1rem; padding: 0.5rem 1rem; transition: 300ms ease-in-out; } .profile-image { width: 40px; height: 40px; box-shadow: 0px 0px 1px 0px #a6a6a6 inset; border-radius: 50%; } .info { display: flex; flex-direction: column; .name { font-weight: 600; } .address { color: #7b7b7b; transition: all 300ms; &:hover { text-decoration: none; color: #dd3345; } } }\\`;const ProjectAmount = styled.div\\` margin-left: auto; display: flex; gap: 1rem; align-items: center; div { font-weight: 600; } svg { width: 16px; }\\`; const ConfirmPot = ({ selectedDenomination, bypassProtocolFee, bypassChefFee, updateState, potDetail, potId, referrerId, accountId, amount, openDonationSuccessModal, selectedProjects, donationType, toggleAmount, onClose }) => { const ProfileImg = ({ accountId, profile }) => <ProfileImage accountId={accountId} profile={profile} style={{}} />; const CheckBoxWrapper = ({ id, checked, onClick }) => <CheckBox {...{ id, checked, onClick }} />; const getFeesBasisPoints = (protocolConfig, potDetail) => { if (protocolConfig) { return [protocolConfig.account_id, protocolConfig.basis_points, potDetail.referral_fee_public_round_basis_points]; } else { return [\"\", 0, 0]; } }; const pollForDonationSuccess = ({ projectIds, afterTs }) => { const pollIntervalMs = 1000; const pollId = setInterval(() => { PotSDK.asyncGetDonationsForDonor(potId, accountId).then((alldonations) => { const donations = {}; for (const donation of alldonations) { const { project_id, donated_at_ms, donated_at } = donation; if (projectIds.includes(project_id) && (donated_at_ms || donated_at) > afterTs) { donations[project_id] = { ...donation, potId }; } } if (Object.keys(donations).length === projectIds.length) { clearInterval(pollId); openDonationSuccessModal(donations); } }).catch((err) => { console.log(err); onClose(); }); setTimeout(() => { onClose(); clearInterval(pollId); }, 60000); }, pollIntervalMs); }; const protocolConfigContractId = potDetail.protocol_config_provider.split(\":\")[0]; const protocolConfigViewMethodName = potDetail.protocol_config_provider.split(\":\")[1]; const protocolConfig = protocolConfigContractId && protocolConfigViewMethodName ? Near.view(protocolConfigContractId, protocolConfigViewMethodName, {}) : null; const [protocolFeeRecipientAccount, protocolFeeBasisPoints, referralFeeBasisPoints] = getFeesBasisPoints(protocolConfig, potDetail); const chefFeeBasisPoints = potDetail?.chef_fee_basis_points; const donationAmountIndivisible = (num) => Big(num).mul(new Big(10).pow(selectedDenomination.decimals)); const projectAmount = parseFloat(amount) / Object.keys(selectedProjects).length; const autoProjectAmount = donationAmountIndivisible(projectAmount); const handleDonate = () => { const now = Date.now(); const successArgs = { projectIds: Object.keys(selectedProjects), afterTs: now }; const transactions = []; Object.keys(selectedProjects).forEach((project) => { let amount = 0; if (donationType === \"auto\") { amount = autoProjectAmount; } else { amount = donationAmountIndivisible(selectedProjects[project]); } if (amount) { transactions.push({ contractName: potId, methodName: \"donate\", args: { referrer_id: referrerId, project_id: project, bypass_protocol_fee: bypassProtocolFee, ...(bypassChefFee ? { custom_chef_fee_basis_points: 0 } : {}) }, deposit: amount.toFixed(0), gas: \"300000000000000\" }); } }); Near.call(transactions); pollForDonationSuccess(successArgs); }; return <A_204> <ContentScrollable> <div> <A_205>Total amount</A_205> <Amout onClick={() => updateState({ toggleAmount: !toggleAmount })}> <div>{selectedDenomination.icon ? <img src={selectedDenomination.icon} /> : <A_237 />}</div> <div> {amount} <span>{selectedDenomination.text}</span> </div> {A_236 && <div className=\"usd-amount\">~\\${(A_236 * amount).toFixed(2)}</div>} <div className=\"toggle-icon\"> <svg viewBox=\"0 0 8 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style={{ rotate: !toggleAmount ? \"\" : \"90deg\" }}> <path d=\"M1.70501 0L0.295013 1.41L4.87501 6L0.295013 10.59L1.70501 12L7.70501 6L1.70501 0Z\" fill=\"#7B7B7B\" /> </svg> </div> </Amout> </div> <A_206 className={\\`\\${!toggleAmount ? \"hidden\" : \"\"}\\`}> {Object.keys(selectedProjects).map((project_id) => { const profile = Social.getr(\\`\\${project_id}/profile\\`); return selectedProjects[project_id] > 0 || donationType === \"auto\" ? <div className={\\`project\\`} key={project_id}> <ProfileImg profile={profile} /> <div className=\"info\"> {profile?.name && <div className=\"name\">{_address(profile?.name, 20)}</div>} <a className=\"address\" href={hrefWithParams(\\`?tab=project&projectId=\\${project_id}\\`)} target=\"_blank\"> {_address(project_id, 20)} </a> </div> <ProjectAmount> <div> {\" \"} {donationType === \"auto\" ? projectAmount < 0.01 ? \"<0.01\" : projectAmount.toFixed(2) : selectedProjects[project_id]}{\" \"} </div> <A_237 /> </ProjectAmount> </div> : \"\"; })} </A_206> <Widget loading=\" \" code={props.alem.componentsCode.BreakdownSummary} props={{ ...{ ...{ ...props, referrerId, protocolFeeBasisPoints, referralFeeBasisPoints, bypassChefFee, chef: potDetail?.chef, chefFeeBasisPoints, totalAmount: amount, bypassProtocolFee: bypassProtocolFee, ftIcon: selectedDenomination.icon }, ...props } }} /> <FeesRemoval> <div className=\"check\"> <CheckBoxWrapper id=\"bypassProtocolFeeSelector\" checked={bypassProtocolFee} onClick={(e) => { updateState({ bypassProtocolFee: e.target.checked }); }} /> <div className=\"label\">Remove {protocolFeeBasisPoints / 100 || \"-\"}% protocol fee</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${protocolFeeRecipientAccount}\\`)} className=\"address\" target=\"_blank\"> <ProfileImg accountId={protocolFeeRecipientAccount} /> {protocolFeeRecipientAccount} </a> </div> {potDetail?.chef && chefFeeBasisPoints > 0 && <div className=\"check\"> <CheckBoxWrapper id=\"bypassChefFeeSelector\" checked={bypassChefFee} onClick={(e) => { updateState({ bypassChefFee: e.target.checked }); }} /> <div className=\"label\"> Remove {chefFeeBasisPoints / 100 || \"-\"}% chef fee</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${potDetail?.chef}\\`)} className=\"address\" target=\"_blank\"> <ProfileImg accountId={potDetail?.chef} /> {potDetail?.chef} </a> </div>} </FeesRemoval> </ContentScrollable> <ButtonWrapper> <A_247 className=\"filled\" onClick={handleDonate}> Confirm donation </A_247> </ButtonWrapper> </A_204>; }; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_219 = styled.div\\` display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem 2rem;\\`;const A_220 = styled.div\\` font-weight: 500; margin-bottom: 4px;\\`;const A_221 = styled.div\\` display: flex; align-items: center; gap: 0.5rem; span { font-weight: 600; } div { font-weight: 600; font-size: 1rem; } .usd-amount { color: #7b7b7b; } img, svg { width: 20px; }\\`;const A_222 = styled.svg\\` width: 16px;\\`;const NoteWrapper = styled.div\\` display: flex; cursor: pointer; align-items: center; gap: 8px; svg { width: 14px; } div { font-weight: 500; }\\`;const A_223 = styled.div\\` display: flex; flex-direction: column; gap: 1rem; .check { display: flex; flex-wrap: wrap; align-items: center; } .label { margin-right: 4px; margin-left: 8px; } .address { font-weight: 600; gap: 4px; display: flex; align-items: center; color: #292929; transition: all 300ms; &:hover { color: #dd3345; text-decoration: none; } } .profile-image { width: 17px; height: 17px; display: flex !important; } @media only screen and (max-width: 480px) { .address { margin-left: 34px; width: 100%; } }\\`;const A_224 = styled.div\\` display: flex; margin-top: 2rem; margin-bottom: 0.5rem; justify-content: flex-end;\\`; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const ConfirmDirect = ({ selectedDenomination, bypassProtocolFee, bypassChefFee, donationNote, donationNoteError, addNote, updateState, selectedRound, projectId, referrerId, accountId, amount, openDonationSuccessModal, donationType, onClose }) => { const getFeesBasisPoints = (protocolConfig, potDetail, donationContractConfig) => { if (protocolConfig) { return [protocolConfig.account_id, protocolConfig.basis_points, potDetail.referral_fee_public_round_basis_points]; } else if (donationContractConfig) { return [donationContractConfig.protocol_fee_recipient_account, donationContractConfig.protocol_fee_basis_points, donationContractConfig.referral_fee_basis_points]; } else { return [\"\", 0, 0]; } }; const pollForDonationSuccess = ({ projectId: donatedProject, afterTs, accountId, isPotDonation }) => { const pollIntervalMs = 1000; const totalPollTimeMs = 60000; const pollId = setInterval(() => { (isPotDonation ? PotSDK.asyncGetDonationsForDonor(selectedRound, accountId) : DonateSDK.asyncGetDonationsForDonor(accountId)).then((donations) => { for (const donation of donations) { const { recipient_id, project_id, donated_at_ms, donated_at } = donation; if ((project_id || recipient_id) === donatedProject && (donated_at_ms || donated_at) > afterTs || donated_at > afterTs) { clearInterval(pollId); openDonationSuccessModal({ [donatedProject]: donation }); } } }); setTimeout(() => { onClose(); clearInterval(pollId); }, totalPollTimeMs); }, pollIntervalMs); }; const ProfileImg = ({ accountId }) => <ProfileImage accountId={accountId} style={{}} />; const CheckBoxWrapper = ({ id, checked, onClick }) => <CheckBox {...{ id, checked, onClick }} />; const potDetail = PotSDK.getConfig(selectedRound); const protocolConfigContractId = potDetail ? potDetail.protocol_config_provider.split(\":\")[0] : \"\"; const protocolConfigViewMethodName = potDetail ? potDetail.protocol_config_provider.split(\":\")[1] : \"\"; const protocolConfig = protocolConfigContractId && protocolConfigViewMethodName ? Near.view(protocolConfigContractId, protocolConfigViewMethodName, {}) : null; const donationContractConfig = !potDetail ? DonateSDK.getConfig() || {} : null; const [protocolFeeRecipientAccount, protocolFeeBasisPoints, referralFeeBasisPoints] = getFeesBasisPoints(protocolConfig, potDetail, donationContractConfig); const chefFeeBasisPoints = donationType === \"pot\" ? potDetail?.chef_fee_basis_points : \"\"; const storageBalanceBounds = Near.view(selectedDenomination.id, \"storage_balance_bounds\", {}); const storageBalanceProtocolFeeRecipient = Near.view(selectedDenomination.id, \"storage_balance_of\", { account_id: protocolFeeRecipientAccount }); const storageBalanceReferrer = referrerId ? Near.view(selectedDenomination.id, \"storage_balance_of\", { account_id: referrerId }) : null; const storageBalanceDonationContract = Near.view(selectedDenomination.id, \"storage_balance_of\", { account_id: constants.DONATION_CONTRACT_ID }); const handleDonate = () => { const donationAmountIndivisible = Big(amount).mul(new Big(10).pow(selectedDenomination.decimals)); const args = { referrer_id: referrerId, bypass_protocol_fee: bypassProtocolFee, message: donationNote, ...(bypassChefFee ? { custom_chef_fee_basis_points: 0 } : {}) }; const potId = selectedRound || null; const isPotDonation = potId && donationType === \"pot\"; const now = Date.now(); const successArgs = { projectId, afterTs: now, accountId, isPotDonation }; if (isPotDonation) { args.project_id = projectId; if (bypassChefFee) { args.custom_chef_fee_basis_points = 0; } } else { args.recipient_id = projectId; } const transactions = []; const isFtDonation = selectedDenomination.text !== \"NEAR\"; if (isFtDonation) { const ftId = selectedDenomination.id; let requiredDepositFloat = 0.012; requiredDepositFloat += 0.0001 * args.message.length; transactions.push({ contractName: constants.DONATION_CONTRACT_ID, methodName: \"storage_deposit\", args: {}, deposit: Big(requiredDepositFloat).mul(Big(10).pow(24)), gas: \"100000000000000\" }); const { min, max } = storageBalanceBounds; const storageMaxBig = Big(max); if (!args.bypass_protocol_fee && (!storageBalanceProtocolFeeRecipient || Big(storageBalanceProtocolFeeRecipient.total).lt(storageMaxBig))) { transactions.push({ contractName: ftId, methodName: \"storage_deposit\", args: { account_id: protocolFeeRecipientAccount }, deposit: storageMaxBig.minus(Big(storageBalanceProtocolFeeRecipient || 0)), gas: \"100000000000000\" }); } if (referrerId && (!storageBalanceReferrer || Big(storageBalanceReferrer.total).lt(storageMaxBig))) { transactions.push({ contractName: ftId, methodName: \"storage_deposit\", args: { account_id: referrerId }, deposit: storageMaxBig.minus(Big(storageBalanceReferrer || 0)), gas: \"100000000000000\" }); } if (!storageBalanceDonationContract || Big(storageBalanceDonationContract.total).lt(storageMaxBig)) { transactions.push({ contractName: ftId, methodName: \"storage_deposit\", args: { account_id: constants.DONATION_CONTRACT_ID }, deposit: storageMaxBig.minus(Big(storageBalanceDonationContract || 0)), gas: \"100000000000000\" }); } Near.asyncView(ftId, \"storage_balance_of\", { account_id: projectId }).then((balance) => { if (!balance || Big(balance.total).lt(storageMaxBig)) { transactions.push({ contractName: ftId, methodName: \"storage_deposit\", args: { account_id: projectId }, deposit: storageMaxBig.minus(Big(balance || 0)), gas: \"100000000000000\" }); } transactions.push({ contractName: ftId, methodName: \"ft_transfer_call\", args: { receiver_id: constants.DONATION_CONTRACT_ID, amount: donationAmountIndivisible.toFixed(0), msg: JSON.stringify({ recipient_id: projectId, referrer_id: referrerId || null, bypass_protocol_fee: bypassProtocolFee, message: args.message }) }, deposit: \"1\", gas: \"300000000000000\" }); Near.call(transactions); pollForDonationSuccess(successArgs); }); } else { transactions.push({ contractName: isPotDonation ? potId : constants.DONATION_CONTRACT_ID, methodName: \"donate\", args, deposit: donationAmountIndivisible.toFixed(0), gas: \"300000000000000\" }); Near.call(transactions); pollForDonationSuccess(successArgs); } }; const MAX_NOTE_LENGTH = 60; return <A_219> <div> <A_220>Total amount</A_220> <A_221> <div>{selectedDenomination.icon ? <img src={selectedDenomination.icon} /> : <A_237 />}</div> <div> {amount} <span>{selectedDenomination.text}</span> </div> {A_236 && selectedDenomination.text === \"NEAR\" && <div className=\"usd-amount\">~\\${(A_236 * amount).toFixed(2)}</div>} </A_221> </div> <div> <Widget loading=\" \" code={props.alem.componentsCode.BreakdownSummary} props={{ ...{ ...{ ...props, referrerId, protocolFeeBasisPoints, referralFeeBasisPoints, bypassChefFee, chef: potDetail?.chef, chefFeeBasisPoints, totalAmount: amount, bypassProtocolFee: bypassProtocolFee, ftIcon: selectedDenomination.icon }, ...props } }} /> </div> <A_223> <div className=\"check\"> <CheckBoxWrapper id=\"bypassProtocolFeeSelector\" checked={bypassProtocolFee} onClick={(e) => { updateState({ bypassProtocolFee: e.target.checked }); }} /> <div className=\"label\">Remove {protocolFeeBasisPoints / 100 || \"-\"}% protocol fee</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${protocolFeeRecipientAccount}\\`)} className=\"address\" target=\"_blank\"> <ProfileImg accountId={protocolFeeRecipientAccount} /> {protocolFeeRecipientAccount} </a> </div> {potDetail?.chef && chefFeeBasisPoints > 0 && <div className=\"check\"> <CheckBoxWrapper id=\"bypassChefFeeSelector\" checked={bypassChefFee} onClick={(e) => { updateState({ bypassChefFee: e.target.checked }); }} /> <div className=\"label\"> Remove {chefFeeBasisPoints / 100 || \"-\"}% chef fee</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${potDetail?.chef}\\`)} className=\"address\" target=\"_blank\"> <ProfileImg accountId={potDetail?.chef} /> {potDetail?.chef} </a> </div>} </A_223> {addNote ? <TextArea {...{ label: \"Note\", inputRows: 2, inputStyle: { background: \"#FAFAFA\" }, placeholder: \\`Add an optional note for the project (max \\${MAX_NOTE_LENGTH} characters)\\`, value: donationNote, onChange: (donationNote) => updateState({ donationNote }), validate: () => { if (donationNote.length > MAX_NOTE_LENGTH) { updateState({ donationNoteError: \\`Note must be less than \\${MAX_NOTE_LENGTH} characters\\` }); return; } updateState({ donationNoteError: \"\" }); }, error: donationNoteError }} /> : <NoteWrapper onClick={() => updateState({ addNote: true })}> <svg viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0.249054 13.7509H3.06155L11.3566 5.4559L8.54405 2.6434L0.249054 10.9384V13.7509ZM1.74905 11.5609L8.54405 4.7659L9.23405 5.4559L2.43905 12.2509H1.74905V11.5609Z\" fill=\"#7B7B7B\" /> <path d=\"M11.7766 0.468398C11.4841 0.175898 11.0116 0.175898 10.7191 0.468398L9.34655 1.8409L12.1591 4.6534L13.5316 3.2809C13.8241 2.9884 13.8241 2.5159 13.5316 2.2234L11.7766 0.468398Z\" fill=\"#7B7B7B\" /> </svg> <div>Add Note</div> </NoteWrapper>} <A_224> <A_247 className=\"filled\" onClick={handleDonate}> Confirm donation </A_247> </A_224> </A_219>; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const Form = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem 0;\\`;const A_225 = styled.div\\` display: flex; flex-direction: column; padding: 0 2rem; @media only screen and (max-width: 480px) { padding: 0 1.125rem; }\\`;const A_226 = styled.div\\` font-weight: 500; margin-bottom: 0.5rem; margin-top: 0.5rem;\\`;const CurrentBalance = styled.div\\` display: flex; margin-top: 0.5rem; gap: 0.5rem; flex-wrap: wrap; justify-content: flex-end; .amount-alert { color: #e54141; } .balance { display: flex; gap: 0.5rem; div:last-of-type { color: #7b7b7b; } }\\`;const TotalAmount = styled.div\\` display: flex; gap: 0.5rem; align-items: center; margin-left: auto; .label { color: #7b7b7b; } .amount { font-weight: 600; .usd { color: #7b7b7b; } } @media only screen and (max-width: 480px) { width: 100%; justify-content: space-between; }\\`;const A_227 = styled.div\\` padding: 8px 0; border-top: 1px solid #ebebeb; margin-top: 1.5rem; display: flex; flex-direction: column; height: 238px; overflow-y: scroll; .project { display: flex; align-items: center; gap: 1rem; cursor: pointer; padding: 0.5rem 2rem; transition: 300ms ease-in-out; &:hover, &.selected { background: rgba(235, 235, 235, 0.24); .check { border-color: #dd3345; svg { display: block; } } } } .profile-image { width: 40px; height: 40px; box-shadow: 0px 0px 1px 0px #a6a6a6 inset; border-radius: 50%; } .info { display: flex; flex-direction: column; .name { font-weight: 600; } .address { color: #7b7b7b; transition: all 300ms; &:hover { text-decoration: none; color: #dd3345; } } } .check { margin-left: auto; display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; border: 2px solid #c7c7c7; border-radius: 50%; svg { display: none; width: 12px; } &.selected { border-color: #dd3345; svg { display: block; } } } @media only screen and (max-width: 480px) { .project { padding: 0.5rem 1.125rem; } }\\`;const A_228 = styled.div\\` margin-left: auto; position: relative; display: flex; border-radius: 6px; background: rgb(246, 245, 243); box-shadow: rgb(255, 255, 255) 0px 1px 0px 0px, rgba(41, 41, 41, 0.1) 0px 0px 4px 0px, rgba(41, 41, 41, 0.1) 0px 2px 4px -1px inset, rgba(41, 41, 41, 0.1) 0px 8px 16px -4px inset; input { padding: 10px 16px; padding-right: 46px; text-align: right; width: 120px; background: transparent; border: none; } svg { position: absolute; top: 50%; transform: translateY(-50%); right: 1rem; width: 16px; }\\`;const CustomButton = styled.div\\` display: flex; margin-top: 4rem; margin-bottom: 0.5rem; padding: 0 2rem; gap: 1rem; justify-content: flex-end; @media only screen and (max-width: 480px) { padding: 0px 1.125rem; margin-top: 2rem; }\\`; const FormPot = ({ amount, amountError, DENOMINATION_OPTION, updateState, selectedDenomination, donationType, ftBalance, selectedProjects, selectedRound, projects: _projects, handleAddToCart, potDetail}) => { const donationTypes = [{ label: \"Auto\", info: \"(allocate funds evenly across multiple projects)\", val: \"auto\", disabled: false }, { label: \"Manual\", info: \"(manually specify amount for each project)\", val: \"manual\", disabled: false }]; const isEmpty = (obj) => { return Object.keys(obj).length === 0; }; const projects = _projects ?? []; const projectHegiht = 58; const projectsContaienrHegiht = projects.length > 4 ? 234 : projectHegiht * projects.length; const HandleAmoutChange = (amount) => { amount = amount.replace(/[^0-9.]+/g, \"\"); if (amount === \".\") amount = \"0.\"; updateState({ amount, amountError: \"\" }); if (amount > ftBalance) { updateState({ amountError: \"You don’t have enough balance to complete this transaction.\" }); } else if (parseFloat(amount) < 0.1) { updateState({ amountError: \"Minimum donation is 0.1 NEAR\" }); } }; const handleAddProject = (project) => { const updatedProjects = selectedProjects; if (selectedProjects[project] === \"\") { delete updatedProjects[project]; } else { updatedProjects[project] = \"\"; } updateState({ selectedProjects: updatedProjects }); }; let totalAmountAllocated = 0; Object.values(selectedProjects).forEach((amount) => totalAmountAllocated += parseFloat(amount || 0)); totalAmountAllocated = parseFloat(totalAmountAllocated.toFixed(1)); const handleProjectAmount = (project, amount) => { amount = amount.replace(/[^\\\\d.]/g, \"\"); if (amount === \".\") amount = \"0.\"; const updatedProjects = selectedProjects; updatedProjects[project] = amount; let totalAmount = 0; Object.values(updatedProjects).forEach((amount) => totalAmount += parseFloat(amount)); if (totalAmount > ftBalance && ftBalance !== null) { updateState({ amountError: \"You don’t have enough balance to complete this transaction.\" }); } else if (parseFloat(amount) < 0.1 && parseFloat(amount) !== 0) { updateState({ amountError: \"Minimum donation is 0.1 NEAR\" }); } else { updateState({ amountError: \"\" }); } updateState({ selectedProjects: updatedProjects }); }; const { NADABOT_HUMAN_METHOD, NADABOT_CONTRACT_ID } = constants; const isUserHumanVerified = Near.view(NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, { account_id: context.accountId }); const isDisabled = isEmpty(selectedProjects) || (donationType === \"auto\" ? amountError || parseFloat(amount) === 0 || !amount : totalAmountAllocated > ftBalance || amountError || parseFloat(totalAmountAllocated.toString()) === 0); return <A_242> <A_225> <A_226>How do you want to allocate funds?</A_226> <Checks options={donationTypes} value={donationType} onClick={(val) => { console.log(\"donationType\", val); updateState({ selectedProjects: {}, donationType: val }); }} /> {donationType === \"auto\" && <> <A_226 style={{ marginTop: \"1.5rem\" }}> Amount </A_226> <AmountInput value={amount} donationType={donationType} HandleAmoutChange={HandleAmoutChange} updateState={updateState} denominationOptions={DENOMINATION_OPTION} selectedDenomination={selectedDenomination} /> </>} <A_244> {ftBalance && <div className=\"balance\"> <div> {ftBalance} <span> {selectedDenomination.text} </span> </div> <div>available</div> </div>} {donationType === \"manual\" && <TotalAmount> <div className=\"label\">Total amount allocated</div> <div className=\"amount\"> {totalAmountAllocated} <span>NEAR</span> {A_236 && <span className=\"usd\"> ~\\$ {A_236} </span>} </div> </TotalAmount>} </A_244> {amountError && <Alert error={amountError} />} {isUserHumanVerified !== null && !isUserHumanVerified && <VerifyInfo />} </A_225> <A_227 style={{ height: projectsContaienrHegiht + \"px\" }}> {projects.map(({ project_id }) => { const profile = Social.getr(\\`\\${project_id}/profile\\`); return <div className={\\`project \\${selectedProjects[project_id] === \"\" ? \"selected\" : \"\"}\\`} style={{ cursor: donationType == \"auto\" ? \"pointer\" : \"default\" }} key={project_id} onClick={() => donationType == \"auto\" ? handleAddProject(project_id) : {}}> <ProfileImage profile={profile} style={{}} />{\" \"} <div className=\"info\"> {profile?.name && <div className=\"name\">{_address(profile?.name, 20)}</div>} <a className=\"address\" href={hrefWithParams(\\`?tab=project&projectId=\\${project_id}\\`)} target=\"_blank\"> {_address(project_id, 20)} </a> </div> {donationType === \"manual\" ? <A_228> <input className=\"amount\" type=\"text\" placeholder=\"0.00\" onChange={(e) => handleProjectAmount(project_id, e.target.value)} /> <svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg> </A_228> : <div className=\"check\"> <svg viewBox=\"0 0 12 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_468_92)\"> <path d=\"M1 5.1618L4 8.16197L11.1621 1\" stroke=\"#DD3345\" stroke-width=\"2\" /> </g> <defs> <clipPath id=\"clip0_468_92\"> <rect width=\"12\" height=\"10\" fill=\"white\" /> </clipPath> </defs> </svg> </div>} </div>; })} </A_227> <CustomButton> <A_247 className={\\`filled \\${isDisabled ? \"disabled\" : \"\"}\\`} onClick={() => { if (donationType === \"auto\") updateState({ currentPage: \"confirmPot\" });else { updateState({ currentPage: \"confirmPot\", amount: totalAmountAllocated }); } }}> Proceed to donate </A_247> <A_247 className={\\`outline \\${isDisabled ? \"disabled\" : \"\"}\\`} onClick={() => { const cartItems = Object.entries(selectedProjects).map(([project_id, project_amount]) => ({ id: project_id, amount: project_amount || parseFloat(amount) / Object.keys(selectedProjects).length, token: selectedDenomination, potId: selectedRound, potDetail })); handleAddToCart(cartItems); }}> Add to cart </A_247> </CustomButton> </A_242>;}; const A_229 = styled.div\\` display: flex; flex-direction: column; border-radius: 12px; background: #fff; font-size: 14px; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); overflow: hidden; border-radius: 6px; @media only screen and (max-width: 480px) { top: 0; border-radius: 0; position: fixed; left: 0; width: 100vw; height: 100vh; overflow-y: scroll; display: flex; z-index: 1000; }\\`;const A_230 = styled.div\\` position: relative; display: flex; flex-direction: column; padding: 1.5rem 2rem; gap: 0.5rem; overflow: hidden; background: #f8d3b0; color: white; font-size: 22px; div { color: #3f130b; font-size: 20px; font-weight: 600; } .left-pattern { position: absolute; left: 0; top: 0; width: 30%; transform: translate(-10%, -10%) scaleX(-1); pointer-events: none; } .right-pattern { position: absolute; right: 0; top: 0; width: 30%; transform: translate(10%, -10%); pointer-events: none; } @media only screen and (max-width: 480px) { padding: 1.125rem; }\\`;const A_231 = styled.div\\` display: flex; align-items: center; justify-content: space-between; svg { width: 14px; cursor: pointer; transition: all 300ms ease-in-out; path { fill: #3f130b; } } .close-icon { margin-left: auto; &:hover { rotate: 90deg; } } div { cursor: pointer; display: flex; } .back-arrow:hover svg { transform: translateX(-10px); }\\`; const BannerBg = __props__ => <svg {...__props__} viewBox=\"0 0 145 152\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <rect x=\"157.654\" y=\"-37\" width=\"20\" height=\"161.118\" rx=\"10\" transform=\"rotate(45 157.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"189.654\" y=\"-37\" width=\"20\" height=\"245.972\" rx=\"10\" transform=\"rotate(45 189.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"221.654\" y=\"-37\" width=\"20\" height=\"164.654\" rx=\"10\" transform=\"rotate(45 221.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"125.654\" y=\"-37\" width=\"20\" height=\"177.702\" rx=\"10\" transform=\"rotate(45 125.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"93.6543\" y=\"-37\" width=\"20\" height=\"78.4889\" rx=\"10\" transform=\"rotate(45 93.6543 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> </svg>; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const VerifyInfo = () => <VerifyInfoWrapper> <div className=\"icon\"> <svg width=\"18\" height=\"16\" viewBox=\"0 0 18 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0.75 15.125H17.25L9 0.875L0.75 15.125ZM9.75 12.875H8.25V11.375H9.75V12.875ZM9.75 9.875H8.25V6.875H9.75V9.875Z\" fill=\"#ECC113\" /> </svg> </div> <div className=\"text\"> Your contribution won't be matched unless verified as human before the matching round ends. </div> <a href=\"https://app.nada.bot/\" target=\"_blank\"> Verify you’re human </a> </VerifyInfoWrapper>; const AlertBanner = styled.div\\` display: flex; padding: 0.75rem 1rem; color: #ed464f; gap: 1rem; align-items: center; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; margin-top: 1.5rem; div { font-weight: 500; } .icon { width: 22px; }\\`;const NadabotBanner = styled.div\\` display: flex; align-items: center; padding: 0.75rem 1rem; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; flex-wrap: wrap; margin-top: 1.5rem; .label { display: flex; align-items: center; font-weight: 500; gap: 1rem; img { width: 24px; height: 24px; } } .verify { color: #dd3345; font-weight: 500; margin-left: auto; &:hover { text-decoration: none; } } @media only screen and (max-width: 480px) { flex-direction: column; align-items: flex-start; gap: 0px; .labe { align-items: flex-start; } .verify { margin-left: 40px; } }\\`;const VerifyInfoWrapper = styled.div\\` display: flex; align-items: center; gap: 14px; padding: 1rem; border-radius: 6px; border: 1px solid #ecc113; background: #fbf9c6; box-shadow: 0px 2px 1px 1px rgba(255, 255, 255, 0.8) inset, 0px -2px 4px 0px rgba(15, 15, 15, 0.15) inset; font-size: 14px; color: #3f2209; margin-top: 1.5rem; .icon { width: 17px; display: flex; height: fit-content; svg { width: 100%; } } .text { flex: 1; line-height: 150%; } a { font-weight: 500; color: #dd3345; :hover { text-decoration: none; } } @media only screen and (max-width: 480px) { flex-wrap: wrap; a { width: 100%; text-align: center; } }\\`; const Alert = ({ error}) => <AlertBanner> <div className=\"icon\"> <svg viewBox=\"0 0 22 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11 4.49L18.53 17.5H3.47L11 4.49ZM11 0.5L0 19.5H22L11 0.5ZM12 14.5H10V16.5H12V14.5ZM12 8.5H10V12.5H12V8.5Z\" fill=\"#F6767A\" /> </svg> </div> <div>{error}</div> </AlertBanner>; const A_232 = styled.div\\` position: relative; display: flex; border-radius: 6px; border: 1px solid #dbdbdb; border-bottom: 2px solid #dbdbdb; background: #fff; input { padding: 0.75rem 1rem; flex: 1; background: transparent; border: none; border-radius: 0; &:focus { box-shadow: none; } } .usd-amount { padding-right: 12px; color: #7b7b7b; display: flex; align-items: center; border-right: 1px solid #dbdbdb; }\\`;const DropdownWrapper = styled.div\\` display: flex; span { font-weight: 500; }\\`;const PotDenomination = styled.div\\` display: flex; align-items: center; gap: 4px; padding: 0 1rem; .text { font-weight: 500; }\\`; const A_233 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_234 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_235 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 10px; background: #ffffff; border: 1px solid #d0d5dd; /* box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); */ box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset; border-radius: 4px; color: #101828; width: 100%;\\`;const Placeholder = styled.span\\` color: #a0a3a8;\\`;const scaleOut = styled.keyframes\\` from { transform: scaleY(0); } to { transform: scaleY(1); }\\`;const SelectContent = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; gap: 0.5em; width: 100%; border: 1px solid #d0d5dd; border-radius: 4px; background: #ffffff; z-index: 3 !important;\\`;const Viewport = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; width: 100%;\\`;const Item = styled.button\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 0.5em; width: 100%; cursor: pointer; background: transparent; border: none; transition: background 0.2s ease-in-out; &:nth-child(n + 1) { border-top: 1px solid #d0d5dd; } &:hover { background: #d0d5dd; boder: none; } &:focus { outline: none; }\\`; const Select = (componentProps) => { const label = componentProps.label ?? \"Label\"; const noLabel = componentProps.noLabel ?? false; const placeholder = componentProps.placeholder ?? \"Select an option\"; const value = componentProps.value ?? \"\"; const options = componentProps.options ?? []; const onChange = componentProps.onChange ?? (() => {}); const validate = componentProps.validate ?? (() => {}); const error = componentProps.error ?? \"\"; return <A_233 style={componentProps.containerStyles || {}}> {noLabel ? <></> : <A_234>{label}</A_234>} <Select.Root value={value?.value} onValueChange={(value) => onChange(options.find((option) => option.value === value))}> <Select.Trigger asChild={true}> <A_235 style={componentProps.inputStyles || {}}> {componentProps.iconLeft && componentProps.iconLeft} <Select.Value aria-label={value.value} placeholder={<Placeholder>{placeholder}</Placeholder>} /> {} <Select.Icon> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1 1.5L6 6.5L11 1.5\" stroke=\"currentColor\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg> </Select.Icon> {} </A_235> </Select.Trigger> <Select.Content asChild={true}> <SelectContent> <Select.Viewport asChild={true}> <Viewport> {options.map(({ text, value }) => <Select.Item value={value} asChild={true}> <Item> <Select.ItemText>{text}</Select.ItemText> <Select.ItemIndicator> <svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" /> </svg> </Select.ItemIndicator> </Item> </Select.Item>)} </Viewport> </Select.Viewport> </SelectContent> </Select.Content> </Select.Root> </A_233>;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_237 = (__props__) => <svg {...__props__} style={{ width: 16}} viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg>; const AmountInput = (__props__) => { const Dropdown = ({ selectedDenomination, denominationOptions, updateState }) => <DropdownWrapper> <Select {...{ noLabel: true, placeholder: \"\", options: denominationOptions, value: { text: selectedDenomination.text, value: selectedDenomination.value }, onChange: ({ value }) => { updateState({ selectedDenomination: denominationOptions.find((option) => option.value === value) }); }, containerStyles: { width: \"auto\" }, inputStyles: { border: \"none\", boxShadow: \"none\", width: \"auto\", padding: \"12px 16px\", height: \"100%\", color: \"#292929\" }, iconLeft: selectedDenomination.icon ? <img src={selectedDenomination.icon} style={{ height: \"16px\", width: \"16px\" }} /> : <A_237 /> }} /> </DropdownWrapper>; const { value, HandleAmoutChange, donationType, denominationOptions, selectedDenomination } = __props__; return <A_232> <input type=\"text\" value={value} placeholder=\"0\" onChange={(e) => HandleAmoutChange(e.target.value)} name=\"amount\" /> <div className=\"usd-amount\"> {\" \"} {A_236 && selectedDenomination.value === \"NEAR\" ? \\`~\\$ \\${(A_236 * value).toFixed(2)}\\` : \"\"} </div> {donationType === \"pot\" || denominationOptions.length === 1 ? <PotDenomination> <A_237 /> <div className=\"text\">{denominationOptions[0].text}</div> </PotDenomination> : <Dropdown {...__props__} />} </A_232>;}; const SelectPot = ({ selectedRound, activeRoundsOptions, updateState }) => { const PotSelector = styled.div\\` display: flex; > div:last-of-type { width: 100%; } .custom-menu-style { left: 0; right: auto; } \\`; return <PotSelector> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ ...{ sortVal: activeRoundsOptions ? activeRoundsOptions[selectedRound].label : \"\", showCount: false, sortList: Object.values(activeRoundsOptions), buttonStyle: { border: \"1px solid #dbdbdb\", padding: \"0.75rem 1rem\", borderBottomWidth: \"2px\", borderRadius: \"6px\", justifyContent: \"space-between\" }, menuStyle: { top: \"120%\" }, FilterMenuCustomClass: \"custom-menu-style\", handleSortChange: ({ val }) => {updateState({ selectedRound: val });} }, ...props } }} /> </PotSelector>; }; const A_239 = styled.div\\` display: flex; flex-direction: column; gap: 0.75rem; > div { display: flex; border-radius: 8px; align-items: center; gap: 0.5rem; padding: 1rem; cursor: pointer; border: 1px solid #dbdbdb; color: #7b7b7b; background: white; transition: all 300ms; .text { flex: 1; font-weight: 500; } &.active { box-shadow: 0px 0px 1.4px 2px #fee6e5; color: #dd3345; border-color: #dd3345; span { color: #7b7b7b; } } &.disabled { pointer-events: none; color: #a6a6a6; background: #f6f5f3; span { color: #a6a6a6; } } }\\`;const A_240 = styled.div\\` width: 20px; height: 20px; border: 2px solid #d9d9d9; display: flex; border-radius: 50%; div { width: 10px; height: 10px; background: transparent; border-radius: 50%; margin: auto; } &.active { border-color: #dd3345; div { background: #dd3345; } }\\`; const Checks = ({ options, value, onClick}) => { return <A_239> {options.map((option) => <div key={option.val} onClick={() => onClick(option.val)} className={\\`\\${value === option.val ? \"active\" : \"\"} \\${option.disabled ? \"disabled\" : \"\"}\\`}> <A_240 className={\\`\\${value === option.val ? \"active\" : \"\"}\\`}> <div></div> </A_240> <div className=\"text\"> {option.label} {option.info && <span> {option.info} </span>} {option.disabled && <span> {option.disabledText} </span>} </div> </div>)} </A_239>;}; const A_241 = () => <div className=\"spinner-border text-secondary\" role=\"status\" />; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const A_242 = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem 2rem; @media only screen and (max-width: 480px) { padding: 1.5rem 1.125rem; }\\`;const A_243 = styled.div\\` font-weight: 500; margin-bottom: 0.5rem; margin-top: 0.5rem;\\`;const A_244 = styled.div\\` display: flex; margin-top: 0.5rem; gap: 0.5rem; justify-content: flex-end; .amount-alert { color: #e54141; } .balance { display: flex; gap: 0.5rem; div:last-of-type { color: #7b7b7b; } }\\`;const PotWrapper = styled.div\\` display: flex; flex-direction: column; margin-top: 1.5rem;\\`;const A_245 = styled.div\\` display: flex; > div:last-of-type { width: 100%; }\\`;const A_246 = styled.div\\` border-radius: 6px; border: 1px solid #dbdbdb; border-bottom-width: 2px; background: #fff; padding: 0.75rem 1rem;\\`;const DirectButton = styled.div\\` display: flex; justify-content: flex-end; margin-top: 4rem; margin-bottom: 0.5rem; gap: 1rem; @media only screen and (max-width: 480px) { margin-top: 2rem; }\\`;const A_247 = styled.button\\` padding: 9px 12px; border-radius: 6px; font-weight: 500; font-size: 14px; transition: background 200ms ease; outline: none; border: none; width: fit-content; &.filled { background: #464646; color: white; box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.84) inset, 0px 1px 1px 1px rgba(166, 166, 166, 0.4) inset, 0px 0px 0px 2px rgba(166, 166, 166, 0.4) inset, 0px 1px 2px 0px rgba(15, 15, 15, 0.15), 0px 1px 3px -1px rgba(5, 5, 5, 0.08); &:hover { background: #525252; } &.disabled { color: #a6a6a6; background: var(--Neutral-100, #ebebeb); box-shadow: 0px 0px 0px 1px rgba(15, 15, 15, 0.15) inset; } } &.outline { background: #fff; color: #292929; box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.22) inset, 0px -1px 0px 0px rgba(15, 15, 15, 0.15) inset, 0px 1px 2px -0.5px rgba(5, 5, 5, 0.08); &:hover { background: #f7f7f7; } &.disabled { color: #c7c7c7; background: var(--Neutral-White, #fff); box-shadow: 0px 0px 0px 1px rgba(15, 15, 15, 0.15) inset; } } &:focus { box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.84) inset, 0px 1px 1px 1px rgba(166, 166, 166, 0.3) inset, 0px 0px 0px 2px rgba(166, 166, 166, 0.3) inset, 0px 0px 0px 2px #fff, 0px 0px 0px 4px rgba(0, 0, 0, 0.84); } &.disabled { pointer-events: none; }\\`; const FormDirect = (__props__) => { const { projectId, profile, amount, amountError, denominationOptions, updateState, selectedDenomination, donationType, ftBalance, activeRounds, accountId, selectedRound, handleAddToCart } = __props__; const { NADABOT_HUMAN_METHOD, NADABOT_CONTRACT_ID } = constants; const isUserHumanVerified = Near.view(NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, { account_id: accountId }); const needsToVerify = isUserHumanVerified === false && donationType === \"pot\"; const donationTypes = [{ label: \"Direct donation\", val: \"direct\", disabled: false }, { label: \"Quadratically matched donation\", val: \"pot\", disabled: !activeRounds || activeRounds.length === 0, disabledText: \"(no pots available)\" }]; const activeRoundsOptions = {}; (activeRounds || []).forEach((round) => { activeRoundsOptions[round] = { label: PotSDK.getConfig(round)?.pot_name || round, val: round }; }); const isFtDonation = selectedDenomination.text !== \"NEAR\"; const HandleAmoutChange = (amount) => { amount = amount.replace(/[^0-9.]+/g, \"\"); if (amount === \".\") amount = \"0.\"; updateState({ amount, amountError: \"\" }); if (amount > ftBalance && ftBalance !== null) { updateState({ amountError: \"You don’t have enough balance to complete this transaction.\" }); } else if (!isFtDonation && parseFloat(amount) < 0.1) { updateState({ amountError: \"Minimum donation is 0.1 NEAR\" }); } }; const isLoading = donationType === \"pot\" ? isUserHumanVerified === null || activeRounds === null : false; const isDisabled = amountError || !amount || !accountId; return projectId ? profile === null ? <A_241 /> : <A_242> <A_243>How do you want to donate?</A_243> <Checks options={donationTypes} value={donationType} onClick={(val) => updateState({ donationType: val })} /> {donationType === \"pot\" && <PotWrapper> <A_243>Select Pot</A_243> <SelectPot {...__props__} activeRoundsOptions={activeRoundsOptions} /> </PotWrapper>} <A_243 style={{ marginTop: \"1.5rem\" }}> Amount </A_243> <AmountInput value={amount} donationType={donationType} HandleAmoutChange={HandleAmoutChange} updateState={updateState} denominationOptions={denominationOptions} selectedDenomination={selectedDenomination} /> {ftBalance && <A_244> <div className=\"balance\"> <div> {ftBalance} <span> {selectedDenomination.text} </span> </div> <div>available</div> </div> </A_244>} {amountError && <Alert error={amountError} />} {needsToVerify && !isLoading && <VerifyInfo />} <DirectButton> <A_247 {...{ className: \\`filled \\${isDisabled ? \"disabled\" : \"\"}\\`, onClick: () => updateState({ currentPage: \"confirm\" }) }}> {\" \"} {!accountId ? \"Sign In to Proceed\" : isLoading ? \"Loading...\" : \"Proceed to donate\"}{\" \"} </A_247> <A_247 {...{ className: \\`outline \\${isDisabled ? \"disabled\" : \"\"}\\`, onClick: () => handleAddToCart([{ id: projectId, amount, token: selectedDenomination, potId: donationType === \"pot\" ? selectedRound : null }]) }}> Add to cart </A_247> </DirectButton> </A_242> : \"\";}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const DENOMINATION_OPTIONS = [{ text: \"NEAR\", value: \"NEAR\", decimals: 24 }]; const { donationModalProps, setSuccessfulDonation, setDonationModalProps } = useDonationModal(); const { addItemstoCart } = useCart(); const onClose = () => { setDonationModalProps(null); }; const { potId, referrerId } = props.alem.useParams(); const { projectId, multiple } = donationModalProps || {}; const potDetail = donationModalProps?.potDetail ?? PotSDK.getConfig(potId); const accountId = context.accountId; State.init({ amount: \"\", donationType: multiple ? \"auto\" : \"direct\", showBreakdown: false, bypassProtocolFee: false, bypassChefFee: false, addNote: false, donationNote: \"\", donationNoteError: \"\", allPots: null, intervalId: null, ftBalances: null, selectedDenomination: DENOMINATION_OPTIONS[0], denominationOptions: DENOMINATION_OPTIONS, selectedRound: \"\", currentPage: multiple ? \"formPot\" : \"form\", selectedProjects: {}, toggleAmount: true }); const { donationType, ftBalances, denominationOptions, selectedDenomination, selectedRound, currentPage } = state; const [activeRounds, setActiveRounds] = useState(null); const profile = Social.getr(\\`\\${projectId}/profile\\`); const profileName = profile?.name || projectId; const pages = { form: FormDirect, formPot: FormPot, confirm: ConfirmDirect, confirmPot: ConfirmPot }; const ActivePageComponent = pages[currentPage]; const pots = useCache(() => PotFactorySDK.asyncGetPots().then(pots => { const activePots = pots.map(pot => PotSDK.isRoundActive(pot.id).then(isActive => isActive && pot.id).catch(e => { console.error(\"error checking active round for pot: \" + pot.id, e); })); return Promise.all(activePots); }).catch(e => { console.error(\"error getting pots: \", e); }), \"active-pots\"); useEffect(() => { if (potId && !activeRounds) { setActiveRounds([potId]); State.update({ selectedRound: potId, donationType: multiple ? \"auto\" : \"pot\" }); } else if (!activeRounds?.length && projectId) { if (!pots) setActiveRounds([]); (pots ?? []).forEach((pot, idx) => { if (pot) { PotSDK.asyncGetApplicationByProjectId(pot, projectId).then(application => { if (application.status === \"Approved\") { setActiveRounds(prev => { const prevRounds = prev || []; if (!prevRounds.includes(pot)) { return [...prevRounds, pot]; } }); if (!selectedRound) State.update({ selectedRound: pot }); } else if (pots.length - 1 === idx && !activeRounds) { setActiveRounds(prev => [...(prev || [])]); } }).catch(err => { console.log(err); setActiveRounds(prev => [...(prev || [])]); }); } }); } }, [pots]); useEffect(() => { if (donationType === \"direct\") { asyncFetch(\\`https://near-mainnet.api.pagoda.co/eapi/v1/accounts/\\${accountId}/balances/FT\\`, { headers: { \"Content-Type\": \"application/json\", \"x-api-key\": \"dce81322-81b0-491d-8880-9cfef4c2b3c2\" } }).then(ftBalancesRes => { if (ftBalancesRes) { const ftBalances = ftBalancesRes.body.balances; State.update({ ftBalances: ftBalances, denominationOptions: DENOMINATION_OPTIONS.concat(ftBalances.map(({ amount, contract_account_id, metadata }) => ({ amount, id: contract_account_id, text: metadata.symbol, value: metadata.symbol, icon: metadata.icon, decimals: metadata.decimals })).filter(option => option.text.length < 10)) }); } }).catch(err => console.log(\"fetching Ft balances faild\")); } }, [ftBalances, donationType]); const nearBalanceRes = fetch(\\`https://near-mainnet.api.pagoda.co/eapi/v1/accounts/\\${accountId}/balances/NEAR\\`, { headers: { \"Content-Type\": \"application/json\", \"x-api-key\": \"dce81322-81b0-491d-8880-9cfef4c2b3c2\" } }); const ftBalance = useMemo(() => { if (selectedDenomination.text === \"NEAR\") { const nearBalance = nearBalanceRes?.body?.balance; return nearBalance ? parseFloat(Big(nearBalance.amount).div(Big(10).pow(24)).toFixed(2)) : null; } const balance = denominationOptions.find(option => option.text === selectedDenomination.text); return balance ? parseFloat(Big(balance.amount).div(Big(10).pow(balance.decimals)).toFixed(2)) : null; }, [selectedDenomination, ftBalances, nearBalanceRes]); return <ModalOverlay onOverlayClick={e => { e.stopPropagation(); onClose(); }} contentStyle={{ padding: \"0px\" }}> <A_229> <div> <A_230> <BannerBg className=\"left-pattern\" /> <BannerBg className=\"right-pattern\" /> <A_231> {![\"form\", \"formPot\"].includes(currentPage) && <div className=\"back-arrow\" onClick={() => State.update({ currentPage: multiple ? \"formPot\" : \"form\" })}> <svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M16 7H3.83L9.42 1.41L8 0L0 8L8 16L9.41 14.59L3.83 9H16V7Z\" fill=\"#FCCFCF\" /> </svg> </div>} <svg onClick={() => onClose()} className=\"close-icon\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#FCCFCF\" /> </svg> </A_231> {[\"confirmPot\", \"confirm\"].includes(currentPage) ? <div> Confirm donation</div> : currentPage === \"formPot\" ? <div>Donate to Projects in {potDetail?.pot_name}</div> : <div> Donate to {profileName}</div>} </A_230> </div> <ActivePageComponent {...donationModalProps} {...state} accountId={accountId} potId={potId} referrerId={referrerId} updateState={State.update} ftBalance={ftBalance} activeRounds={activeRounds} DENOMINATION_OPTION={DENOMINATION_OPTIONS} onClose={onClose} potDetail={potDetail} handleAddToCart={items => { addItemstoCart(items); onClose(); }} openDonationSuccessModal={successfulDonation => { setSuccessfulDonation(successfulDonation); }} /> </A_229> </ModalOverlay>; `, A_250: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const routesPath = { CREATE_PROJECT_TAB: \"createproject\", EDIT_PROJECT_TAB: \"editproject\", PROJECTS_LIST_TAB: \"projects\", PROJECT_DETAIL_TAB: \"project\", CART_TAB: \"cart\", FEED_TAB: \"feed\", POTS_TAB: \"pots\", DEPLOY_POT_TAB: \"deploypot\", POT_DETAIL_TAB: \"pot\", DONORS_TAB: \"donors\", PROFILE_TAB: \"profile\", EDIT_PROFILE_TAB: \"editprofile\"}; const CartButton = styled.div\\` padding: \\${__props__ => __props__.numCartItems > 0 ? \"8px 8px 8px 16px\" : \"8px 16px\"}; background: #2e2e2e; border-radius: 6px; cursor: pointer; display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const CartText = styled.div\\` color: white; font-size: 14px; font-weight: 600; line-height: 20px; word-wrap: break-word; text-align: center;\\`;const CartCountContainer = styled.div\\` display: flex; flex-direction: row; justify-content: center; align-items: center; background: #f86b3f; border-radius: 50%; width: 18px; height: 18px; margin-left: 8px;\\`; const useCart = () => useContext(\"cart-context\"); const NavItem = () => { const { cart } = useCart(); const numCartItems = cart ? Object.keys(cart).length : 0; return <RouteLink to={routesPath.CART_TAB}> <CartButton numCartItems={numCartItems} onClick={() => { numCartItems > 0 ? navigate.to(\"cart\") : {}; }}> <CartText>Cart</CartText> {numCartItems > 0 && <CartCountContainer> <CartText style={{ fontSize: \"12px\" }}>{numCartItems}</CartText> </CartCountContainer>} </CartButton> </RouteLink>;}; const navHeightPx = 110;const navHeightPxMobile = 96;const NavContainer = styled.div\\` width: 100%; display: flex; padding: 0 40px; justify-content: start; align-items: center; align-self: stretch; height: \\${navHeightPx}px; background: #ffffff; z-index: 1000; @media screen and (max-width: 768px) { padding: 24px 8px 24px 16px; height: \\${navHeightPxMobile}px; } @media screen and (max-width: 480px) { padding: 24px 8px 24px 0px; } & > a { width: 10rem; }\\`;const NavLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const NavRight = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const NavRightMobile = styled.div\\` display: none; @media screen and (max-width: 768px) { display: flex; flex-direction: row; align-items: center; justify-content: flex-end; gap: 16px; padding-right: 16px; }\\`;const NavLogo = styled.div\\` a { display: flex; gap: 7px; align-items: baseline; text-align: center; color: #2e2e2e; font-size: 23.95px; font-weight: 700; line-height: 23.95px; word-wrap: break-word; margin-right: 48px; text-decoration: none; @media screen and (max-width: 480px) { font-size: 20px; margin-right: 1rem; } :hover { text-decoration: none; } img { height: 1em; } }\\`;const NavTabs = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center; @media screen and (max-width: 768px) { display: none; }\\`;const NavTab = styled.div\\` a { margin-right: 32px; cursor: \\${(__props__) => __props__.disabled ? \"not-allowed\" : \"pointer\"}; color: \\${(__props__) => __props__.selected ? \"#2E2E2E\" : \"#7B7B7B\"}; font-size: 14px; font-weight: \\${(__props__) => __props__.selected ? 500 : 400}; line-height: 16px; word-wrap: break-word; text-decoration: none; position: relative; :not(:last-child) { margin-right: 32px; } :hover { text-decoration: none; } }\\`;const A_248 = styled.div\\` width: 100%; background: #dd3345; display: flex; flex-direction: row; justify-content: center; align-items: center; padding: 8px 0;\\`;const A_249 = styled.div\\` text-align: center; color: white; font-size: 16px; font-weight: 600; margin-left: 8px; @media screen and (max-width: 768px) { font-size: 12px; margin-left: 4px; }\\`;const BannerLinkContainer = styled.a\\` display: flex; cursor: pointer; text-align: center; font-weight: bold; color: white; font-size: 14px; line-height: 21px; margin-left: 16px; gap: 8px; &:hover { text-decoration: none; } @media screen and (max-width: 768px) { font-size: 12px; margin-left: 8px; gap: 4px; }\\`;const BannerLinkSvg = styled.svg\\` width: 20px; height: 20px; fill: none; transition: transform 0.2s ease; &:hover { transform: rotate(45deg); } @media screen and (max-width: 768px) { width: 16px; height: 16px; }\\`;const BannerAlertSvg = styled.svg\\` width: 18px; @media screen and (max-width: 768px) { width: 14px; }\\`;const NavMenu = styled.div\\` display: none; background: white; padding: 24px; width: 100%; gap: 16px; @media screen and (max-width: 768px) { display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; }\\`;const NavMenuItem = styled.a\\` color: \\${(__props__) => __props__.selected ? \"#2E2E2E\" : \"#7B7B7B\"}; font-size: 14px; font-weight: \\${(__props__) => __props__.selected ? 500 : 400}; line-height: 20px; word-wrap: break-word; cursor: pointer;\\`; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const navigate = { to: (route, params) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } if (routeContext.routes.includes(route)) { routeContext.updateRouteParameters({ ...routeContext, activeRoute: route, routeParams: params || {} }); } }, back: () => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } const updatedHistory = routeContext.history; if (updatedHistory) { updatedHistory.pop(); const routeProps = updatedHistory.at(-1); if (routeProps.route) { routeContext.updateRouteParameters({ ...routeContext, history: updatedHistory, activeRoute: routeProps.route, routeParams: routeProps.routeParams }); } } }}; const RouteLink = ({ to, href, target, params, label, className, style, onClick, children}) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"RouteLink component is being used without Router on top of it.\"); } const onClickHandler = () => { if (onClick) { onClick(); } if (routeContext.routeType === \"ContentBased\") { navigate.to(to, params); } }; if (routeContext.routeType === \"URLBased\") { let strParams = \"\"; if (params) { Object.keys(params).forEach(paramKey => { strParams += \\`&\\${paramKey}=\\${params[paramKey]}\\`; }); } const Link = styled(\"Link\")\\`\\`; return <Link onClick={onClickHandler} className={className} style={{ cursor: \"pointer\", textDecoration: \"none\", ...style }} target={target} href={href ? href : \\`?\\${routeContext.routeParameterName || \"path\"}=\\${to}\\${strParams}\\`}> {label || children} </Link>; } return <a style={{ cursor: \"pointer\", textDecoration: \"none\", ...style }} className={className} onClick={onClickHandler}> {label || children} </a>;}; const [isNavMenuOpen, setIsNavMenuOpen] = useState(false); const params = props.alem.useParams(); const tabOptions = [{ href: null, newTab: null, text: \"Projects\", link: \"projects\", disabled: false }, { href: null, newTab: null, text: \"Feed\", link: \"feed\", disabled: false }, { href: null, newTab: null, text: \"Pots\", link: \"pots\", disabled: false }, { href: null, newTab: null, text: \"Donors\", link: \"donors\", disabled: false }]; return <> {false && <A_248> <BannerAlertSvg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"white\" aria-hidden=\"true\"> <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z\"></path> </BannerAlertSvg> <A_249>This app is in beta. It has not been audited.</A_249> <BannerLinkContainer href=\"https://docs.potlock.io/general-information/beta-phase\" target=\"_blank\"> <span>Learn more</span> <BannerLinkSvg viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"w-[18px] group-hover:rotate-[45deg] transition-all\"> <path d=\"M11.6652 6.77894C11.0834 6.78279 10.5015 6.78574 9.91929 6.78777C9.06125 6.78766 8.20376 6.79135 7.34566 6.78145C6.762 6.77478 6.29535 6.33298 6.30266 5.81732C6.31009 5.32123 6.77706 4.88706 7.32973 4.89083C9.53277 4.89897 11.7351 4.91291 13.9368 4.93265C14.6025 4.93925 14.9748 5.32235 14.9826 6.0022C15.0022 8.19227 15.0157 10.3823 15.0231 12.5723C15.0251 13.2043 14.6477 13.6102 14.0912 13.6135C13.5527 13.6152 13.1403 13.1552 13.1372 12.5298C13.1307 11.2364 13.133 9.9431 13.1287 8.64975C13.1284 8.51553 13.113 8.38013 13.0963 8.12137L12.7089 8.50873C10.6829 10.5347 8.64711 12.5508 6.63972 14.5954C6.22161 15.0212 5.62148 14.9861 5.28149 14.6461C4.88466 14.2493 4.90002 13.7158 5.32463 13.2846C7.35705 11.2478 9.39203 9.21284 11.4295 7.17969L11.7105 6.89876L11.6652 6.77894Z\" fill=\"currentColor\"></path> </BannerLinkSvg> </BannerLinkContainer> </A_248>} <NavContainer> <NavLeft> <NavLogo> <RouteLink to={routesPath.PROJECTS_LIST_TAB}> <> <img src=\"https://ipfs.near.social/ipfs/bafkreiafms2jag3gjbypfceafz2uvs66o25qc7m6u6hkxfyrzfoeyvj7ru\" alt=\"logo\" /> POTLOCK </> </RouteLink> </NavLogo> </NavLeft> <NavRight> <NavTabs> {(tabOptions ?? []).map(tab => { return <NavTab disabled={tab.disabled} selected={tab.link === params.tab}> <RouteLink href={tab.href} to={tab.link} target={tab.newTab ? \"_blank\" : \"\"} onClick={e => { if (tab.disabled) e.preventDefault(); }}> {tab.text} </RouteLink> </NavTab>; })} <NavItem /> </NavTabs> </NavRight> <NavRightMobile> <NavItem /> <NavTab onClick={() => setIsNavMenuOpen(!isNavMenuOpen)}> <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\"> <path d=\"M3 18H21V16H3V18ZM3 13H21V11H3V13ZM3 6V8H21V6H3Z\" fill=\"#7B7B7B\" /> </svg> </NavTab> </NavRightMobile> </NavContainer> {isNavMenuOpen && <NavMenu> {tabOptions.map(tab => { return <NavMenuItem href={hrefWithParams(\\`?tab=\\${tab.link}\\`)} onClick={e => { if (tab.disabled) e.preventDefault(); }} selected={props.tab === tab.link}> {tab.text} {tab.disabled && \" (Coming Soon)\"} </NavMenuItem>; })} </NavMenu>} </>; `, A_253: ` const ReferrerIcon = __props__ => <svg {...__props__} viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M12.375 9.5625C14.6925 7.455 16.875 5.4825 16.875 3.7875C16.875 2.4 15.7875 1.3125 14.4 1.3125C13.62 1.3125 12.8625 1.68 12.375 2.25C11.88 1.68 11.13 1.3125 10.35 1.3125C8.9625 1.3125 7.875 2.4 7.875 3.7875C7.875 5.4825 10.0575 7.455 12.375 9.5625ZM10.35 2.8125C10.68 2.8125 11.0175 2.97 11.235 3.225L12.375 4.5675L13.515 3.225C13.7325 2.97 14.07 2.8125 14.4 2.8125C14.955 2.8125 15.375 3.2325 15.375 3.7875C15.375 4.6275 13.845 6.165 12.375 7.53C10.905 6.165 9.375 4.62 9.375 3.7875C9.375 3.2325 9.795 2.8125 10.35 2.8125Z\" fill=\"#7B7B7B\" /> <path d=\"M14.625 11.8125H13.125C13.125 10.9125 12.5625 10.1025 11.7225 9.7875L7.1025 8.0625H1.125V16.3125H5.625V15.2325L10.875 16.6875L16.875 14.8125V14.0625C16.875 12.8175 15.87 11.8125 14.625 11.8125ZM2.625 14.8125V9.5625H4.125V14.8125H2.625ZM10.8525 15.12L5.625 13.6725V9.5625H6.8325L11.1975 11.19C11.4525 11.2875 11.625 11.535 11.625 11.8125C11.625 11.8125 10.1325 11.775 9.9 11.7L8.115 11.1075L7.6425 12.5325L9.4275 13.125C9.81 13.2525 10.2075 13.32 10.6125 13.32H14.625C14.9175 13.32 15.18 13.4925 15.3 13.74L10.8525 15.12Z\" fill=\"#7B7B7B\" /> </svg>; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_251 = styled.div\\` height: 46px; width: 100%; margin-top: 1rem; @media screen and (max-width: 480px) { height: 72px; }\\`;const A_252 = styled.div\\` position: fixed; z-index: 999; bottom: 0; left: 0; background: #faa7a8; width: 100%; height: 46px; color: white; overflow: hidden; font-size: 14px; > div { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; background-blend-mode: overlay, normal; background: radial-gradient(circle, #fef3f2 0%, #feefe0 55.1%, #f8d3b0 100%); } .text { font-size: 14px; font-weight: 500; color: #192c07; line-height: 150%; display: flex; gap: 1rem; .link { display: flex; align-items: center; font-weight: 500; gap: 8px; color: #dd3345; text-decoration: none; svg { height: 15px; path { fill: #f6767a; transition: fill 300ms ease-in-out; } } :hover svg path { fill: #dd3345; } } } @media screen and (max-width: 480px) { height: 72px; .text { flex-direction: column; align-items: center; gap: 4px; } }\\`; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useRoutes = () => { const contextData = useContext(\"alemRoutes\"); if (!contextData) { console.error(\"useRoutes: You need to call \\`RouterProvider()\\` first.\"); } const data = { routesInitialized: contextData.routesInitialized, activeRoute: contextData.activeRoute, routeParameterName: contextData.routeParameterName, routes: contextData.routes, routeType: contextData.routeType, routeParams: contextData.routeParams, history: contextData.history }; return data;}; const getLocation = () => { const routes = useRoutes(); return { pathname: routes.activeRoute, routes: routes.routes, isRoutesReady: routes.routes && routes.routes.length > 0 };}; const [activeRounds, setActiveRounds] = useState([]); const { pathname } = getLocation(); const showLiveBanner = !(pathname === \"pots\" || pathname === \"pot\"); const pots = PotFactorySDK.getPots(); const now = Date.now(); useEffect(() => { if (pots) { pots.forEach(pot => { PotSDK.asyncGetConfig(pot.id).then(potConfig => { const { public_round_start_ms, public_round_end_ms } = potConfig; if (public_round_start_ms < now && public_round_end_ms > now) { setActiveRounds(prevActiveRounds => [...prevActiveRounds, { ...potConfig, pot_id: pot.id }]); } }).catch(e => { console.error(\"error getting pot detail: \", e); }); }); } }, [pots]); const isSingleRound = activeRounds.length === 1; const limit = isSingleRound ? 20 : 10; const potName = activeRounds[0].pot_name.length > limit ? activeRounds[0].pot_name.slice(0, limit).trim() + \"...\" : activeRounds[0].pot_name; const textForOneRound = \\`\\${potName} round is live\\`; const textForMultipleRounds = \\`Pot round is live for \\${potName} and +\\${activeRounds.length - 1} More\\`; if (!activeRounds.length) return \"\"; return showLiveBanner ? <A_251> <A_252> <div> <div className=\"text\"> {isSingleRound ? textForOneRound : textForMultipleRounds} <a href={hrefWithParams(isSingleRound ? \\`?tab=pot&potId=\\${activeRounds[0].pot_id}\\` : \\`?tab=pots\\`)} className=\"link\"> <ReferrerIcon /> Donate now </a> </div> </div> </A_252> </A_251> : \"\"; `, App: ` const DonationModalProvider = () => { const { setDefaultData, updateData, getSelf } = createContext(\"donation-modal\"); setDefaultData({ successfulDonation: null, donationModalProps: null, setSuccessfulDonation: successfulDonation => { updateData({ successfulDonation, donationModalProps: null }); }, setDonationModalProps: donationModalProps => { updateData({ donationModalProps }); } });}; const Spinner = () => <div className=\"spinner-border\" role=\"status\"> <span className=\"visually-hidden\">Loading...</span> </div>; const DEFAULT_CART = {};const CART_KEY = \"cart\";const getCart = () => JSON.parse(Storage.get(CART_KEY)) || DEFAULT_CART;const CartProvider = () => { const { setDefaultData, updateData, getSelf } = createContext(\"cart-context\"); const updateCart = cart => { Storage.set(CART_KEY, JSON.stringify(cart)); updateData({ cart }); }; setDefaultData({ cart: getCart(), addItemstoCart: items => { const { cart } = getSelf(); items.forEach(item => { cart[item.id] = item; }); updateCart(cart); }, clearCart: () => { updateData({ cart: DEFAULT_CART }); updateCart(DEFAULT_CART); }, removeItemsFromCart: idsToRemove => { const { cart } = getSelf(); idsToRemove.forEach(id => { if (cart.hasOwnProperty(id)) { delete cart[id]; } }); updateCart(cart); } });}; const HeaderContainer = styled.div\\` width: 100%; padding: 80px 64px; @media (max-width: 768px) { padding: 36px 24px; }\\`;const HeaderContent = styled.div\\` display: flex; flex-direction: column;\\`;const HeaderTitle = styled.div\\` color: #2e2e2e; font-size: 88px; font-weight: 500; word-wrap: break-word; position: relative; text-align: center; z-index: 1; position: relative; font-family: \"Lora\"; @media (max-width: 768px) { font-size: 48px; }\\`;const HeaderDescription = styled.div\\` color: #2e2e2e; font-size: 32px; font-weight: 400; word-wrap: break-word; max-width: 866px; margin-top: 32px; @media (max-width: 768px) { font-size: 24px; text-align: center; }\\`;const A_17 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 32px; margin-top: 32px;\\`;const Underline = styled.div\\` position: absolute; top: 44px; left: -40px; z-index: -1; @media (max-width: 768px) { top: 30px; left: -30px; }\\`; const Header = (__props__) => { const containerStyle = __props__.containerStyle ?? {}; const { tab } = props.alem.useParams(); const showStats = !tab || tab == \"projects\"; return <HeaderContainer style={containerStyle}> <HeaderContent style={{ alignItems: __props__.centered ? \"center\" : \"flex-start\" }}> <HeaderTitle> {__props__.title1} <Underline> <svg width=\"340\" height=\"42\" viewBox=\"0 0 340 42\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7.29967 39C-14.0566 35.9491 49.9788 32.436 71.4774 30.6444C151.734 23.9564 232.915 20.5161 312.9 15\" stroke=\"#DD3345\" stroke-width=\"5\" stroke-linecap=\"round\" /> <path d=\"M31.2997 27C9.94337 23.9491 73.9788 20.436 95.4774 18.6444C175.734 11.9564 256.915 8.51608 336.9 3\" stroke=\"#DD3345\" stroke-width=\"5\" stroke-linecap=\"round\" /> </svg> </Underline> </HeaderTitle> {__props__.title2 && <HeaderTitle>{__props__.title2}</HeaderTitle>} <HeaderDescription style={{ textAlign: __props__.centered ? \"center\" : \"start\" }}> {__props__.description} </HeaderDescription> </HeaderContent> {__props__.children && __props__.children} <A_17> {__props__.buttonPrimary && __props__.buttonPrimary} {__props__.buttonSecondary && __props__.buttonSecondary} </A_17> {showStats && <Widget loading=\" \" code={props.alem.componentsCode.DonationStats} props={{ ...{ ...props } }} />} </HeaderContainer>; }; const CreateProject = () => { const { tab } = props.alem.useParams(); const edit = tab === \"editproject\"; return <> <Header {...{ title1: edit ? \"Edit your project\" : \"Create new project\", description: \\`\\${edit ? \"Update your \" : \"Create a \"} profile for your impact project to receive direct donations, qualify for funding rounds, join NEAR's accelerator, and get discovered across social platforms.\\`, centered: false, containerStyle: { background: \"#FEF6EE\" } }} /> <Widget loading=\" \" code={props.alem.componentsCode.CreateForm} props={{ ...{ edit: edit, ...props } }} /> </>; }; const svgContent = \\`<svg width=\"1320\" height=\"332\" viewBox=\"0 0 1320 332\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"1320\" height=\"1\" fill=\"#FEF6EE\"/><mask id=\"mask0_11854_22101\" style=\"mask-type:alpha\" maskUnits=\"userSpaceOnUse\" x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\"><rect x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\" fill=\"url(#paint0_radial_11854_22101)\"/></mask><g mask=\"url(#mask0_11854_22101)\"><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" fill=\"#FEF6EE\"/><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" fill=\"#FEF6EE\"/><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" fill=\"#F8D3B0\"/><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" fill=\"#F8D3B0\"/><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" fill=\"#F8D3B0\"/><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" fill=\"#F8D3B0\"/><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" fill=\"#F8D3B0\"/><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" fill=\"#F8D3B0\"/><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" fill=\"#F8D3B0\"/><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" fill=\"#F8D3B0\"/><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" fill=\"#FEF6EE\"/><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" fill=\"#FEF6EE\"/><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" fill=\"#FEF6EE\"/><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" fill=\"#F8D3B0\"/><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" fill=\"#F8D3B0\"/><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" fill=\"#F8D3B0\"/><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" fill=\"#F8D3B0\"/><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" fill=\"#F8D3B0\"/><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" fill=\"#F8D3B0\"/><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" fill=\"#F8D3B0\"/><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" fill=\"#F8D3B0\"/><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" stroke=\"#F4B37D\"/></g></g><defs><radialGradient id=\"paint0_radial_11854_22101\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(660 -158.5) rotate(90) scale(724.5 1018.83)\"><stop stop-color=\"#FCE9D5\"/><stop offset=\"0.855072\" stop-color=\"#FEF6EE\" stop-opacity=\"0\"/></radialGradient></defs></svg>\\`;const HomeBannerStyle = { backgroundImage: \\`url(\"data:image/svg+xml;charset=utf-8,\\${encodeURIComponent(svgContent)}\")\\`, backgroundSize: \"cover\", backgroundRepeat: \"no-repeat\", backgroundColor: \"#FEF6EE\"}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_32 = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; > div:last-of-type { padding: 0px 175px; } @media only screen and (max-width: 992px) { > div:last-of-type { padding: 0px 20px; } }\\`;const A_33 = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; gap: 24px;\\`;const A_34 = styled.div\\` color: #292929; font-size: 60px; font-weight: 400; line-height: 72px; word-wrap: break-word; font-family: Lora;\\`;const A_35 = styled.div\\` display: flex; flex-direction: column; position: relative; width: 100%; justify-content: center; min-height: 400px; overflow: hidden; .background { position: absolute; pointer-events: none; height: 100%; left: 0; top: 0; } .content { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: center; padding: 64px 175px; } .sub-title { letter-spacing: 1.12px; font-weight: 500; font-size: 14px; margin-top: 0; margin-bottom: 24px; text-transform: uppercase; } .title { letter-spacing: -0.4px; font-weight: 500; font-size: 40px; font-family: \"Lora\"; margin: 0; } .info { display: flex; align-items: center; gap: 8px; font-size: 14px; margin-top: 24px; > svg { height: 1em; } } @media only screen and (max-width: 992px) { .content { padding: 64px 20px; } .title { font-size: 36px; } .btns { flex-direction: column; gap: 1rem; margin-top: 24px; } .line-break { display: none; } } @media only screen and (max-width: 480px) { .btns a { width: 100%; padding: 12px 0; } }\\`; const DeployPot = (__props__) => { return __props__.deploymentSuccess || state.deploymentSuccess ? <A_33> <A_34>Deployment Successful!</A_34> <Button {...{ type: \"primary\", text: \"View all pots\", style: __props__.style || {}, href: hrefWithParams(\\`?tab=pots\\`) }} /> </A_33> : <A_32> <A_35 style={{ ...HomeBannerStyle }}> <div className=\"content\"> <h3 className=\"sub-title\">Deploy pot</h3> <h1 className=\"title\"> Deploy a Quadratic <br className=\"line-break\" /> Funding Round </h1> <div className=\"info\"> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6.3335 3.66732H7.66683V5.00065H6.3335V3.66732ZM6.3335 6.33398H7.66683V10.334H6.3335V6.33398ZM7.00016 0.333984C3.32016 0.333984 0.333496 3.32065 0.333496 7.00065C0.333496 10.6807 3.32016 13.6673 7.00016 13.6673C10.6802 13.6673 13.6668 10.6807 13.6668 7.00065C13.6668 3.32065 10.6802 0.333984 7.00016 0.333984ZM7.00016 12.334C4.06016 12.334 1.66683 9.94065 1.66683 7.00065C1.66683 4.06065 4.06016 1.66732 7.00016 1.66732C9.94016 1.66732 12.3335 4.06065 12.3335 7.00065C12.3335 9.94065 9.94016 12.334 7.00016 12.334Z\" fill=\"#7B7B7B\" /> </svg> <div>Know More about Quadratic Funding</div> </div> </div> </A_35> <Widget loading=\" \" code={props.alem.componentsCode.ConfigForm} props={{ ...{ ...props } }} /> </A_32>; }; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const FeedPage = () => { const registrations = ListsSDK.getRegistrations() || []; const registrantIds = registrations.filter(reg => reg.status === \"Approved\").map(reg => reg.registrant_id); const Container = styled.div\\` padding: 24px 64px; @media screen and (max-width: 768px) { padding: 24px 16px; } \\`; return <Container> <Feed key=\"feed\" accounts={registrantIds} /> </Container>;}; const EditProfile = () => { return <div style={{ paddingTop: \"24px\" }}> <Widget src=\"mob.near/widget/ProfileEditor\" /> </div>;}; const A_152 = styled.div\\` display: flex; justify-content: space-between; align-items: center; padding: 68px 105px; border-radius: 12px; background: #f6f5f3; .text { font-family: \"Lora\"; max-width: 290px; font-size: 22px; font-style: italic; font-weight: 500; color: #292929; } img { width: 60%; } @media screen and (max-width: 768px) { flex-direction: column-reverse; padding: 24px 16px; .text { font-size: 16px; } img { width: 100%; } }\\`; const MergedIndexFeed = (compProps) => { if (!compProps.index) { return \"props.index is not defined\"; } const indices = JSON.parse(JSON.stringify(Array.isArray(compProps.index) ? compProps.index : [compProps.index])); const filter = compProps.filter; const renderItem = compProps.renderItem ?? ((item) => <div key={JSON.stringify(item)}> #{item.blockHeight}: {JSON.stringify(item)} </div>); const cachedRenderItem = (item, i) => { const key = JSON.stringify(item); if (!(key in state.cachedItems)) { state.cachedItems[key] = renderItem(item, i); } return state.cachedItems[key]; }; const initialRenderLimit = compProps.initialRenderLimit ?? 10; const addDisplayCount = compProps.nextLimit ?? initialRenderLimit; const reverse = !!compProps.reverse; const computeFetchFrom = (items, limit, desc) => { if (!items || items.length < limit) { return false; } const blockHeight = items[items.length - 1].blockHeight; return desc ? blockHeight - 1 : blockHeight + 1; }; const mergeItems = (iIndex, oldItems, newItems, desc) => { const index = indices[iIndex]; const items = [...new Set([...newItems.map((item) => ({ ...item, action: index.action, key: index.key, index: iIndex })), ...oldItems].map((i) => JSON.stringify(i)))].map((i) => JSON.parse(i)); items.sort((a, b) => a.blockHeight - b.blockHeight); if (desc) { items.reverse(); } return items; }; const jIndices = JSON.stringify(indices); if (jIndices !== state.jIndices) { State.update({ jIndices, feeds: indices.map(() => ({})), items: [], displayCount: initialRenderLimit, cachedItems: {} }); } let stateChanged = false; for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; let feedChanged = false; index.options = index.options || {}; index.options.limit = Math.min(Math.max(initialRenderLimit + addDisplayCount * 2, index.options.limit), 100); const desc = index.options.order === \"desc\"; const initialItems = Social.index(index.action, index.key, index.options, index.cacheOptions); if (initialItems === null) { continue; } const jInitialItems = JSON.stringify(initialItems); const nextFetchFrom = computeFetchFrom(initialItems, index.options.limit, desc); if (feed.jInitialItems !== jInitialItems) { feed.jInitialItems = jInitialItems; feedChanged = true; if (nextFetchFrom !== feed.initialNextFetchFrom) { feed.fetchFrom = false; feed.items = mergeItems(iIndex, [], initialItems, desc); feed.initialNextFetchFrom = nextFetchFrom; feed.nextFetchFrom = nextFetchFrom; } else { feed.items = mergeItems(iIndex, feed.items, initialItems, desc); } } feed.usedCount = 0; if (feedChanged) { state.feeds[iIndex] = feed; stateChanged = true; } } const filteredItems = []; while (filteredItems.length < state.displayCount) { let bestItem = null; for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; const desc = index.options.order === \"desc\"; if (!feed.items) { continue; } const item = feed.items[feed.usedCount]; if (!item) { continue; } if (bestItem === null || (desc ? item.blockHeight > bestItem.blockHeight : item.blockHeight < bestItem.blockHeight)) { bestItem = item; } } if (!bestItem) { break; } state.feeds[bestItem.index].usedCount++; if (filter) { if (filter.ignore) { if (bestItem.accountId in filter.ignore) { continue; } } if (filter.require) { if (!(bestItem.accountId in filter.require)) { continue; } } } filteredItems.push(bestItem); } for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; const desc = index.options.order === \"desc\"; let feedChanged = false; if ((feed.items.length || 0) - feed.usedCount < addDisplayCount * 2 && !feed.fetchFrom && feed.nextFetchFrom && feed.nextFetchFrom !== feed.fetchFrom) { feed.fetchFrom = feed.nextFetchFrom; feedChanged = true; } if (feed.fetchFrom) { const limit = addDisplayCount; const newItems = Social.index(index.action, index.key, Object.assign({}, index.options, { from: feed.fetchFrom, subscribe: undefined, limit })); if (newItems !== null) { feed.items = mergeItems(iIndex, feed.items, newItems, desc); feed.fetchFrom = false; feed.nextFetchFrom = computeFetchFrom(newItems, limit, desc); feedChanged = true; } } if (feedChanged) { state.feeds[iIndex] = feed; stateChanged = true; } } if (stateChanged) { State.update(); } const makeMoreItems = () => { State.update({ displayCount: state.displayCount + addDisplayCount }); }; const loader = <div className=\"loader\" key={\"loader\"}> <span className=\"spinner-grow spinner-grow-sm me-1\" role=\"status\" aria-hidden=\"true\" /> Loading ... </div>; const fetchMore = compProps.manual && (state.feeds.some((f) => !!f.fetchFrom) && filteredItems.length < state.displayCount ? loader : state.displayCount < filteredItems.length && <div key={\"loader more\"}> <a href=\"javascript:void\" onClick={(e) => makeMoreItems()}> {compProps.loadMoreText ?? \"Load more...\"} </a> </div>); const items = filteredItems ? filteredItems.slice(0, state.displayCount) : []; if (reverse) { items.reverse(); } const renderedItems = items.length === 0 ? <A_152> <div className=\"text\">This {compProps.tab === \"profile\" ? \"user\" : \"project\"} has not posted yet.</div> <img src=\"https://ipfs.near.social/ipfs/bafkreicwz5cuku3kxxp3wzldslpfzmfqgukpm2wwy7t7zuevkdp4gbl2uq\" alt=\"pots\" /> </A_152> : items.map(cachedRenderItem); return compProps.manual ? <> {reverse && fetchMore} {renderedItems} {!reverse && fetchMore} </> : <InfiniteScroll pageStart={0} loadMore={makeMoreItems} threshold={compProps.threshold ?? 250} hasMore={state.displayCount <= filteredItems.length} loader={loader}> {renderedItems} </InfiniteScroll>;}; const Post = postProps => { return <Widget loading={<div className=\"w-100\" style={{ height: \"200px\" }} />} src=\"mob.near/widget/MainPage.N.Post\" props={postProps} />;}; const Feed = (compProps) => { const post = compProps.post === undefined ?? true; const hashtags = compProps.hashtags || []; const indexKey = compProps.indexKey ?? \"main\"; const index = [{ action: \"post\", key: indexKey, options: { limit: 10, order: \"desc\", accountId: compProps.accounts }, cacheOptions: { ignoreCache: true } }, { action: \"repost\", key: indexKey, options: { limit: 10, order: \"desc\", accountId: compProps.accounts }, cacheOptions: { ignoreCache: true } }]; const isPremiumFeed = compProps.isPremiumFeed; const commentAccounts = compProps.commentAccounts; const renderedPosts = {}; const makePostItem = (a) => ({ type: \"social\", path: \\`\\${a.accountId}/post/main\\`, blockHeight: a.blockHeight }); const renderPost = (a) => { if (a.value.type !== \"md\") { return false; } const item = JSON.stringify(makePostItem(a)); if (item in renderedPosts) { return false; } renderedPosts[item] = true; const postProps = { accountId: a.accountId, blockHeight: a.blockHeight, isPremiumFeed, commentAccounts, indexKey, groupId: compProps.groupId, permissions: compProps.permissions }; return <div key={JSON.stringify(a)}> <Post {...postProps} /> </div>; }; const repostSvg = <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 2 24 24\" stroke=\"currentColor\" strokeWidth=\"1\"> <path fill-rule=\"evenodd\" d=\"M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z\" transform=\"rotate(180, 12, 12), translate(0, 4)\" /> <path fill-rule=\"evenodd\" d=\"M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z\" transform=\"translate(0, 4)\" /> </svg>; const extractParentPost = (item) => { if (!item || item.type !== \"social\" || !item.path || !item.blockHeight) { return undefined; } const accountId = item.path.split(\"/\")[0]; const parentPost = { accountId, blockHeight: item.blockHeight }; return \\`\\${accountId}/post/main\\` === item.path ? parentPost : undefined; }; const renderRepost = (a) => { if (a.value.type !== \"repost\") { return false; } const post = extractParentPost(a.value.item); if (!post) { return false; } const item = JSON.stringify(makePostItem(post)); if (item in renderedPosts) { return false; } renderedPosts[item] = true; const profileLineProps = { accountId: a.accountId, hideImage: true, hideAccountId: true, tooltip: true }; const postProps = { accountId: post.accountId, blockHeight: post.blockHeight, reposted: true, isPremiumFeed, commentAccounts, indexKey, groupId: compProps.groupId, permissions: compProps.permissions }; const postLoading = <div className=\"w-100\" style={{ height: \"200px\" }} />; return <div key={JSON.stringify(a)}> <div className=\"text-muted\" style={{ fontSize: \"13px\", fontWeight: 700, marginLeft: \"24px\", marginBottom: \"-24px\", paddingTop: \"4px\", position: \"relative\", zIndex: 1 }}> {repostSvg}{\" \"} <span style={{ marginLeft: \"8px\" }}> Reposted by <Widget loading={a.accountId} src=\"mob.near/widget/N.ProfileLine\" /> </span> </div> <Widget loading={postLoading} src=\"mob.near/widget/MainPage.N.Post\" props={postProps} /> </div>; }; const renderItem = (item) => { return item.action === \"post\" ? renderPost(item) : renderRepost(item); }; const Container = styled.div\\` display: flex; flex-direction: column; .post-btn { background: rgb(46, 46, 46); border-radius: 6px; padding: 12px 16px; border: none; color: white; } \\`; return <Container> {post && <Widget loading=\" \" code={props.alem.componentsCode.Compose} props={{ ...{ ...props } }} />} <MergedIndexFeed index={index} renderItem={renderItem} filter={compProps.filter} threshold={800} /> </Container>; }; const A_158 = styled.div\\` display: flex; flex-direction: column; .tab-content { padding: 1rem 0; }\\`;const Nav = styled.div\\` .nav-pills { background: #fbfbfb; font-weight: 500; --bs-nav-pills-border-radius: 0; --bs-nav-link-color: #000; --bs-nav-pills-link-active-color: #000; --bs-nav-pills-link-active-bg: #fbfbfb; --bs-nav-link-padding-y: 0.75rem; border-bottom: 1px solid #eee; padding-top: 3px; } .nav-link.active { border-bottom: 3px solid #dd3345; } .nav-item:not(:has(> .disabled)):hover { background: #dd334456; .nav-link { color: #dd3345; } } margin: 0 -12px;\\`; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const FollowTabs = (__props__) => { const { accountId, projectId, nav } = __props__; const profileLink = hrefWithParams(\\`?tab=profile&accountId=\\${accountId}\\`); return <A_158> <Nav> <ul className=\"nav nav-pills nav-fill\" role=\"tablist\"> <li className=\"nav-item\" role=\"presentation\"> <a href={\\`\\${profileLink}&nav=followers\\`} className={\\`btn nav-link \\${nav === \"followers\" ? \"active\" : \"\"}\\`} role=\"tab\"> Followers </a> </li> <li className=\"nav-item\" role=\"presentation\"> <a href={\\`\\${profileLink}&nav=following\\`} className={\\`btn nav-link \\${nav === \"following\" ? \"active\" : \"\"}\\`} role=\"tab\"> Following </a> </li> </ul> </Nav> <div className=\"tab-content\"> <div className=\"tab-pane fade in show active\" role=\"tabpanel\"> <Widget loading=\" \" code={props.alem.componentsCode.FollowersList} props={{ ...{ accountId: projectId || accountId, nav: nav, ...props } }} /> </div> </div> </A_158>; }; const donorOptions = (accountId) => [{ label: \"Social Feed\", id: \"feed\", disabled: false, source: (componentProps) => <Feed {...componentProps} />, href: hrefWithParams(\\`?tab=profile&accountId=\\${accountId}&nav=feed\\`) }, { label: \"Donations\", id: \"donations\", disabled: false, source: (componentProps) => <Widget loading=\" \" code={props.alem.componentsCode.A_131} props={{ ...{ ...componentProps, ...props } }} />, href: hrefWithParams(\\`?tab=profile&accountId=\\${accountId}&nav=donations\\`) }, { label: \"\", id: \"followers\", disabled: false, source: FollowTabs }, { label: \"\", id: \"following\", disabled: false, source: FollowTabs }]; const A_132 = styled.div\\` display: flex; flex-direction: column;\\`; const DonorPage = () => { const { accountId: _accountId, nav } = props.alem.useParams(); const accountId = _accountId || context.accountId; const profile = Social.getr(\\`\\${accountId}/profile\\`); return <A_132> <Widget loading=\" \" code={props.alem.componentsCode.Body} props={{ ...{ nav: nav ?? \"donations\", navOptions: donorOptions(accountId), profile: profile, post: accountId === context.accountId, ...props } }} /> </A_132>; }; const routesPath = { CREATE_PROJECT_TAB: \"createproject\", EDIT_PROJECT_TAB: \"editproject\", PROJECTS_LIST_TAB: \"projects\", PROJECT_DETAIL_TAB: \"project\", CART_TAB: \"cart\", FEED_TAB: \"feed\", POTS_TAB: \"pots\", DEPLOY_POT_TAB: \"deploypot\", POT_DETAIL_TAB: \"pot\", DONORS_TAB: \"donors\", PROFILE_TAB: \"profile\", EDIT_PROFILE_TAB: \"editprofile\"}; const Routes = () => { const ProjectsRoute = props.alem.createRoute(routesPath.PROJECTS_LIST_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.ProjectsPage} props={{ ...{ ...props } }} />); const ProjectRoute = props.alem.createRoute(routesPath.PROJECT_DETAIL_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.ProjectPage} props={{ ...{ ...props } }} />); const DonorRoute = props.alem.createRoute(routesPath.PROFILE_TAB, () => <DonorPage />); const EditRoute = props.alem.createRoute(routesPath.EDIT_PROFILE_TAB, () => <EditProfile />); const PotsHomeRoute = props.alem.createRoute(routesPath.POTS_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.PotsHome} props={{ ...{ ...props } }} />); const PotRoute = props.alem.createRoute(routesPath.POT_DETAIL_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.Pot} props={{ ...{ ...props } }} />); const FeedRoute = props.alem.createRoute(routesPath.FEED_TAB, () => <FeedPage />); const DeployPotRoute = props.alem.createRoute(routesPath.DEPLOY_POT_TAB, () => <DeployPot />); const LeaderboardRoute = props.alem.createRoute(routesPath.DONORS_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.Leaderboard} props={{ ...{ ...props } }} />); const CartRoute = props.alem.createRoute(routesPath.CART_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.Cart} props={{ ...{ ...props } }} />); const CreateProjectRoute = props.alem.createRoute(routesPath.CREATE_PROJECT_TAB, () => <CreateProject />); const EditProjectRoute = props.alem.createRoute(routesPath.EDIT_PROJECT_TAB, () => <CreateProject />); const routes = [ProjectsRoute, ProjectRoute, DonorRoute, EditRoute, PotsHomeRoute, PotRoute, FeedRoute, DeployPotRoute, LeaderboardRoute, CartRoute, CreateProjectRoute, EditProjectRoute]; return <Widget loading=\" \" code={props.alem.componentsCode.Router} props={{ ...{ routes: routes, parameterName: \"tab\", ...props } }} />; }; const useDonationModal = () => useContext(\"donation-modal\"); const ModulesContext = () => { const { setDefaultData, updateData, getSelf } = createContext(\"alemModulesContext\"); setDefaultData({ calls: {}, callModule: (setupCode, code, callId, onComplete) => { if (!code || !callId) return; const codeStructure = \\` \\${setupCode}; event.source.postMessage({response: \\${code.replaceAll(\";\", \"\")}, forCallId: \\${callId}}, \"*\"); \\`; const updatedCalls = { ...getSelf().calls }; updatedCalls[callId] = { code: codeStructure, handler: onComplete }; updateData({ calls: updatedCalls }); }, removeCall: callId => { const updatedCalls = {}; const currentCalls = getSelf().calls; const calls = Object.keys(currentCalls); calls.forEach(call => { if (call !== callId.toString()) { updatedCalls[call] = currentCalls[call]; } }); updateData({ calls: updatedCalls }); } });}; const ModulesProvider = () => { ModulesContext(); const modulesHandler = \\` <script> window.addEventListener(\"message\", (event) => { if (event.data.code) { eval(event.data.code); } }, false); </script> \\`; const modules = useContext(\"alemModulesContext\"); const calls = modules.calls; const callsKeys = Object.keys(calls); return <> {callsKeys.map(callKey => <iframe style={{ height: 0, width: 0 }} srcDoc={modulesHandler} message={{ code: calls[callKey].code }} onMessage={message => { if (message) { calls[message.forCallId].handler(message.response); modules.removeCall(message.forCallId); } }} />)} </>;}; const Main = () => { const { transactionHashes: _transactionHashes } = props.alem.useParams(); const { successfulDonation, donationModalProps } = useDonationModal(); return <> <ModulesProvider /> <Widget loading=\" \" code={props.alem.componentsCode.A_250} props={{ ...{ ...props } }} /> <div className=\"app-content\"> <Routes /> </div> <Widget loading=\" \" code={props.alem.componentsCode.A_253} props={{ ...{ ...props } }} /> {(successfulDonation || _transactionHashes) && <Widget loading=\" \" code={props.alem.componentsCode.ModalSuccess} props={{ ...{ ...props } }} />} {donationModalProps && <Widget loading=\" \" code={props.alem.componentsCode.ModalDonation} props={{ ...{ ...props } }} />} </>; }; const createContext = contextKey => { const setDefaultData = defaultStateValue => { if (!state[contextKey] || !state[contextKey].initialized) { const stateKeys = Object.keys(defaultStateValue); let mainKeys = [...stateKeys]; mainKeys = mainKeys.filter((item, index) => mainKeys.indexOf(item) === index); State.update({ ...state, [contextKey]: { initialized: true, keys: mainKeys, ...defaultStateValue } }); } props = { ...props, ...state, [contextKey]: { ...state[contextKey] } }; }; const updateData = updates => { const updatedState = { [contextKey]: { ...state[contextKey], ...updates } }; State.update(updatedState); props = { ...props, ...updatedState }; }; const getSelf = () => props[contextKey]; return { setDefaultData, updateData, getSelf };}; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const ALEM_ROUTES_CONTEXT_KEY = \"alemRoutes\";const RouterContext = () => { const { setDefaultData, updateData, getSelf } = createContext(ALEM_ROUTES_CONTEXT_KEY); const updateAlemRoutesState = updatedState => { updateData({ ...updatedState }); }; const alemRoutesState = () => useContext(ALEM_ROUTES_CONTEXT_KEY); setDefaultData({ routesInitialized: false, activeRoute: \"\", routeParams: {}, history: [], routeParameterName: \"path\", routes: [], routeType: \"URLBased\", updateRouteParameters: routeProps => { const currentHistory = alemRoutesState().history; const hasPreviousHistory = currentHistory.length === 0 && routeProps.history; const updatedHistory = hasPreviousHistory ? routeProps.history : alemRoutesState().history; if (routeProps.activeRoute) { const newHistory = { route: routeProps.activeRoute, routeParams: routeProps.routeParams }; if (updatedHistory.length > 10) { updatedHistory.shift(); } if (updatedHistory.at(-1).route !== routeProps.activeRoute) { updatedHistory.push(newHistory); } } updateAlemRoutesState({ routes: routeProps.routes || getSelf().routes, routeType: routeProps.routeType || getSelf().routeType, activeRoute: routeProps.activeRoute || getSelf().activeRoute, routeParams: routeProps.routeParams || getSelf().routeParams, routeParameterName: routeProps.routeParameterName || getSelf().routeParameterName, history: updatedHistory, routesInitialized: true }); if (props.alem.keepRoute && routeProps.activeRoute) { Storage.privateSet(\"alem::keep-route\", updatedHistory); } } });}; const fontsLoaded = props.alem.loadExternalStyles([\"https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&display=swap\"]); RouterContext(); CartProvider(); DonationModalProvider(); return <div className=\"app-container\">{fontsLoaded ? <Main /> : <Spinner />}</div>; `, }, },};if (props.alem.keepRoute) { if (!props.alem.ready) { props.alem.promisify( () => Storage.privateGet(\"alem::keep-route\"), (data) => { updateAlemState({ previousRoute: data.route, previousRouteParams: data.routeParams, ready: true, }); }, () => { updateAlemState({ previousRoute: null, ready: true, }); }, 300, ); }} else { updateAlemState({ previousRoute: null, ready: true, });}const alemCssBody = `.app-container { margin-top: calc(-1 * var(--body-top-padding, 0));}.app-content { width: 100%; height: 100%; background: #ffffff; border-radius: 0rem 0rem 1.5rem 1.5rem; border-top: 1px solid var(--ui-elements-light, #eceef0); background: var(--base-white, #fff); &.form { border: none; background: #fafafa; }}* { font-family: \"Mona-Sans\"; font-style: normal; font-weight: 400;}@font-face { font-family: mona-sans; font-style: normal; font-weight: 400; src: local(\"Mona-Sans\"), url(https://fonts.cdnfonts.com/s/91271/Mona-Sans-Regular.woff) format(\"woff\");}@font-face { font-family: mona-sans; font-style: normal; font-weight: 500; src: local(\"Mona-Sans\"), url(https://fonts.cdnfonts.com/s/91271/Mona-Sans-Medium.woff) format(\"woff\");}@font-face { font-family: mona-sans; font-style: normal; font-weight: 600; src: local(\"Mona-Sans\"), url(https://fonts.cdnfonts.com/s/91271/Mona-Sans-SemiBold.woff) format(\"woff\");}@font-face { font-family: mona-sans; font-style: normal; font-weight: 700; src: local(\"Mona-Sans\"), url(https://fonts.cdnfonts.com/s/91271/Mona-Sans-Bold.woff) format(\"woff\");}`;const AlemTheme = styled.div` ${state.alem.alemExternalStylesBody} ${alemCssBody}`;const AlemApp = useMemo(() => { if (!props.alem.ready) { return \"\"; } const widgetLayer2code = ` const props = { ...props, alem: { ...props.alem, m: { }, } }; return ( <Widget loading=\" \" code={props.alem.componentsCode.App} props={props} /> ) `; return ( <AlemTheme> <Widget loading=\" \" code={widgetLayer2code} props={{ alem: props.alem }} /> </AlemTheme> );}, [props.alem.ready, props.alem.alemExternalStylesBody, props.alem.rootProps]);return AlemApp;\n " } } } } }

Transaction Execution Plan

Convert Transaction To Receipt
Gas Burned:
2 Tgas
Tokens Burned:
0.00024 
Receipt:
Predecessor ID:
Receiver ID:
Gas Burned:
159 Tgas
Tokens Burned:
0.01597 
Called method: 'set' in contract: social.near
Arguments:
{ "data": { "potlock.near": { "widget": { "Index": { "": "\n /** Bundle generated by Além Library v1.0.0 - See more here: https://github.com/wpdas/alem */\n /** Project repository: https://github.com/PotLock/bos-app-alem */\n const updateAlemState = (updatedState) => { State.update({ alem: { ...state.alem, ...updatedState, }, });};const alemState = () => state.alem ;const AlemStateInitialBody = { alem: { ready: false, rootProps: props, alemEnvironment: \"production\", keepRoute: false, previousRoute: null, previousRouteParams: null, alemExternalStylesLoaded: false, alemExternalStylesBody: \"\", },};State.init(AlemStateInitialBody);State.update({ alem: { ...state.alem, rootProps: props } });const props = { ...props, alem: { ...state.alem, createRoute: (path, component) => ({ path, component }) , useParams: () => { let params = alemState().rootProps; return params; }, loadExternalStyles: (URLs) => { if (!URLs && !alemState().alemExternalStylesLoaded) { return; } let stylesBody = \"\"; const totalItems = URLs.length; let loadedCounter = 0; const loadStyle = (styleURL) => { asyncFetch(styleURL).then((response) => { Storage.set(styleURL, response.body); stylesBody += response.body; loadedCounter += 1; if (loadedCounter === totalItems) { updateAlemState({ alemExternalStylesLoaded: true, alemExternalStylesBody: stylesBody, }); } }); }; URLs.forEach((styleURL) => { props.alem.promisify( () => Storage.get(styleURL), (response) => { stylesBody += response; loadedCounter += 1; if (loadedCounter === totalItems) { updateAlemState({ alemExternalStylesLoaded: true, alemExternalStylesBody: stylesBody, }); } }, () => { loadStyle(styleURL); }, 100, ); }); return alemState().alemExternalStylesLoaded; }, promisify: ( caller, resolve, reject, _timeout, ) => { const timer = 100; const timeout = _timeout || 10000; let timeoutCheck = 0; const find = () => { const response = caller(); if (response !== undefined && response !== null) { resolve(response); } else { if (timeoutCheck < timeout) { setTimeout(find, timer); timeoutCheck += timer; } else { if (reject) { reject(); } } } }; find(); }, isDevelopment: alemState().alemEnvironment === \"development\", getAlemEnvironment: () => alemState().alemEnvironment, componentsCode: { ModalAddFundingSource: ` const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_53 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_54 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const Error = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const InputContainer = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_55 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const Input = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`; const DateInput = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_53> {label && <A_54>{label}</A_54>} <InputContainer> {__props__.preInputChildren && __props__.preInputChildren} <Input type={__props__.selectTime ? \"datetime-local\" : \"date\"} placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? (() => {})} style={__props__.inputStyles || {}} /> {__props__.postInputChildren && __props__.postInputChildren} </InputContainer> <Error className={error ? \"show\" : \"\"}>{error}</Error> </A_53>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const ModalHeader = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; background: #f6f5f3; padding: 10px 20px; border-top-left-radius: 6px; border-top-right-radius: 6px;\\`;const ModalHeaderText = styled.div\\` font-size: 16px; font-weight: 600; color: #292929; line-height: 24px; word-wrap: break-word; margin-left: 8px;\\`;const ModalBody = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 16px 20px 32px 20px; gap: 24px;\\`;const Icon = styled.svg\\` width: 20px; height: 20px;\\`;const CloseIcon = styled.svg\\` width: 20px; height: 20px; cursor: pointer; transition: rotate 300ms ease-in-out; :hover { rotate: 180deg; }\\`;const Row = styled.div\\` display: flex; flex-direction: row; align-items: center;\\`; const props = props; const { onClose, handleAddFundingSource, fundingSources, fundingSourceIndex } = props; const initalState = { investorName: fundingSources[fundingSourceIndex]?.investorName || \"\", investorNameError: \"\", date: fundingSources[fundingSourceIndex]?.date || \"\", dateError: \"\", description: fundingSources[fundingSourceIndex]?.description || \"\", descriptionError: \"\", denomination: fundingSources[fundingSourceIndex]?.denomination || \"\", denominationError: \"\", amountReceived: fundingSources[fundingSourceIndex]?.amountReceived || \"\", amountReceivedError: \"\" }; State.init(initalState); useEffect(() => { State.update(initalState); }, [fundingSources, fundingSourceIndex]); return <ModalOverlay overlayStyle={onClose}> <ModalHeader> <div></div> <Row> <Icon width=\"12\" height=\"18\" viewBox=\"0 0 12 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6.39016 7.9C4.12016 7.31 3.39016 6.7 3.39016 5.75C3.39016 4.66 4.40016 3.9 6.09016 3.9C7.87016 3.9 8.53016 4.75 8.59016 6H10.8002C10.7302 4.28 9.68016 2.7 7.59016 2.19V0H4.59016V2.16C2.65016 2.58 1.09016 3.84 1.09016 5.77C1.09016 8.08 3.00016 9.23 5.79016 9.9C8.29016 10.5 8.79016 11.38 8.79016 12.31C8.79016 13 8.30016 14.1 6.09016 14.1C4.03016 14.1 3.22016 13.18 3.11016 12H0.910156C1.03016 14.19 2.67016 15.42 4.59016 15.83V18H7.59016V15.85C9.54016 15.48 11.0902 14.35 11.0902 12.3C11.0902 9.46 8.66016 8.49 6.39016 7.9Z\" fill=\"#151A23\" /> </Icon> <ModalHeaderText>Add Past Funding Source</ModalHeaderText> </Row> <CloseIcon viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" onClick={onClose}> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#7B7B7B\" /> </CloseIcon> </ModalHeader> <ModalBody> <A_62 {...{ label: \"Name of investor\", placeholder: \"Enter investor name\", value: state.investorName, onChange: val => State.update({ investorName: val }), validate: () => { if (state.investorName.length < 3) { State.update({ investorNameError: \"Must be at least 3 characters\" }); return; } if (state.investorName.length > 50) { State.update({ investorNameError: \"Must be less than 50 characters\" }); return; } State.update({ investorNameError: \"\" }); }, error: state.investorNameError }} /> <DateInput {...{ label: <> Date <span>(optional)</span> </>, selectTime: false, value: state.date, onChange: date => State.update({ date: date }), error: state.dateError }} /> <TextArea {...{ label: \"Description\", placeholder: \"Type description\", value: state.description, onChange: description => State.update({ description }), validate: () => { if (state.description.length > 500) { State.update({ descriptionError: \"Must be less than 500 characters\" }); return; } State.update({ descriptionError: \"\" }); }, error: state.descriptionError }} /> <A_62 {...{ label: \"Denomination of investment\", placeholder: \"e.g. NEAR, USD, USDC, etc.\", value: state.denomination, onChange: val => State.update({ denomination: val.toUpperCase() }), validate: () => { if (state.denomination.length < 3) { State.update({ denominationError: \"Must be at least 3 characters\" }); return; } if (state.denomination.length > 10) { State.update({ denominationError: \"Must be less than 10 characters\" }); return; } State.update({ denominationError: \"\" }); }, error: state.denominationError }} /> <A_62 {...{ label: \"Investment amount\", placeholder: \"e.g. 1000\", value: state.amountReceived, onChange: val => State.update({ amountReceived: val }), validate: () => { if (isNaN(state.amountReceived)) { State.update({ amountReceivedError: \"Must be a number\" }); return; } State.update({ amountReceivedError: \"\" }); }, error: state.amountReceivedError }} /> <Row style={{ width: \"100%\", justifyContent: \"flex-end\" }}> <Button {...{ type: \"primary\", text: \"Add Funding Source\", disabled: !state.investorName || !!state.investorNameError || !!state.dateError || !state.description || !!state.descriptionError || !state.denomination || !!state.denominationError || !state.amountReceived || !!state.amountReceivedError, onClick: () => { const fundingSource = { investorName: state.investorName, date: state.date, description: state.description, denomination: state.denomination, amountReceived: state.amountReceived }; State.update({ investorName: \"\", investorNameError: \"\", date: \"\", dateError: \"\", description: \"\", descriptionError: \"\", denomination: \"\", denominationError: \"\", amountReceived: \"\", amountReceivedError: \"\" }); handleAddFundingSource(fundingSource); } }} /> </Row> </ModalBody> </ModalOverlay>; `, AccountsStack: ` const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const StackContainer = styled.div\\` width: 200px; height: 30px; margin-bottom: 16px; display: flex; flex-direction: row; @media screen and (max-width: 768px) { margin-left: 36px; }\\`;const MoreAccountsContainer = styled.div\\` width: 28px; height: 28px; border: 2px solid white; border-radius: 50%; background: #dd3345; position: relative; display: flex; justify-content: center; align-items: center; margin-right: -8px;\\`;const MoreAccountsText = styled.div\\` color: white; font-size: 12px; font-weight: 600; text-align: center;\\`; const {accountIds: accountIds, maxDisplayCount: maxDisplayCount, sendToBack: sendToBack} = props; const MAX_DISPLAY_COUNT = maxDisplayCount || 5; const accounts = useMemo(() => accountIds.slice(0, MAX_DISPLAY_COUNT), [accountIds]); return <StackContainer> {accountIds.length > MAX_DISPLAY_COUNT && <MoreAccountsContainer style={{ zIndex: accountIds.length + 1 }}> <MoreAccountsText>{MAX_DISPLAY_COUNT}+</MoreAccountsText> </MoreAccountsContainer>} {accounts.map((accountId, idx) => { return <ProfileImage {...{ accountId, style: { width: \"28px\", height: \"28px\", zIndex: sendToBack ? 0 : accountIds.length - idx, margin: \"0 -8px 0 0\", border: \"2px solid white\", borderRadius: \"50%\", background: \"white\" }, className: \"mb-2\", imageClassName: \"rounded-circle w-100 h-100 d-block\", thumbnail: false, tooltip: true }} />; })} </StackContainer>; `, CreateForm: ` function validateGithubRepoUrl(url) { const githubRepoUrlPattern = /^(https?:\\\\/\\\\/)?(www\\\\.)?github\\\\.com\\\\/[a-zA-Z0-9-]+\\\\/[a-zA-Z0-9_.-]+\\\\/?$/; return githubRepoUrlPattern.test(url);} const MembersListItem = styled.div\\` padding: 16px 0px; border-top: 1px #f0f0f0 solid; display: flex; flex-direction: row; align-items: center; justify-content: space-between;\\`;const MembersListItemLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 16px;\\`;const MembersListItemText = styled.div\\` font-size: 16px; font-weight: 400; color: #2e2e2e;\\`;const RemoveMember = styled.a\\` color: #2e2e2e; font-size: 14px; font-weight: 600; visibility: hidden; cursor: pointer; opacity: 0; transition: opacity 0.2s ease-in-out; &:hover { text-decoration: none; } \\${MembersListItem}:hover & { visibility: visible; opacity: 1; }\\`; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const AccountsList = __props__ => { const { accountIds, allowRemove, handleRemoveAccount } = __props__; return <> {accountIds.map(accountId => { return <MembersListItem> <MembersListItemLeft> <ProfileImage {...{ accountId, style: { width: \"40px\", height: \"40px\", margin: \"0 -8px 0 0\", borderRadius: \"50%\", background: \"white\" }, imageClassName: \"rounded-circle w-100 h-100 d-block\", thumbnail: false, tooltip: true }} /> <MembersListItemText>@{accountId}</MembersListItemText> </MembersListItemLeft> {allowRemove && <RemoveMember onClick={() => handleRemoveAccount(accountId)}>Remove</RemoveMember>} </MembersListItem>; })} </>;}; const A_49 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; margin-bottom: 24px;\\`;const ModalHeaderLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const IconContainer = styled.div\\` width: 40px; height: 40px; background: #f0f0f0; border-radius: 50%; display: flex; justify-content: center; align-items: center; margin-right: 16px;\\`;const A_50 = styled.svg\\` width: 20px; height: 20px; cursor: pointer; transition: 300ms ease-in-out; :hover { rotate: 180deg; }\\`;const A_51 = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 600;\\`;const ModalDescription = styled.p\\` color: #2e2e2e; font-size: 16px; font-weight: 400;\\`;const A_52 = styled.div\\` height: 24px;\\`;const MembersCount = styled.span\\` color: #2e2e2e; font-weight: 600;\\`;const MembersText = styled.div\\` color: #7b7b7b; font-size: 12px; font-weight: 400;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const ModalMultiAccount = (__props__) => { const { onClose, titleText, descriptionText, unitText, inputValue, onInputChange, handleAddAccount, handleRemoveAccount, accountError, accountIds } = __props__; return <ModalOverlay onOverlayClick={onClose}> <A_49> <ModalHeaderLeft> <IconContainer> <A_50 viewBox=\"0 0 24 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M16.24 7.65C15.07 7.13 13.63 6.75 12 6.75C10.37 6.75 8.93 7.14 7.76 7.65C6.68 8.13 6 9.21 6 10.39V12H18V10.39C18 9.21 17.32 8.13 16.24 7.65ZM8.07 10C8.16 9.77 8.34 9.58 8.56 9.48C9.66 8.99 10.82 8.75 11.99 8.75C13.17 8.75 14.32 9 15.42 9.48C15.65 9.58 15.82 9.77 15.91 10H8.07Z\" fill=\"#151A23\" /> <path d=\"M1.22 8.58C0.48 8.9 0 9.62 0 10.43V12H4.5V10.39C4.5 9.56 4.73 8.78 5.13 8.1C4.76 8.04 4.39 8 4 8C3.01 8 2.07 8.21 1.22 8.58Z\" fill=\"#151A23\" /> <path d=\"M22.78 8.58C21.93 8.21 20.99 8 20 8C19.61 8 19.24 8.04 18.87 8.1C19.27 8.78 19.5 9.56 19.5 10.39V12H24V10.43C24 9.62 23.52 8.9 22.78 8.58Z\" fill=\"#151A23\" /> <path d=\"M12 6C13.66 6 15 4.66 15 3C15 1.34 13.66 0 12 0C10.34 0 9 1.34 9 3C9 4.66 10.34 6 12 6ZM12 2C12.55 2 13 2.45 13 3C13 3.55 12.55 4 12 4C11.45 4 11 3.55 11 3C11 2.45 11.45 2 12 2Z\" fill=\"#151A23\" /> <path d=\"M3.9999 2.49687L1.49677 5L3.9999 7.50313L6.50303 5L3.9999 2.49687Z\" fill=\"#151A23\" /> <path d=\"M20 3L17.5 7H22.5L20 3Z\" fill=\"#151A23\" /> </A_50> </IconContainer> <A_51>{titleText}</A_51> </ModalHeaderLeft> <A_50 viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" onClick={onClose}> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#7B7B7B\" /> </A_50> </A_49> <ModalDescription>{descriptionText}</ModalDescription> <A_62 {...{ placeholder: \"NEAR account ID\", value: inputValue, onChange: onInputChange, postInputChildren: <Button {...{ type: \"primary\", text: \"Add\", onClick: handleAddAccount, style: { borderRadius: \\`0px 4px 4px 0px\\` }, submit: true }} />, handleKeyPress: (e) => { if (e.key === \"Enter\") { handleAddAccount(); } }, error: accountError }} /> <A_52 /> <MembersText> <MembersCount>{accountIds.length} </MembersCount> {accountIds.length == 1 ? unitText : \\`\\${unitText}s\\`} </MembersText> <AccountsList {...{ accountIds, allowRemove: true, handleRemoveAccount }} /> </ModalOverlay>;}; function validateEVMAddress(address) { if (!address || address.length !== 42) { return false; } const re = /^0x[a-fA-F0-9]{40}$/; return re.test(address);} const A_233 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_234 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_235 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 10px; background: #ffffff; border: 1px solid #d0d5dd; /* box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); */ box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset; border-radius: 4px; color: #101828; width: 100%;\\`;const Placeholder = styled.span\\` color: #a0a3a8;\\`;const scaleOut = styled.keyframes\\` from { transform: scaleY(0); } to { transform: scaleY(1); }\\`;const SelectContent = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; gap: 0.5em; width: 100%; border: 1px solid #d0d5dd; border-radius: 4px; background: #ffffff; z-index: 3 !important;\\`;const Viewport = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; width: 100%;\\`;const Item = styled.button\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 0.5em; width: 100%; cursor: pointer; background: transparent; border: none; transition: background 0.2s ease-in-out; &:nth-child(n + 1) { border-top: 1px solid #d0d5dd; } &:hover { background: #d0d5dd; boder: none; } &:focus { outline: none; }\\`; const Select = (componentProps) => { const label = componentProps.label ?? \"Label\"; const noLabel = componentProps.noLabel ?? false; const placeholder = componentProps.placeholder ?? \"Select an option\"; const value = componentProps.value ?? \"\"; const options = componentProps.options ?? []; const onChange = componentProps.onChange ?? (() => {}); const validate = componentProps.validate ?? (() => {}); const error = componentProps.error ?? \"\"; return <A_233 style={componentProps.containerStyles || {}}> {noLabel ? <></> : <A_234>{label}</A_234>} <Select.Root value={value?.value} onValueChange={(value) => onChange(options.find((option) => option.value === value))}> <Select.Trigger asChild={true}> <A_235 style={componentProps.inputStyles || {}}> {componentProps.iconLeft && componentProps.iconLeft} <Select.Value aria-label={value.value} placeholder={<Placeholder>{placeholder}</Placeholder>} /> {} <Select.Icon> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1 1.5L6 6.5L11 1.5\" stroke=\"currentColor\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg> </Select.Icon> {} </A_235> </Select.Trigger> <Select.Content asChild={true}> <SelectContent> <Select.Viewport asChild={true}> <Viewport> {options.map(({ text, value }) => <Select.Item value={value} asChild={true}> <Item> <Select.ItemText>{text}</Select.ItemText> <Select.ItemIndicator> <svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" /> </svg> </Select.ItemIndicator> </Item> </Select.Item>)} </Viewport> </Select.Viewport> </SelectContent> </Select.Content> </Select.Root> </A_233>;}; const Container = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const Label = styled.label\\` font-style: normal; /* font-weight: 600; */ font-size: 0.95em; line-height: 1.25em; color: #344054;\\`; const SelectMultiple = __props__ => { const { label, options, onChange, placeholder, selected } = __props__; return <Container> {label && <Label>{label}</Label>} <Typeahead options={options} multiple onChange={onChange} placeholder={placeholder} /> </Container>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_166 = styled.div\\` padding-top: 0; position: relative;\\`;const ProfileWraper = styled.div\\` display: flex; padding-left: 4rem; align-items: end; transform: translateY(-50%); position: relative; z-index: 6; @media screen and (max-width: 768px) { padding-left: 8px; }\\`;const ProfileStats = styled.div\\` position: relative; z-index: 1; display: flex; align-items: center; gap: 1.5rem; transform: translate(-25px, -20px); @media screen and (max-width: 768px) { transform: translate(-25px, 0px); gap: 10px; }\\`;const Verified = styled.div\\` opacity: 1; display: flex; align-items: center; font-size: 11px; letter-spacing: 0.88px; gap: 4px; padding: 3px; border-radius: 20px; background: #fff; text-transform: uppercase; overflow: hidden; &.not-verified { width: 10px; opacity: 0; } div { font-weight: 600; } svg { background: white; border-radius: 50%; } @media screen and (max-width: 768px) { div { display: none; } }\\`;const ProfileImageContainer = styled.div\\` background: white; border-radius: 50%; padding: 6px; position: relative; width: 120px; height: 120px; &.editable { &:after { content: \"\"; position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%; border-radius: 50%; background-color: rgba(45.9, 45.9, 45.9, 0); transition: background-color 0.3s; pointer-events: none; @media screen and (max-width: 768px) { height: 64px; } } &:hover { cursor: pointer; &:after { background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; } } } .profile-image { height: 100%; width: 100%; display: flex; border-radius: 50%; img { object-fit: cover; width: 100%; height: 100%; } } > svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0; /* Start with the image invisible */ transition: opacity 0.3s; z-index: 2; /* Ensure the image is on top */ pointer-events: none; } @media screen and (max-width: 768px) { width: 100px; height: 100px; }\\`;const BackgroundImageContainer = styled.div\\` position: relative; width: 100%; height: 318px; display: flex; &.editable { &:after { content: \"\"; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(45.9, 45.9, 45.9, 0); transition: background-color 0.3s; pointer-events: none; } &:hover { cursor: pointer; &:after { background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; } } } img { object-fit: cover; height: 100%; width: 100%; } svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0; transition: opacity 0.3s; z-index: 2; pointer-events: none; } @media screen and (max-width: 768px) { height: 264px; }\\`; const statuses = { Approved: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M10.7835 0.943599C10.3834 0.454917 9.6361 0.454917 9.236 0.943599L8.44604 1.90847C8.17411 2.24062 7.72052 2.36216 7.31895 2.21048L6.15238 1.76985C5.56155 1.54669 4.91436 1.92035 4.8122 2.5436L4.61051 3.77419C4.54108 4.19781 4.20904 4.52985 3.78542 4.59928L2.55484 4.80097C1.93158 4.90313 1.55792 5.55032 1.78108 6.14115L2.22171 7.30772C2.37339 7.70929 2.25185 8.16288 1.9197 8.43481L0.95483 9.22477C0.466148 9.62487 0.466147 10.3722 0.954829 10.7723L1.9197 11.5622C2.25185 11.8342 2.37339 12.2878 2.22171 12.6893L1.78108 13.8559C1.55792 14.4467 1.93158 15.0939 2.55484 15.1961L3.78542 15.3978C4.20904 15.4672 4.54108 15.7992 4.61051 16.2229L4.8122 17.4534C4.91436 18.0767 5.56155 18.4504 6.15238 18.2272L7.31895 17.7866C7.72052 17.6349 8.17411 17.7564 8.44604 18.0886L9.236 19.0535C9.6361 19.5421 10.3834 19.5421 10.7835 19.0535L11.5735 18.0886C11.8454 17.7564 12.299 17.6349 12.7006 17.7866L13.8671 18.2272C14.458 18.4504 15.1052 18.0767 15.2073 17.4534L15.409 16.2229C15.4784 15.7992 15.8105 15.4672 16.2341 15.3978L17.4647 15.1961C18.0879 15.0939 18.4616 14.4467 18.2384 13.8559L17.7978 12.6893C17.6461 12.2878 17.7677 11.8342 18.0998 11.5622L19.0647 10.7723C19.5534 10.3722 19.5534 9.62487 19.0647 9.22478L18.0998 8.43481C17.7677 8.16288 17.6461 7.70929 17.7978 7.30772L18.2384 6.14115C18.4616 5.55032 18.0879 4.90313 17.4647 4.80097L16.2341 4.59928C15.8105 4.52985 15.4784 4.19781 15.409 3.77419L15.2073 2.54361C15.1052 1.92035 14.458 1.54669 13.8671 1.76985L12.7006 2.21048C12.299 2.36216 11.8454 2.24062 11.5735 1.90847L10.7835 0.943599ZM13.5029 7.49591C13.3065 7.30179 12.9899 7.3036 12.7957 7.49996L8.92649 11.4119L7.25946 9.53742C7.07595 9.33107 6.75991 9.31256 6.55356 9.49607C6.34722 9.67958 6.3287 9.99562 6.51221 10.202L8.8858 12.8709L9.90713 11.8496L13.5073 8.20316C13.7013 8.00663 13.6994 7.69003 13.5029 7.49591Z\" fill=\"#0DBFAF\" /> </svg>, color: \"#0E615E\" }, Rejected: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM13.59 5L10 8.59L6.41 5L5 6.41L8.59 10L5 13.59L6.41 15L10 11.41L13.59 15L15 13.59L11.41 10L15 6.41L13.59 5Z\" fill=\"#F6767A\" /> </svg>, color: \"#ED464F\" }, Pending: { icon: <svg width=\"21\" height=\"20\" viewBox=\"0 0 21 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.1523 0C4.63234 0 0.152344 4.48 0.152344 10C0.152344 15.52 4.63234 20 10.1523 20C15.6723 20 20.1523 15.52 20.1523 10C20.1523 4.48 15.6723 0 10.1523 0ZM10.1523 18C5.73234 18 2.15234 14.42 2.15234 10C2.15234 5.58 5.73234 2 10.1523 2C14.5723 2 18.1523 5.58 18.1523 10C18.1523 14.42 14.5723 18 10.1523 18Z\" fill=\"#F4B37D\" /> <path d=\"M5.15234 11.5C5.98077 11.5 6.65234 10.8284 6.65234 10C6.65234 9.17157 5.98077 8.5 5.15234 8.5C4.32392 8.5 3.65234 9.17157 3.65234 10C3.65234 10.8284 4.32392 11.5 5.15234 11.5Z\" fill=\"#F4B37D\" /> <path d=\"M10.1523 11.5C10.9808 11.5 11.6523 10.8284 11.6523 10C11.6523 9.17157 10.9808 8.5 10.1523 8.5C9.32392 8.5 8.65234 9.17157 8.65234 10C8.65234 10.8284 9.32392 11.5 10.1523 11.5Z\" fill=\"#F4B37D\" /> <path d=\"M15.1523 11.5C15.9808 11.5 16.6523 10.8284 16.6523 10C16.6523 9.17157 15.9808 8.5 15.1523 8.5C14.3239 8.5 13.6523 9.17157 13.6523 10C13.6523 10.8284 14.3239 11.5 15.1523 11.5Z\" fill=\"#F4B37D\" /> </svg>, color: \"#EA6A25\" }, Graylisted: { icon: <svg width=\"21\" height=\"20\" viewBox=\"0 0 21 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9.15234 16H11.1523V14H9.15234V16ZM10.1523 0C4.63234 0 0.152344 4.48 0.152344 10C0.152344 15.52 4.63234 20 10.1523 20C15.6723 20 20.1523 15.52 20.1523 10C20.1523 4.48 15.6723 0 10.1523 0ZM10.1523 18C5.74234 18 2.15234 14.41 2.15234 10C2.15234 5.59 5.74234 2 10.1523 2C14.5623 2 18.1523 5.59 18.1523 10C18.1523 14.41 14.5623 18 10.1523 18ZM10.1523 4C7.94234 4 6.15234 5.79 6.15234 8H8.15234C8.15234 6.9 9.05234 6 10.1523 6C11.2523 6 12.1523 6.9 12.1523 8C12.1523 10 9.15234 9.75 9.15234 13H11.1523C11.1523 10.75 14.1523 10.5 14.1523 8C14.1523 5.79 12.3623 4 10.1523 4Z\" fill=\"#7B7B7B\" /> </svg>, color: \"#7B7B7B\" }, Blacklisted: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM2 10C2 5.58 5.58 2 10 2C11.85 2 13.55 2.63 14.9 3.69L3.69 14.9C2.63 13.55 2 11.85 2 10ZM10 18C8.15 18 6.45 17.37 5.1 16.31L16.31 5.1C17.37 6.45 18 8.15 18 10C18 14.42 14.42 18 10 18Z\" fill=\"#292929\" /> </svg>, color: \"#292929\" }}; const FollowStats = ({ projectId: _projectId, accountId: _accountId}) => { const projectId = _projectId; const accountId = projectId || _accountId; if (!accountId) { return \"\"; } const following = Social.keys(\\`\\${accountId}/graph/follow/*\\`, \"final\", { return_type: \"BlockHeight\", values_only: true }); const followers = Social.keys(\\`*/graph/follow/\\${accountId}\\`, \"final\", { return_type: \"BlockHeight\", values_only: true }); const numFollowing = following ? Object.keys(following[accountId].graph.follow || {}).length : 0; const numFollowers = followers ? Object.keys(followers || {}).length : 0; const profileLink = hrefWithParams(\\`?tab=\\${projectId ? \"project\" : \"profile\"}&\\${projectId ? \"projectId\" : \"accountId\"}=\\${projectId || accountId}\\`); const Container = styled.div\\` display: flex; align-items: center; font-size: 14px; gap: 2rem; a { gap: 8px; display: flex; } @media screen and (max-width: 768px) { gap: 1rem; } \\`; return <Container> <div> <a href={\\`\\${profileLink}&nav=followers\\`} className=\"text-dark\"> {numFollowers !== null ? <span style={{ fontWeight: 600 }}>{numFollowers}</span> : \"-\"} <span>Follower{numFollowers !== 1 && \"s\"}</span> </a> </div> <div> <a href={\\`\\${profileLink}&nav=following\\`} className=\"text-dark\"> {numFollowing !== null ? <span style={{ fontWeight: 600 }}>{numFollowing}</span> : \"-\"} <span>Following</span> </a> </div> </Container>;}; const CameraSvg = ({ height}) => <svg width={height || 48} height={height || 48} viewBox={\\`0 0 48 48\\`} fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <mask id=\"mask0_3178_2528\" style={{ maskType: \"alpha\" }} maskUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width={height || 48} height={height || 48}> <rect width={height || 48} height={height || 48} fill=\"#D9D9D9\" /> </mask> <g mask=\"url(#mask0_3178_2528)\"> <path d=\"M5 40.35C4.2 40.35 3.5 40.05 2.9 39.45C2.3 38.85 2 38.15 2 37.35V11.7C2 10.9333 2.3 10.2417 2.9 9.625C3.5 9.00833 4.2 8.7 5 8.7H12.35L16 4.35H29.7V7.35H17.4L13.75 11.7H5V37.35H39V16.65H42V37.35C42 38.15 41.6917 38.85 41.075 39.45C40.4583 40.05 39.7667 40.35 39 40.35H5ZM39 11.65V7.35H34.7V4.35H39V0H42V4.35H46.35V7.35H42V11.65H39ZM21.975 33C24.3917 33 26.4167 32.1833 28.05 30.55C29.6833 28.9167 30.5 26.8917 30.5 24.475C30.5 22.0583 29.6833 20.0417 28.05 18.425C26.4167 16.8083 24.3917 16 21.975 16C19.5583 16 17.5417 16.8083 15.925 18.425C14.3083 20.0417 13.5 22.0583 13.5 24.475C13.5 26.8917 14.3083 28.9167 15.925 30.55C17.5417 32.1833 19.5583 33 21.975 33ZM21.975 30C20.3917 30 19.0833 29.475 18.05 28.425C17.0167 27.375 16.5 26.0583 16.5 24.475C16.5 22.8917 17.0167 21.5833 18.05 20.55C19.0833 19.5167 20.3917 19 21.975 19C23.5583 19 24.875 19.5167 25.925 20.55C26.975 21.5833 27.5 22.8917 27.5 24.475C27.5 26.0583 26.975 27.375 25.925 28.425C24.875 29.475 23.5583 30 21.975 30Z\" fill=\"white\" /> </g> </svg>; const BannerHeader = (__props__) => { const { showFollowers, registration, projectId, profileImageOnChange, bgImageOnChange } = __props__; const accountId = __props__.accountId || projectId || context.accountId; if (!accountId) { return \"No account ID\"; } const editable = bgImageOnChange && profileImageOnChange; const profile = __props__.profile ?? Social.getr(\\`\\${accountId}/profile\\`); const image = profile.image; const backgroundImage = __props__.backgroundImage || profile.backgroundImage; const profileImage = __props__.profileImage || image; const imageStyle = __props__.imageStyle ?? {}; const backgroundStyle = __props__.backgroundStyle ?? {}; const containerStyle = __props__.containerStyle ?? {}; const nadaBotVerified = Near.view(\"v1.nadabot.near\", \"is_human\", { account_id: accountId }); return <A_166 style={{ ...containerStyle }}> <BackgroundImageContainer className={editable ? \"editable\" : \"\"} style={{ height: backgroundStyle.height ? backgroundStyle.height : \"\" }}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: backgroundImage, alt: \"profile background\", style: { ...backgroundStyle, pointerEvents: \"none\" }, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\" }, ...props } }} /> <CameraSvg height={48} /> {editable && <Files multiple={false} accepts={[\"image/*\"]} minFileSize={1} style={{ zIndex: 4, top: 0, width: \"100%\", height: backgroundStyle.height ?? \"100%\", position: \"absolute\" }} clickable onChange={bgImageOnChange} />} </BackgroundImageContainer> <ProfileWraper> <ProfileImageContainer className={editable ? \"editable\" : \"\"}> <CameraSvg height={24} /> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ style: { ...imageStyle }, className: \"profile-image\", image: profileImage, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\", ...props } }} /> {editable && <Files multiple={false} accepts={[\"image/*\"]} minFileSize={1} style={{ position: \"absolute\", width: \"100%\", height: \"100%\", left: 0, top: 0 }} clickable onChange={profileImageOnChange}> </Files>} </ProfileImageContainer> {showFollowers && <ProfileStats> {registration ? <Verified> {statuses[registration.status].icon} <div style={{ color: statuses[registration.status].color }}> {registration.status}</div> </Verified> : nadaBotVerified ? <Verified> {statuses.Approved.icon} <div style={{ color: statuses.Approved.color }}> Verified</div> </Verified> : <div style={{ width: \"10px\" }} />} <FollowStats accountId={projectId || accountId} projectId={projectId} /> </ProfileStats>} </ProfileWraper> {__props__.children && __props__.children} </A_166>; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const validateNearAddress = address => { const NEAR_ACCOUNT_ID_REGEX = /^(?=.{2,64}$)(?!.*\\\\.\\\\.)(?!.*-$)(?!.*_$)[a-z\\\\d._-]+$/i; let isValid = NEAR_ACCOUNT_ID_REGEX.test(address); if (address.length < 64 && !address.endsWith(\".near\")) { isValid = false; } return isValid;}; const getTeamMembersFromSocialProfileData = profileData => { if (!profileData) return []; const team = profileData.plTeam ? JSON.parse(profileData.plTeam) : profileData.team ? Object.entries(profileData.team).filter(([_, v]) => v !== null).map(([k, _]) => k) : []; return team;}; function doesUserHaveDaoFunctionCallProposalPermissions(accountId, policy) { const userRoles = policy.roles.filter(role => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); return allowed;} const A_13 = styled.div\\` display: flex; flex-direction: column; width: 100%; padding: 72px 64px 72px 64px; @media screen and (max-width: 768px) { padding: 0px; }\\`;const LowerBannerContainer = styled.div\\` position: absolute; top: 340px; left: 0px; display: flex; align-items: stretch; /* Ensuring child elements stretch to full height */ justify-content: space-between; width: 100%; z-index: 10; @media screen and (max-width: 768px) { top: 310px; position: initial; align-items: flex-start; gap: 10px; flex-direction: column; }\\`;const LowerBannerContainerLeft = styled.div\\` display: flex; flex-direction: row; align-items: flex-end; margin-left: 190px; @media screen and (max-width: 768px) { margin-left: 0px; }\\`;const LowerBannerContainerRight = styled.div\\` display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; /* Pushes TeamContainer to the bottom */ flex: 1;\\`;const AddTeamMembers = styled.a\\` margin: 0px 0px 16px 36px; cursor: pointer; color: #dd3345; font-size: 14px; font-weight: 600; &:hover { text-decoration: none; } @media screen and (max-width: 768px) { margin-bottom: 0; }\\`;const FormBody = styled.div\\` display: flex; flex-direction: column; padding: 0px 68px 32px 68px; width: 100%; @media screen and (max-width: 768px) { padding: 0px 32px 32px 32px; }\\`;const FormDivider = styled.div\\` height: 2px; width: 100%; background-color: #ebebeb;\\`;const FormSectionContainer = styled.div\\` display: flex; flex-direction: row; gap: 160px; margin: 48px 0 48px 0; @media screen and (max-width: 768px) { flex-direction: column; gap: 32px; }\\`;const FormSectionLeftDiv = styled.div\\` flex: 1; display: flex; flex-direction: column; /* background-color: yellow; */ gap: 16px;\\`;const FormSectionRightDiv = styled.div\\` flex: 1; display: flex; flex-direction: column; /* background-color: lightblue; */\\`;const FormSectionTitle = styled.div\\` color: #2e2e2e; font-size: 16; font-weight: 600; word-wrap: break-word;\\`;const FormSectionDescription = styled.div\\` color: #2e2e2e; font-size: 16; font-weight: 400; word-wrap: break-word;\\`;const FormSectionIsRequired = styled.div\\` font-size: 16px; font-weight: 400; word-wrap: break-word; position: relative;\\`;const SvgContainer = styled.div\\` position: absolute; top: -6; left: -26;\\`;const ButtonsContainer = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 32px; margin-top: 32px;\\`;const Space = styled.div\\`\\`;const InputPrefix = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400; box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset;\\`;const A_14 = styled.div\\` display: flex; flex-direction: row; gap: 16px; align-items: flex-start; justify-content: center;\\`;const A_15 = styled.svg\\` width: 20px; height: 20px; cursor: pointer; path { transition: 300ms; } :hover path { fill: #dd3345; }\\`;const FundingHeader = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; background: #f6f5f3; width: 100%;\\`;const FundingHeaderItem = styled.div\\` display: flex; flex-direction: row; align-items: space-between; justify-content: flex-start; padding: 10px 20px;\\`;const FundingHeaderItemText = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const Table = styled.div\\` display: flex; flex-direction: column; border-radius: 6px; border: 1px solid #7b7b7b; background: #fff; .header, .fudning-row { display: flex; justify-content: space-between; } .header { border-bottom: 0.5px solid #7b7b7b; } .fudning-row:not(:last-of-type) { border-bottom: 0.5px solid #7b7b7b; } .item { width: 140px; display: flex; align-items: center; &:nth-of-type(1) { width: 190px; } &:nth-of-type(2) { flex: 1; } } .source { width: 190px; flex-direction: column; align-items: flex-start; gap: 4px; div { font-weight: 600; } div:last-of-type { color: #7b7b7b; font-weight: 400; } } .amount { display: flex; gap: 0.5rem; justify-content: flex-end; div:last-child { font-weight: 600; } } .btns { width: 95px; gap: 2rem; justify-content: space-between; svg { cursor: pointer; path { transition: 300ms ease-in-out; } &:hover { path { fill: black; } } } } .header .item { padding: 10px 1rem; color: #7b7b7b; font-weight: 600; } .fudning-row .item { padding: 1rem 1rem; } @media only screen and (max-width: 769px) { .header { display: none; } .fudning-row { flex-direction: column; } .item { width: 100%; justify-content: flex-start; } }\\`; const InfoIcon = __props__ => { return <svg {...__props__} viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.0001 13.3327V9.99935M10.0001 6.66602H10.0084M18.3334 9.99935C18.3334 14.6017 14.6025 18.3327 10.0001 18.3327C5.39771 18.3327 1.66675 14.6017 1.66675 9.99935C1.66675 5.39698 5.39771 1.66602 10.0001 1.66602C14.6025 1.66602 18.3334 5.39698 18.3334 9.99935Z\" stroke=\"#475467\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg>;}; const A_16 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: flex-start; padding: 1em; gap: 0.75em; background: #fcfcfd; border: 1px solid #d0d5dd; border-radius: 4px; width: 100%; svg { width: 20px; }\\`;const Text = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; padding: 0px; gap: 0.75em;\\`;const Heading = styled.div\\` font-style: normal; font-weight: 600; font-size: 0.95em; line-height: 1.25em; color: #344054;\\`;const Description = styled.p\\` font-style: normal; font-weight: 400; font-size: 0.95em; line-height: 1.25em; color: #475467; white-space: wrap; margin: 0px;\\`; const InfoSegment = ({ title, description}) => { const icon = <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.0001 13.3327V9.99935M10.0001 6.66602H10.0084M18.3334 9.99935C18.3334 14.6017 14.6025 18.3327 10.0001 18.3327C5.39771 18.3327 1.66675 14.6017 1.66675 9.99935C1.66675 5.39698 5.39771 1.66602 10.0001 1.66602C14.6025 1.66602 18.3334 5.39698 18.3334 9.99935Z\" stroke=\"#475467\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg>; return <A_16> <InfoIcon /> <Text> <Heading>{title}</Heading> <Description>{description}</Description> </Text> </A_16>;}; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const props = props; const { projectId } = props.alem.useParams(); const HORIZON_CONTRACT_ID = \"nearhorizon.near\"; const SOCIAL_CONTRACT_ID = \"social.near\"; const ownerId = \"potlock.near\"; Big.PE = 100; const FIFTY_TGAS = \"50000000000000\"; const THREE_HUNDRED_TGAS = \"300000000000000\"; const MIN_PROPOSAL_DEPOSIT_FALLBACK = \"100000000000000000000000\"; const DEFAULT_BANNER_IMAGE_CID = \"bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\"; if (!context.accountId) { return <InfoSegment {...{ title: \"Not logged in!\", description: \"You must log in to create a new project!\" }} />; } const existingHorizonProject = Near.view(HORIZON_CONTRACT_ID, \"get_project\", { account_id: context.accountId }); const registrations = ListsSDK.getRegistrations() || []; State.init({ isDao: false, daoAddressTemp: \"\", daoAddress: \"\", daoAddressError: \"\", existingSocialData: {}, backgroundImage: { ipfs_cid: DEFAULT_BANNER_IMAGE_CID }, profileImage: \"\", name: \"\", nameError: \"\", originalCategories: [], categories: [], categoriesError: \"\", description: \"\", descriptionError: \"\", publicGoodReason: \"\", publicGoodReasonError: \"\", hasSmartContracts: false, originalSmartContracts: [], smartContracts: [[\"\", \"\"]], originalGithubRepos: [], githubRepos: [[\"\"]], hasReceivedFunding: false, fundingSourceIndex: null, originalFundingSources: [], fundingSources: [], website: \"\", websiteError: \"\", twitter: \"\", twitterError: \"\", telegram: \"\", telegramError: \"\", github: \"\", githubError: \"\", socialDataFetched: false, socialDataIsFetching: false, isMultiAccountModalOpen: false, teamMember: \"\", teamMembers: [], nearAccountIdError: \"\", registrationSuccess: false, showAlert: false, alertMessage: \"\" }); const CATEGORY_MAPPINGS = { SOCIAL_IMPACT: \"Social Impact\", NON_PROFIT: \"NonProfit\", CLIMATE: \"Climate\", PUBLIC_GOOD: \"Public Good\", DE_SCI: \"DeSci\", OPEN_SOURCE: \"Open Source\", COMMUNITY: \"Community\", EDUCATION: \"Education\", _deprecated: { \"social-impact\": \"SOCIAL_IMPACT\", \"non-profit\": \"NON_PROFIT\", climate: \"CLIMATE\", \"public-good\": \"PUBLIC_GOOD\", \"de-sci\": \"DE_SCI\", \"open-source\": \"OPEN_SOURCE\", community: \"COMMUNITY\", education: \"EDUCATION\" } }; const CHAIN_OPTIONS = { NEAR: { isEVM: false }, Solana: { isEVM: false }, Ethereum: { isEVM: true }, Polygon: { isEVM: true }, Avalanche: { isEVM: true }, Optimism: { isEVM: true }, Arbitrum: { isEVM: true }, BNB: { isEVM: true }, Sui: { isEVM: false }, Aptos: { isEVM: false }, Polkadot: { isEVM: false }, Stellar: { isEVM: false }, ZkSync: { isEVM: false }, Celo: { isEVM: true }, Aurora: { isEVM: true }, Injective: { isEVM: true }, Base: { isEVM: false }, Manta: { isEVM: false }, Fantom: { isEVM: true }, ZkEVM: { isEVM: true }, Flow: { isEVM: false }, Tron: { isEVM: true }, MultiverseX: { isEVM: false }, Scroll: { isEVM: true }, Linea: { isEVM: true }, Metis: { isEVM: true } }; const accountId = projectId ? projectId : state.isDao ? state.daoAddress : context.accountId; const policy = Near.view(accountId, \"get_policy\", {}); const userHasPermissions = policy == null ? false : policy ? doesUserHaveDaoFunctionCallProposalPermissions(context.accountId, policy) : projectId === context.accountId; const setSocialData = (accountId, shouldSetTeamMembers) => { Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${accountId}/**\\`] }).then((socialData) => { if (!socialData || !socialData[accountId].profile) { State.update({ socialDataFetched: true, name: \"\", originalCategories: [], categories: [], description: \"\", website: \"\", twitter: \"\", telegram: \"\", github: \"\", teamMembers: [] }); return; } const profileData = socialData[accountId].profile; const backgroundImage = profileData.backgroundImage; const profileImage = profileData.image || \"\"; const description = profileData.description || \"\"; const publicGoodReason = profileData.plPublicGoodReason || \"\"; let categories = []; if (profileData.plCategories) { categories = JSON.parse(profileData.plCategories); } else if (profileData.category) { if (typeof profileData.category == \"string\") { const availableCategory = CATEGORY_MAPPINGS[CATEGORY_MAPPINGS._deprecated[profileData.category]]; if (availableCategory) { categories.push(availableCategory); } } } const smartContracts = profileData.plSmartContracts ? Object.entries(JSON.parse(profileData.plSmartContracts)).reduce((accumulator, [chain, contracts]) => { const contractsForChain = Object.keys(contracts).map((contractAddress) => { return [chain, contractAddress]; }); return accumulator.concat(contractsForChain); }, []) : []; const hasSmartContracts = smartContracts.length > 0; smartContracts.push([\"\", \"\"]); const githubRepos = profileData.plGithubRepos ? JSON.parse(profileData.plGithubRepos).map((repo) => [repo]) : []; const originalGithubRepos = githubRepos; githubRepos.push([\"\"]); const fundingSources = profileData.plFundingSources ? JSON.parse(profileData.plFundingSources) : []; const hasReceivedFunding = fundingSources.length > 0; const linktree = profileData.linktree || {}; const twitter = linktree.twitter || \"\"; const telegram = linktree.telegram || \"\"; const github = linktree.github || \"\"; const website = linktree.website || \"\"; const team = getTeamMembersFromSocialProfileData(profileData); const stateUpdates = { existingSocialData: socialData[accountId], backgroundImage, profileImage, name: profileData?.name || \"\", description, publicGoodReason, originalCategories: categories, categories, hasSmartContracts, originalSmartContracts: smartContracts, smartContracts, originalGithubRepos, githubRepos, hasReceivedFunding, originalFundingSources: fundingSources, fundingSources, twitter, telegram, github, website, socialDataFetched: true }; if (backgroundImage) { stateUpdates.backgroundImage = backgroundImage; } if (shouldSetTeamMembers) { stateUpdates.teamMembers = team; } State.update(stateUpdates); }).catch((e) => { console.log(\"error getting social data: \", e); State.update({ socialDataFetched: true }); }); }; useEffect(() => { if (state.isDao && state.daoAddress) { setSocialData(state.daoAddress, true); } else if (!state.isDao && context.accountId && !state.socialDataFetched) { setSocialData(context.accountId, true); } }, [state.socialDataFetched, state.isDao, state.daoAddress, context.accountId]); const isCreateProjectDisabled = !state.profileImage || !state.backgroundImage || state.daoAddressError || !state.name || state.nameError || !state.description || state.descriptionError || !state.publicGoodReason || state.publicGoodReasonError || state.categories.includes(CATEGORY_MAPPINGS.OPEN_SOURCE) && !state.githubRepos.filter((val) => val[0]).length || state.hasSmartContracts && !state.smartContracts.filter((val) => val[0]).length || state.hasReceivedFunding && !state.fundingSources.length || !state.categories.length || state.categoriesError; const deepObjectDiff = (objOriginal, objUpdated) => { if (!objUpdated) objUpdated = {}; let diff = {}; function findDiff(original, updated, diffObj) { Object.keys(updated).forEach((key) => { const updatedValue = updated[key]; const originalValue = original ? original[key] : undefined; if (typeof updatedValue === \"object\" && updatedValue !== null && (originalValue === undefined || typeof originalValue === \"object\" && originalValue !== null)) { const nestedDiff = originalValue ? findDiff(originalValue, updatedValue, {}) : updatedValue; if (Object.keys(nestedDiff).length > 0) { diffObj[key] = nestedDiff; } } else if (updatedValue !== originalValue) { diffObj[key] = updatedValue; } }); return diffObj; } return findDiff(objOriginal, objUpdated, diff); }; const handleCreateOrUpdateProject = (e) => { if (isCreateProjectDisabled) return; const daoAddressValid = state.isDao ? validateNearAddress(state.daoAddress) : true; if (!daoAddressValid) { State.update({ daoAddressError: \"Invalid NEAR account ID\" }); return; } const formattedSmartContracts = state.smartContracts.reduce((accumulator, [chain, contractAddress]) => { if (!chain || !contractAddress) return accumulator; if (!accumulator[chain]) { accumulator[chain] = {}; } accumulator[chain][contractAddress] = \"\"; return accumulator; }, {}); const socialData = { profile: { name: state.name, plCategories: JSON.stringify(state.categories), description: state.description, plPublicGoodReason: state.publicGoodReason, plSmartContracts: state.hasSmartContracts ? JSON.stringify(formattedSmartContracts) : null, plGithubRepos: JSON.stringify(state.githubRepos.map((repo) => repo[0]).filter((val) => val)), plFundingSources: JSON.stringify(state.fundingSources), linktree: { website: state.website, twitter: state.twitter, telegram: state.telegram, github: state.github }, plTeam: JSON.stringify(state.teamMembers) }, index: { star: { key: { type: \"social\", path: \\`\\${ownerId}/widget/Index\\` }, value: { type: \"star\" } }, notify: { key: ownerId, value: { type: \"star\", item: { type: \"social\", path: \\`\\${ownerId}/widget/Index\\` } } } }, graph: { star: { [ownerId]: { widget: { Index: \"\" } } }, follow: { [ownerId]: \"\" } } }; if (state.backgroundImage) { socialData.profile.backgroundImage = state.backgroundImage; } if (state.profileImage) { socialData.profile.image = state.profileImage; } const diff = deepObjectDiff(state.existingSocialData, socialData); const socialArgs = { data: { [accountId]: diff } }; const potlockRegistryArgs = { list_id: 1 }; const horizonArgs = { account_id: state.isDao ? state.daoAddress : context.accountId }; Near.asyncView(SOCIAL_CONTRACT_ID, \"get_account\", { account_id: state.isDao ? state.daoAddress : context.accountId }).then((account) => { const socialTransaction = { contractName: SOCIAL_CONTRACT_ID, methodName: \"set\", args: socialArgs }; let depositFloat = JSON.stringify(socialArgs).length * 0.00015; if (!account) { depositFloat += 0.1; } socialTransaction.deposit = Big(depositFloat).mul(Big(10).pow(24)); let transactions = [socialTransaction]; if (!props.edit) { transactions.push({ contractName: ListsSDK.getContractId(), methodName: \"register_batch\", deposit: Big(0.05).mul(Big(10).pow(24)), args: potlockRegistryArgs }); if (!existingHorizonProject) { transactions.push({ contractName: HORIZON_CONTRACT_ID, methodName: \"add_project\", args: horizonArgs }); } } if (state.isDao) { const clonedTransactions = JSON.parse(JSON.stringify(transactions)); transactions = clonedTransactions.map((tx) => { const action = { method_name: tx.methodName, gas: FIFTY_TGAS, deposit: tx.deposit ? tx.deposit.toString() : \"0\", args: Buffer.from(JSON.stringify(tx.args), \"utf-8\").toString(\"base64\") }; return { ...tx, contractName: state.daoAddress, methodName: \"add_proposal\", args: { proposal: { description: props.edit ? \"Update project on Potlock (via NEAR Social)\" : \"Create project on Potlock (3 steps: Register information on NEAR Social, register on Potlock, and register on NEAR Horizon)\", kind: { FunctionCall: { receiver_id: tx.contractName, actions: [action] } } } }, deposit: policy.proposal_bond || MIN_PROPOSAL_DEPOSIT_FALLBACK, gas: THREE_HUNDRED_TGAS }; }); } Near.call(transactions); const pollIntervalMs = 1000; const pollId = setInterval(() => { ListsSDK.asyncGetRegistration(null, context.accountId).then((_project) => { if (_project) { clearInterval(pollId); State.update({ registrationSuccess: true }); } }); }, pollIntervalMs); }); }; if (projectId) { Near.asyncView(projectId, \"get_policy\", {}).then((policy) => { if (policy) { State.update({ isDao: true, daoAddress: projectId, daoAddressTemp: projectId }); } }); } const registeredProject = useMemo(() => { return ListsSDK.getRegistration(null, state.isDao ? state.daoAddress : context.accountId); }, [state.isDao, state.daoAddress]); console.log(\"registeredProject: \", registeredProject); const proposals = Near.view(state.daoAddress, \"get_proposals\", { from_index: 0, limit: 1000 }); const proposalInProgress = useMemo(() => { if (!state.isDao || !state.daoAddress || !proposals) return false; return proposals?.find((proposal) => { return proposal.status == \"InProgress\" && proposal.kind.FunctionCall?.receiver_id == ListsSDK.getContractId() && proposal.kind.FunctionCall?.actions[0]?.method_name == \"register\"; }); }, [state, proposals]); const handleAddTeamMember = () => { let isValid = validateNearAddress(state.teamMember); if (!isValid) { State.update({ nearAccountIdError: \"Invalid NEAR account ID\" }); return; } if (!state.teamMembers.find((tm) => tm == state.teamMember)) { State.update({ teamMembers: [...state.teamMembers, state.teamMember], teamMember: \"\", nearAccountIdError: \"\" }); } }; const FormSectionLeft = (title, description, isRequired) => { return <FormSectionLeftDiv> <FormSectionTitle>{title}</FormSectionTitle> <FormSectionDescription>{description}</FormSectionDescription> <FormSectionIsRequired style={{ color: isRequired ? \"#DD5633\" : \"#7B7B7B\" }}> {isRequired ? \"Required\" : \"Optional\"} {isRequired && <SvgContainer style={{ top: -6, left: -26 }}> <svg width=\"117\" height=\"31\" viewBox=\"0 0 117 31\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M81.8 3.40116C82.247 3.1908 83.0709 3.13488 82.6 2.60116C81.0461 0.840105 83.0819 0.798833 78.6667 1.22338C65.6302 2.47689 52.5192 4.47997 39.6667 6.95672C31.3106 8.56697 19.0395 10.1936 12.7333 17.09C3.95785 26.6869 29.2286 29.1656 32.9333 29.3567C53.953 30.4413 75.9765 28.9386 96.5111 24.1789C99.8286 23.41 122.546 18.5335 112.733 11.5345C107.621 7.88815 100.796 6.47335 94.7333 5.75672C77.7504 3.74928 60.1141 5.22649 43.2222 7.35671C28.8721 9.16641 14.4138 11.8506 1 17.4012\" stroke=\"#2E2E2E\" stroke-width=\"1.8\" stroke-linecap=\"round\" /> </svg> </SvgContainer>} </FormSectionIsRequired> </FormSectionLeftDiv>; }; const DeleteIcon = (props) => <A_15 {...props} viewBox=\"0 0 12 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M2.5 14C2.0875 14 1.73437 13.8531 1.44062 13.5594C1.14687 13.2656 1 12.9125 1 12.5V2.5H0V1H4V0H8V1H12V2.5H11V12.491C11 12.9137 10.8531 13.2708 10.5594 13.5625C10.2656 13.8542 9.9125 14 9.5 14H2.5ZM9.5 2.5H2.5V12.5H9.5V2.5ZM4 11H5.5V4H4V11ZM6.5 11H8V4H6.5V11Z\" fill=\"#7B7B7B\" /> </A_15>; if (props.edit && !userHasPermissions) { return <h3 style={{ textAlign: \"center\", paddingTop: \"32px\" }}>Unauthorized</h3>; } const uploadFileUpdateState = (body, callback) => { asyncFetch(\"https://ipfs.near.social/add\", { method: \"POST\", headers: { Accept: \"application/json\" }, body }).then(callback); }; console.log(state.fundingSources); return <A_13> {!state.socialDataFetched || !registrations ? <div className=\"spinner-border text-secondary\" role=\"status\" /> : proposalInProgress ? <A_13 style={{ padding: \"32px 16px\", justifyContent: \"center\", alignItems: \"center\", wordWrap: \"break-word\" }}> <h1 style={{ textAlign: \"center\" }}>You have a DAO proposal in progress.</h1> <h5 style={{ wordWrap: \"break-word\", textAlign: \"center\" }}> Please come back once voting on your proposal has been completed. </h5> <div style={{ fontStyle: \"italic\", fontFamily: \"sans-serif\", wordWrap: \"break-word\", textAlign: \"center\" }}> NB: This proposal consists of 3 steps (individual proposals): Register information on NEAR Social, register on Potlock, and register on NEAR Horizon. </div> <a target=\"_blank\" href={\\`https://near.org/sking.near/widget/DAO.Page?daoId=\\${state.daoAddress}&tab=proposal&proposalId=\\${proposalInProgress.id}\\`} style={{ marginTop: \"16px\" }}> View DAO Proposal </a> </A_13> : !props.edit && (registeredProject || state.registrationSuccess) ? <> <h1 style={{ textAlign: \"center\" }}>You've successfully registered!</h1> <ButtonsContainer> <Button {...{ type: \"primary\", text: \"View your project\", disabled: false, href: hrefWithParams(\\`?tab=project&projectId=\\${registeredProject?.id || context.accountId}\\`) }} /> <Button {...{ type: \"secondary\", text: \"View all projects\", disabled: false, href: hrefWithParams(\\`?tab=projects\\`) }} /> </ButtonsContainer> </> : <> <BannerHeader {...{ projectId: state.isDao && state.daoAddress ? state.daoAddress : context.accountId, backgroundImage: state.backgroundImage, profileImage: state.profileImage, bgImageOnChange: (files) => { if (files) { uploadFileUpdateState(files[0], (res) => { const ipfs_cid = res.body.cid; State.update({ backgroundImage: { ipfs_cid } }); }); } }, profileImageOnChange: (files) => { if (files) { uploadFileUpdateState(files[0], (res) => { const ipfs_cid = res.body.cid; State.update({ profileImage: { ipfs_cid } }); }); } }, children: <LowerBannerContainer> <LowerBannerContainerLeft> <AddTeamMembers onClick={() => State.update({ isMultiAccountModalOpen: true })}> {state.teamMembers.length > 0 ? \"Add or remove team members\" : \"Add team members\"} </AddTeamMembers> </LowerBannerContainerLeft> <LowerBannerContainerRight> <Widget loading=\" \" code={props.alem.componentsCode.AccountsStack} props={{ ...{ ...{ accountIds: state.teamMembers, sendToBack: state.isMultiAccountModalOpen }, ...props } }} /> </LowerBannerContainerRight> </LowerBannerContainer> }} /> <FormBody> <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Project details\", \"Give an overview of your project including background details and your mission.\", true)} <FormSectionRightDiv> <CheckBox {...{ id: \"masterSelector\", checked: state.isDao, onClick: (e) => { State.update({ isDao: e.target.checked }); if (!e.target.checked && context.accountId) { setSocialData(context.accountId); } else { if (state.daoAddress) { setSocialData(state.daoAddress); } } }, label: \"Register as DAO\", disabled: props.edit, containerStyle: { marginBottom: \"24px\" } }} /> <A_62 {...{ label: state.isDao ? \"DAO address *\" : \"Project ID *\", value: state.isDao ? state.daoAddressTemp : context.accountId, disabled: !state.isDao, onChange: (daoAddress) => State.update({ daoAddressTemp: daoAddress.toLowerCase(), daoAddressError: \"\" }), validate: () => { if (state.isDao) { const isValid = validateNearAddress(state.daoAddressTemp); if (!isValid) { State.update({ daoAddressError: \"Invalid NEAR account ID\" }); return; } const NO_PERMISSIONS_ERROR = \"You do not have required roles for this DAO\"; Near.asyncView(state.daoAddressTemp, \"get_policy\", {}).then((policy) => { const userRoles = policy.roles.filter((role) => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(context.accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); if (!allowed) { State.update({ daoAddressError: NO_PERMISSIONS_ERROR }); } else { const councilRole = policy.roles.find((role) => role.name === \"council\"); const councilTeamMembers = councilRole?.kind?.Group || []; State.update({ daoAddress: state.daoAddressTemp, teamMembers: councilTeamMembers }); } }).catch((e) => { console.log(\"error getting DAO policy: \", e); State.update({ daoAddressError: NO_PERMISSIONS_ERROR }); }); setSocialData(state.daoAddressTemp, false); } State.update({ daoAddressError: \"\" }); }, error: state.isDao ? state.daoAddressError : \"\" }} /> <Space style={{ height: \"24px\" }} /> <A_62 {...{ label: \"Project name *\", placeholder: \"Enter project name\", value: state.name, onChange: (name) => State.update({ name }), validate: () => { if (state.name.length < 3) { State.update({ nameError: \"Name must be at least 3 characters\" }); return; } if (state.name.length > 100) { State.update({ nameError: \"Name must be less than 100 characters\" }); return; } State.update({ nameError: \"\" }); }, error: state.nameError }} /> <Space style={{ height: \"24px\" }} /> <TextArea {...{ label: \"Overview *\", placeholder: \"Give a short description of your project\", value: state.description, onChange: (description) => State.update({ description }), validate: () => { if (state.description.length > 500) { State.update({ descriptionError: \"Description must be less than 500 characters\" }); return; } State.update({ descriptionError: \"\" }); }, error: state.descriptionError }} /> <Space style={{ height: \"24px\" }} /> <TextArea {...{ label: \"Reason for considering yourself a public good *\", placeholder: \"Type response\", value: state.publicGoodReason, onChange: (publicGoodReason) => State.update({ publicGoodReason }), validate: () => { if (state.publicGoodReason.length > 500) { State.update({ publicGoodReasonError: \"Response must be less than 500 characters\" }); return; } State.update({ publicGoodReasonError: \"\" }); }, error: state.publicGoodReasonError }} /> <Space style={{ height: \"24px\" }} /> <SelectMultiple {...{ label: \"Select category (select multiple) *\", placeholder: \"Choose category\", options: Object.values(CATEGORY_MAPPINGS).filter((el) => typeof el === \"string\"), onChange: (categories) => { State.update({ categories }); }, selected: state.categories }} /> <Space style={{ height: \"24px\" }} /> <CheckBox {...{ id: \"hasSmartContractsSelector\", checked: state.hasSmartContracts, onClick: (e) => { State.update({ hasSmartContracts: e.target.checked }); }, label: \"Yes, my project has smart contracts\", containerStyle: { marginBottom: \"16px\" } }} /> <CheckBox {...{ id: \"hasReceivedFundingSelector\", checked: state.hasReceivedFunding, onClick: (e) => { State.update({ hasReceivedFunding: e.target.checked }); }, label: \"Yes, my project has received funding\" }} /> </FormSectionRightDiv> </FormSectionContainer> {state.categories.includes(CATEGORY_MAPPINGS.OPEN_SOURCE) && <> <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Add Your Repositories\", \"Add full URLs for specific github repositories so we can track their popularity.\", true)} <FormSectionRightDiv> {state.githubRepos.map((repo, index) => { return <A_14 style={{ marginBottom: \"12px\" }} key={index}> <A_62 {...{ label: \"GitHub Repo URL #\" + (index + 1), inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.githubRepos[index][0], onChange: (repo) => State.update({ githubRepos: state.githubRepos.map((r, i) => i == index ? [repo] : [r[0]]) }), validate: () => { const isValid = validateGithubRepoUrl(repo); if (!isValid) { State.update({ githubRepos: state.githubRepos.map((r, i) => i == index ? [r[0], \"Invalid GitHub Repo URL\"] : [r[0]]) }); return; } }, error: state.githubRepos[index][1] || \"\" }} /> {state.githubRepos.length > 1 && <div style={{ height: \"100%\", display: \"flex\", alignItems: \"center\" }}> <DeleteIcon onClick={() => { const updatedRepos = state.githubRepos.filter((r, i) => i != index); State.update({ githubRepos: updatedRepos }); }} /> </div>} </A_14>; })} <Button {...{ type: \"tertiary\", text: \"Add another repository\", disabled: !state.githubRepos[state.githubRepos.length - 1][0], onClick: () => { State.update({ githubRepos: [...state.githubRepos, [\"\"]] }); } }} /> </FormSectionRightDiv> </FormSectionContainer> </>} {state.hasSmartContracts && <> <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Smart contracts\", \"Add smart contracts from different chains that belong to your application.\", true)} <FormSectionRightDiv> {state.smartContracts.map(([chain, contractAddress], index) => { return <A_14 style={{ marginBottom: \"12px\" }} key={index}> <Select {...{ label: \"Add chain\", noLabel: false, placeholder: \"Select chain\", options: Object.keys(CHAIN_OPTIONS).map((chain) => ({ text: chain, value: chain })), value: { text: chain, value: chain }, onChange: (chain) => { const updatedSmartContracts = state.smartContracts.map((sc, i) => { if (i == index) { return [chain.value, sc[1]]; } return sc; }); State.update({ smartContracts: updatedSmartContracts }); } }} /> <A_62 {...{ label: \"Contract address\", placeholder: \"Enter address\", value: contractAddress, onChange: (contractAddress) => { const updatedSmartContracts = state.smartContracts.map((sc, i) => { if (i == index) { return [sc[0], contractAddress]; } return sc; }); State.update({ smartContracts: updatedSmartContracts }); }, validate: () => { const chain = state.smartContracts[index][0]; const isEvm = CHAIN_OPTIONS[chain].isEVM; const isValid = chain == \"NEAR\" ? validateNearAddress(contractAddress) : isEvm ? validateEVMAddress(contractAddress) : true; if (!isValid) { State.update({ smartContracts: state.smartContracts.map((sc, i) => { if (i == index) { return [sc[0], sc[1], \"Invalid address\"]; } return sc; }) }); return; } }, error: state.smartContracts[index][2] || \"\" }} /> {state.smartContracts.length > 1 && <div style={{ height: \"100%\", display: \"flex\", alignItems: \"center\" }}> <DeleteIcon onClick={() => { const updatedSmartContracts = state.smartContracts.filter((sc, i) => i != index); State.update({ smartContracts: updatedSmartContracts }); }} /> </div>} </A_14>; })} <Button {...{ type: \"tertiary\", text: \"Add another contract\", disabled: !state.smartContracts[state.smartContracts.length - 1][0] && !state.smartContracts[state.smartContracts.length - 1][1], onClick: () => { State.update({ smartContracts: [...state.smartContracts, [\"\", \"\"]] }); } }} /> </FormSectionRightDiv> </FormSectionContainer> </>} {state.hasReceivedFunding && <> <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Funding sources\", \"Add any previous funding you have received.\", true)} {} </FormSectionContainer> {state.fundingSources.length > 0 && <Table> <div className=\"header\"> <div className=\"item\">Funding source</div> <div className=\"item\">Description</div> <div className=\"item amount\">Amount</div> <div className=\"btns\" /> </div> {state.fundingSources.map((funding, idx) => <div className=\"fudning-row\" key={funding.investorName}> <div className=\"item source\"> <div>{funding.investorName}</div> {funding.date && <div> {new Date(funding.date).toLocaleDateString(\"en-US\", { month: \"numeric\", day: \"numeric\", year: \"numeric\" })} </div>} </div> <div className=\"item\">{funding.description}</div> <div className=\"item amount\"> <div>{funding.denomination}</div> <div>{funding.amountReceived}</div> </div> <div className=\"btns item\"> {} <svg onClick={() => State.update({ fundingSourceIndex: idx })} width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_446_76)\"> <path d=\"M15.369 0.290547C14.979 -0.0994531 14.349 -0.0994531 13.959 0.290547L12.129 2.12055L15.879 5.87055L17.709 4.04055C18.099 3.65055 18.099 3.02055 17.709 2.63055L15.369 0.290547Z\" fill=\"#7B7B7B\" /> <path d=\"M-0.000976562 18.0005H3.74902L14.809 6.94055L11.059 3.19055L-0.000976562 14.2505V18.0005ZM1.99902 15.0805L11.059 6.02055L11.979 6.94055L2.91902 16.0005H1.99902V15.0805Z\" fill=\"#7B7B7B\" /> </g> <defs> <clipPath id=\"clip0_446_76\"> <rect width=\"18\" height=\"18\" fill=\"white\" /> </clipPath> </defs> </svg> {} <svg onClick={() => { const updatedFundingSources = state.fundingSources.filter((fudning, i) => i !== idx); State.update({ fundingSources: updatedFundingSources }); }} width=\"14\" height=\"18\" viewBox=\"0 0 14 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11 6V16H3V6H11ZM9.5 0H4.5L3.5 1H0V3H14V1H10.5L9.5 0ZM13 4H1V16C1 17.1 1.9 18 3 18H11C12.1 18 13 17.1 13 16V4Z\" fill=\"#7B7B7B\" /> </svg> </div> </div>)} </Table>} <Button {...{ type: \"tertiary\", text: \"Add funding source\", style: { width: \"fit-content\", marginTop: \"1rem\", marginBottom: \"3rem\" }, disabled: state.fundingSources.some((fs) => !fs.investorName || !fs.amountReceived || !fs.denomination || !fs.description), onClick: () => { const updatedFundingSources = [...state.fundingSources, { investorName: \"\", description: \"\", amountReceived: \"\", denomination: \"\" }]; State.update({ fundingSources: updatedFundingSources, fundingSourceIndex: updatedFundingSources.length - 1 }); } }} /> </>} <FormDivider /> <FormSectionContainer> {FormSectionLeft(\"Social links\", \"Add your project social links to so supporters can connect with you directly.\", false)} <FormSectionRightDiv> <A_62 {...{ label: \"Twitter\", preInputChildren: <InputPrefix>twitter.com/</InputPrefix>, inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.twitter, onChange: (twitter) => State.update({ twitter: twitter.trim() }), validate: () => { if (state.twitter.length > 15) { State.update({ twitterError: \"Invalid Twitter handle\" }); return; } State.update({ twitterError: \"\" }); }, error: state.twitterError }} /> <Space style={{ height: \"24px\" }} /> <A_62 {...{ label: \"Telegram\", preInputChildren: <InputPrefix>t.me/</InputPrefix>, inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.telegram, onChange: (telegram) => State.update({ telegram: telegram.trim() }), validate: () => {}, error: state.telegramError }} /> <Space style={{ height: \"24px\" }} /> <A_62 {...{ label: \"GitHub\", preInputChildren: <InputPrefix>github.com/</InputPrefix>, inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.github, onChange: (github) => State.update({ github: github.trim() }), validate: () => {}, error: state.githubError }} /> <Space style={{ height: \"24px\" }} /> <A_62 {...{ label: \"Website\", preInputChildren: <InputPrefix>https://</InputPrefix>, inputStyles: { borderRadius: \"0px 4px 4px 0px\" }, value: state.website, onChange: (website) => State.update({ website: website.trim() }), validate: () => {}, error: state.websiteError }} /> <Space style={{ height: \"24px\" }} /> <Button {...{ type: \"primary\", prefix: \"https://\", text: props.edit ? state.isDao ? \"Add proposal to update project\" : \"Update your project\" : state.isDao ? \"Add proposal to create project\" : \"Create new project\", disabled: isCreateProjectDisabled, onClick: handleCreateOrUpdateProject }} /> <Space style={{ height: \"24px\" }} /> </FormSectionRightDiv> </FormSectionContainer> </FormBody> {state.isMultiAccountModalOpen && <ModalMultiAccount {...{ onClose: () => State.update({ isMultiAccountModalOpen: false }), titleText: \"Add team members\", descriptionText: \"Add NEAR account IDs for your team members.\", inputValue: state.teamMember, onInputChange: (teamMember) => { State.update({ teamMember, nearAccountIdError: \"\" }); }, handleAddAccount: handleAddTeamMember, handleRemoveAccount: (accountId) => { State.update({ teamMembers: state.teamMembers.filter((tm) => tm != accountId) }); }, accountError: state.nearAccountIdError, accountIds: state.teamMembers, unitText: \"member\" }} />} {state.fundingSourceIndex !== null && <Widget loading=\" \" code={props.alem.componentsCode.ModalAddFundingSource} props={{ ...{ ...{ onClose: () => {const updatedFundingSources = state.fundingSources.filter((fs) => fs.investorName && fs.amountReceived && fs.denomination && fs.description);State.update({ fundingSources: updatedFundingSources, fundingSourceIndex: null });}, fundingSources: state.fundingSources, fundingSourceIndex: state.fundingSourceIndex, handleAddFundingSource: ({ investorName, date, description, amountReceived, denomination }) => {const updatedFundingSources = state.fundingSources.map((fs, i) => {if (i == state.fundingSourceIndex) {return { investorName, date, description, amountReceived, denomination };}return fs;});State.update({ fundingSources: updatedFundingSources, fundingSourceIndex: null });} }, ...props } }} />} </>} </A_13>; `, CheckoutBreakdown: ` const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useCart = () => useContext(\"cart-context\"); const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const A_18 = styled.div\\` display: flex; flex-direction: column; gap: 24px; margin-top: 20px; width: 380px; /* background: white; */ @media screen and (max-width: 768px) { width: 100%; margin-bottom: 50px; }\\`;const Title = styled.div\\` color: #2e2e2e; font-size: 24px; font-weight: 600; line-height: 32px; word-wrap: break-word;\\`;const CurrencyHeader = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px; border-radius: 5px; background: #f0f0f0;\\`;const CurrencyHeaderText = styled.div\\` color: #7b7b7b; font-size: 12px; font-weight: 400; line-height: 14px; word-wrap: break-word;\\`;const BreakdownItemContainer = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px;\\`;const BreakdownItemLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; width: 50%; gap: 8px;\\`;const BreakdownItemRight = styled.div\\` display: flex; flex: 1; flex-direction: row; align-items: center; justify-content: flex-end;\\`;const BreakdownItemText = styled.div\\` color: #2e2e2e; font-size: 14px; font-weight: 400; line-height: 16px; word-wrap: break-word;\\`;const CurrencyIcon = styled.img\\` width: 20px; height: 20px;\\`;const TotalContainer = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px; border-top: 1px #7b7b7b solid;\\`;const TotalText = styled.div\\` color: #2e2e2e; font-size: 14px; font-weight: 600; line-height: 20px; word-wrap: break-word;\\`;const ErrorText = styled.div\\` color: #dd3345; font-size: 14px; font-weight: 400; line-height: 20px; word-wrap: break-word; width: 100%; text-align: center;\\`; const props = props; const { cart, clearCart } = useCart(); const { SUPPORTED_FTS } = constants; const DONATION_CONTRACT_ID = DonateSDK.getContractId(); Big.PE = 100; const MIN_REQUIRED_DONATION_AMOUNT_PER_PROJECT = 0.1; const [tokens, amountsByFt, totalAmount, donationTooSmall] = useMemo(() => { const tokens = {}; const amountsByFt = {}; let donationTooSmall = false; Object.entries(cart || {}).forEach(([projectId, { token, amount }]) => { const ft = token.text; if (!amountsByFt[ft]) amountsByFt[ft] = 0; amountsByFt[ft] += parseFloat(amount || 0); if (amountsByFt[ft] < MIN_REQUIRED_DONATION_AMOUNT_PER_PROJECT) donationTooSmall = true; tokens[ft] = token; }); const totalAmount = Object.values(amountsByFt).reduce((acc, amount) => acc + amount, 0); return [tokens, amountsByFt, totalAmount, donationTooSmall]; }, [props]); const handleDonate = () => { const transactions = []; let potIdContained; Object.entries(cart).forEach(([projectId, { token, amount, referrerId, note, potId }]) => { const isFtDonation = token.text != \"NEAR\"; const amountIndivisible = Big(parseFloat(amount)).mul(Big(10).pow(isFtDonation ? token.decimals : 24)); const args = {}; if (isFtDonation) { args.receiver_id = DONATION_CONTRACT_ID; args.amount = amountIndivisible.toString(); args.memo = JSON.stringify({ recipient_id: projectId, referrer_id: referrerId || null, bypass_protocol_fee: false, message: note || null }); } else { if (potId) args.project_id = projectId;else args.recipient_id = projectId; args.referrer_id = referrerId; args.message = note; potIdContained = potId; } transactions.push({ contractName: isFtDonation ? token.id : potId ?? DONATION_CONTRACT_ID, methodName: isFtDonation ? \"ft_transfer_call\" : \"donate\", args, deposit: isFtDonation ? \"1\" : amountIndivisible.toString(), gas: \"300000000000000\" }); }); if (Object.keys(amountsByFt).some(ft => ft !== \"NEAR\")) { const requiredDepositFloat = transactions.reduce((acc, { methodName, args }) => { if (methodName === \"donate\") return acc; const baseAmount = 0.008; const argsAmount = (args.message.length || 0) * 0.0001; return acc + baseAmount + argsAmount; }, 0); transactions.unshift({ contractName: DONATION_CONTRACT_ID, methodName: \"storage_deposit\", args: {}, deposit: Big(requiredDepositFloat).mul(Big(10).pow(24)), gas: \"100000000000000\" }); } const now = Date.now(); Near.call(transactions); const pollIntervalMs = 1000; const pollId = setInterval(() => { (potIdContained ? PotSDK.asyncGetDonationsForDonor(potIdContained, context.accountId) : DonateSDK.asyncGetDonationsForDonor(context.accountId)).then(donations => { const foundDonations = []; for (const donation of donations) { const { recipient_id, project_id, donated_at_ms, donated_at, total_amount } = donation; const matchingCartItem = cart[project_id || recipient_id]; if (matchingCartItem && (donated_at_ms > now || donated_at > now)) { foundDonations.push(donation); } } if (foundDonations.length) { clearInterval(pollId); props.updateSuccessfulDonationRecipientId(foundDonations[0].recipient_id); clearCart(); } }); }, pollIntervalMs); }; return <A_18> <Title>Breakdown summary</Title> <CurrencyHeader> <CurrencyHeaderText>Currency</CurrencyHeaderText> <CurrencyHeaderText>Amount</CurrencyHeaderText> </CurrencyHeader> {Object.entries(amountsByFt).map(([ft, amount]) => { const amountFloat = parseFloat(amount || 0); return <BreakdownItemContainer> <BreakdownItemLeft> {ft == \"NEAR\" ? <CurrencyIcon src={SUPPORTED_FTS.NEAR.iconUrl} /> : <CurrencyIcon src={tokens[ft].icon} />} <BreakdownItemText>{tokens[ft].text}</BreakdownItemText> </BreakdownItemLeft> <BreakdownItemRight> <BreakdownItemText>{amountFloat.toFixed(2)}</BreakdownItemText> </BreakdownItemRight> </BreakdownItemContainer>; })} {Object.keys(amountsByFt).length <= 1 && amountsByFt.NEAR && <TotalContainer> <TotalText>Total</TotalText> <TotalText>{totalAmount.toFixed(2)}</TotalText> </TotalContainer>} <Button {...{ type: \"primary\", text: \\`Process Donation\\`, disabled: !Object.keys(cart).length || donationTooSmall || !context.accountId, onClick: handleDonate, style: { width: \"100%\" } }} /> {donationTooSmall && <ErrorText>Minimum required donation per project is {MIN_REQUIRED_DONATION_AMOUNT_PER_PROJECT} N</ErrorText>} {!context.accountId && <ErrorText>Please sign in to donate</ErrorText>} </A_18>; `, CheckoutItem: ` function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_233 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_234 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_235 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 10px; background: #ffffff; border: 1px solid #d0d5dd; /* box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); */ box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset; border-radius: 4px; color: #101828; width: 100%;\\`;const Placeholder = styled.span\\` color: #a0a3a8;\\`;const scaleOut = styled.keyframes\\` from { transform: scaleY(0); } to { transform: scaleY(1); }\\`;const SelectContent = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; gap: 0.5em; width: 100%; border: 1px solid #d0d5dd; border-radius: 4px; background: #ffffff; z-index: 3 !important;\\`;const Viewport = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; width: 100%;\\`;const Item = styled.button\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 0.5em; width: 100%; cursor: pointer; background: transparent; border: none; transition: background 0.2s ease-in-out; &:nth-child(n + 1) { border-top: 1px solid #d0d5dd; } &:hover { background: #d0d5dd; boder: none; } &:focus { outline: none; }\\`; const Select = (componentProps) => { const label = componentProps.label ?? \"Label\"; const noLabel = componentProps.noLabel ?? false; const placeholder = componentProps.placeholder ?? \"Select an option\"; const value = componentProps.value ?? \"\"; const options = componentProps.options ?? []; const onChange = componentProps.onChange ?? (() => {}); const validate = componentProps.validate ?? (() => {}); const error = componentProps.error ?? \"\"; return <A_233 style={componentProps.containerStyles || {}}> {noLabel ? <></> : <A_234>{label}</A_234>} <Select.Root value={value?.value} onValueChange={(value) => onChange(options.find((option) => option.value === value))}> <Select.Trigger asChild={true}> <A_235 style={componentProps.inputStyles || {}}> {componentProps.iconLeft && componentProps.iconLeft} <Select.Value aria-label={value.value} placeholder={<Placeholder>{placeholder}</Placeholder>} /> {} <Select.Icon> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1 1.5L6 6.5L11 1.5\" stroke=\"currentColor\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg> </Select.Icon> {} </A_235> </Select.Trigger> <Select.Content asChild={true}> <SelectContent> <Select.Viewport asChild={true}> <Viewport> {options.map(({ text, value }) => <Select.Item value={value} asChild={true}> <Item> <Select.ItemText>{text}</Select.ItemText> <Select.ItemIndicator> <svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" /> </svg> </Select.ItemIndicator> </Item> </Select.Item>)} </Viewport> </Select.Viewport> </SelectContent> </Select.Content> </Select.Root> </A_233>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const TagContainer = styled.div\\` display: flex; justify-content: center; align-items: center; text-align: center; padding: 6px 8px; border-radius: 4px;\\`;const TagText = styled.span\\` font-size: 14px;\\`; const Tag = __props__ => { const { backgroundColor, borderColor, textColor, text } = __props__; const textStyle = __props__.textStyle || {}; return <TagContainer style={{ backgroundColor: backgroundColor || \"#ffffff\", border: \\`1px solid \\${borderColor || \"#000000\"}\\`, boxShadow: \\`0px -0.699999988079071px 0px \\${borderColor} inset\\` }}> {__props__.preElements} <TagText style={{ color: textColor || \"#000000\", ...textStyle }}> {text} </TagText> </TagContainer>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_237 = (__props__) => <svg {...__props__} style={{ width: 16}} viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg>; const ItemContainer = styled.div\\` display: flex; flex-direction: row; max-width: 800px; background: white; /* background: pink; */ border: 1px solid #dbdbdb; box-shadow: 0px -2px 0px #dbdbdb inset; border-radius: 6px; justify-content: flex-start; align-items: flex-start;\\`;const ItemLeft = styled.div\\` height: 100%; padding: 24px 16px; /* background: green; */\\`;const ItemRight = styled.div\\` display: flex; flex-direction: row; padding: 24px 24px 24px 16px; width: 100%; /* background: yellow; */ border-left: 1px solid #dbdbdb;\\`;const ImageContainer = styled.div\\` display: flex; flex-direction: row; gap: 32px;\\`;const DetailsContainer = styled.div\\` display: flex; flex-direction: column; width: 100%; overflow: hidden;\\`;const A_19 = styled.a\\` color: #2e2e2e; font-size: 16px; line-height: 24px; font-weight: 600; word-wrap: break-word;\\`;const A_20 = styled.div\\` color: #2e2e2e; font-size: 16px; line-height: 24px; font-weight: 400; word-wrap: break-word; overflow-wrap: break-word; margin: 16px 0px 24px 0px;\\`;const FtIcon = styled.img\\` width: 20px; height: 20px;\\`;const A_21 = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center;\\`;const A_22 = styled.svg\\` width: 20px; height: 20px;\\`; const props = props; const { cartItem, checked, handleCheckboxClick } = props; const { referrerId } = props.alem.useParams(); const projectId = cartItem?.id; const isPotDonation = cartItem?.potId; const profile = props.profile || Social.get(\\`\\${projectId}/profile/**\\`, \"final\") || {}; const [itemAmount, setItemAmount] = useState(cartItem?.amount); const [itemToken, setItemToken] = useState(cartItem?.token); const [potDetail, setPotDetail] = useState(null); State.init({ ftBalances: null, denominationOptions: [{ text: \"NEAR\", value: \"NEAR\", selected: itemToken.text === \"NEAR\", decimals: 24 }] }); if (!potDetail && cartItem.potId) { if (cartItem.potDetail) { setPotDetail(cartItem.potDetail); } else { PotSDK.asyncGetConfig(cartItem.potId).then((potDetail) => { setPotDetail(potDetail); }).catch((err) => console.log(\"error while fetching pot detail for cart Item \", err)); } } return <ItemContainer> <ItemLeft> <CheckBox {...{ id: \"selector-\" + projectId, checked, onClick: handleCheckboxClick }} /> </ItemLeft> <ItemRight> <ImageContainer> <ProfileImage {...{ accountId: projectId, style: { width: \"40px\", height: \"40px\", border: \"none\", marginRight: \"24px\" }, className: \"mb-2\", imageClassName: \"rounded-circle w-100 h-100 d-block\", thumbnail: false }} /> </ImageContainer> <DetailsContainer> <A_21> <A_19 href={hrefWithParams(\\`?tab=project&projectId=\\${projectId}\\`)}>{profile.name ?? \"\"}</A_19> <Tag {...{ backgroundColor: isPotDonation ? \"#FEF6EE\" : \"#F6F5F3\", borderColor: isPotDonation ? \"rgba(219, 82, 27, 0.36)\" : \"#DBDBDB\", textColor: isPotDonation ? \"#EA6A25\" : \"#292929\", text: isPotDonation ? potDetail?.pot_name ?? cartItem.potId.slice(0, 20) : \"Direct Donation\" }} /> </A_21> <A_20>{profile.description ?? \"\"}</A_20> <A_62 {...{ label: \"Amount\", placeholder: \"0\", value: itemAmount, onChange: (amount) => { amount = amount.replace(/[^\\\\d.]/g, \"\"); if (amount === \".\") amount = \"0.\"; setItemAmount(amount); }, onBlur: (e) => {}, inputStyles: { textAlign: \"right\", borderRadius: \"0px 4px 4px 0px\" }, preInputChildren: <Select {...{ noLabel: true, placeholder: \"\", options: state.denominationOptions, value: { text: itemToken.text, value: itemToken.value }, onChange: ({ text, value }) => { const token = state.denominationOptions.find((option) => option.text === text); setItemToken(token); setItemAmount(undefined); }, containerStyles: { width: \"auto\" }, inputStyles: { border: \"none\", borderRight: \"1px #F0F0F0 solid\", borderRadius: \"4px 0px 0px 4px\", width: \"auto\", padding: \"12px 16px\", boxShadow: \"0px -2px 0px rgba(93, 93, 93, 0.24) inset\" }, iconLeft: itemToken.text == \"NEAR\" ? <A_237 /> : <FtIcon src={itemToken.icon} /> }} /> }} /> <Widget loading=\" \" code={props.alem.componentsCode.BreakdownSummary} props={{ ...{ ...{ ftIcon: itemToken.icon, referrerId, totalAmount: itemAmount, bypassProtocolFee: false, containerStyle: { marginTop: \"16px\" } }, ...props } }} /> </DetailsContainer> </ItemRight> </ItemContainer>; `, Cart: ` const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useCart = () => useContext(\"cart-context\"); const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_23 = styled.div\\` background: #fafafa; display: flex; flex-direction: row; height: 100%; min-height: 100vh; @media screen and (max-width: 768px) { flex-direction: column; }\\`;const SuccessContainer = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; padding: 24px; gap: 24px;\\`;const ColumnLeft = styled.div\\` display: flex; flex-direction: column; width: 55%; padding: 48px 40px 48px 64px; gap: 48px; @media screen and (max-width: 768px) { width: 100%; padding: 24px 16px 24px 16px; }\\`;const ColumnRight = styled.div\\` flex: 1; padding: 152px 148px 152px 84px; border-left: 1px #c7c7c7 solid; @media screen and (max-width: 768px) { padding: 24px 16px 24px 16px; border-left: none; border-top: 1px #c7c7c7 solid; }\\`;const A_24 = styled.div\\` color: #2e2e2e; font-size: 48px; font-family: Lora; font-weight: 500; line-height: 56px; word-wrap: break-word; text-align: center;\\`;const A_25 = styled.svg\\` width: 1rem; height: 1rem; path { transition: 300ms; }\\`;const ActionsContainer = styled.div\\` width: 100%; padding: 16px; background: white; border: 1px solid #dbdbdb; box-shadow: 0px -2px 0px #dbdbdb inset; border-radius: 6px; overflow: hidden; justify-content: flex-start; align-items: center; display: inline-flex; gap: 24px;\\`;const InnerContainer = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 6px; :hover path { fill: #dd3345; }\\`;const SubTitle = styled.div\\` color: #2e2e2e; font-weight: 600; font-size: 14px;\\`;const TxLink = styled.a\\` color: #2e2e2e; cursor: pointer; &:hover { text-decoration: none; }\\`; const { cart, removeItemsFromCart } = useCart(); const numCartItems = Object.keys(cart).length; const { transactionHashes: passedTransactionHashes } = props.alem.useParams(); const DEFAULT_GATEWAY = \"https://bos.potlock.org/\"; const POTLOCK_TWITTER_ACCOUNT_ID = \"PotLock_\"; const DEFAULT_SHARE_HASHTAGS = [\"BOS\", \"PublicGoods\", \"Donations\"]; State.init({ selectedProjectIds: [], masterSelectorSelected: false, successfulDonationRecipientId: null, successfulDonationsRecipientProfiles: null }); const allSelected = state.selectedProjectIds.length !== 0 && state.selectedProjectIds.length === numCartItems; if (passedTransactionHashes && !state.successfulDonationsRecipientProfiles) { const transactionHashes = passedTransactionHashes.split(\",\"); for (let i = 0; i < transactionHashes.length; i++) { const txHash = transactionHashes[i]; const body = JSON.stringify({ jsonrpc: \"2.0\", id: \"dontcare\", method: \"tx\", params: [txHash, context.accountId] }); const res = fetch(\"https://rpc.mainnet.near.org\", { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body }); if (res.ok) { const methodName = res.body.result.transaction.actions[0].FunctionCall.method_name; const successVal = res.body.result.status?.SuccessValue; const result = JSON.parse(Buffer.from(successVal, \"base64\").toString(\"utf-8\")); const args = JSON.parse(Buffer.from(res.body.result.transaction.actions[0].FunctionCall.args, \"base64\").toString(\"utf-8\")); const recipientId = methodName === \"donate\" ? result.recipient_id : methodName === \"ft_transfer_call\" ? JSON.parse(args.msg).recipient_id : \"\"; if (recipientId) { Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${recipientId}/profile/**\\`] }).then((socialData) => { State.update({ successfulDonationsRecipientProfiles: { ...state.successfulDonationsRecipientProfiles, [recipientId]: socialData[recipientId][\"profile\"] } }); }); } } } } useEffect(() => { if (state.successfulDonationRecipientId && !state.successfulDonationsRecipientProfiles) { Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${state.successfulDonationRecipientId}/profile/**\\`] }).then((socialData) => { State.update({ successfulDonationsRecipientProfiles: { [state.successfulDonationRecipientId]: socialData[state.successfulDonationRecipientId][\"profile\"] } }); }); } }, [state.successfulDonationRecipientId, state.successfulDonationsRecipientProfiles]); const twitterIntent = useMemo(() => { if (!state.successfulDonationsRecipientProfiles) return; const recipientIds = Object.keys(state.successfulDonationsRecipientProfiles); const twitterIntentBase = \"https://twitter.com/intent/tweet?text=\"; let url = DEFAULT_GATEWAY + \\`potlock.near/widget/Index?referrerId=\\${context.accountId}\\`; if (recipientIds.length === 1) { url = url + \\`&tab=project&projectId=\\${recipientIds[0]}\\`; } else { url = url + \\`&tab=projects\\`; } const recipientProfiles = []; for (const [recipientId, profile] of Object.entries(state.successfulDonationsRecipientProfiles)) { const identifier = profile.linktree?.twitter ? \\`@\\${profile.linktree.twitter}\\` : profile.name ? profile.name : recipientId; recipientProfiles.push({ identifier, hasTwitter: !!profile.linktree?.twitter }); } recipientProfiles.sort((a, b) => { if (a.hasTwitter && !b.hasTwitter) return -1; if (!a.hasTwitter && b.hasTwitter) return 1; return 0; }); const sortedIdentifiers = recipientProfiles.map((profile) => profile.identifier); const recipientsText = sortedIdentifiers.join(\" & \"); let text = \\`I just donated to \\${recipientsText} on @\\${POTLOCK_TWITTER_ACCOUNT_ID}! Support public goods at \\`; text = encodeURIComponent(text); url = encodeURIComponent(url); return twitterIntentBase + text + \\`&url=\\${url}\\` + \\`&hashtags=\\${DEFAULT_SHARE_HASHTAGS.join(\",\")}\\`; }, [state.successfulDonationsRecipientProfiles]); return <A_23> {passedTransactionHashes || state.successfulDonationRecipientId ? <SuccessContainer> <A_24>Thanks for donating!</A_24> {twitterIntent && <Button {...{ href: twitterIntent, target: \"_blank\", type: \"primary\", text: \"Share to Twitter\", disabled: !twitterIntent, style: { width: \"300px\" } }} />} <Button {...{ href: hrefWithParams(\\`?tab=projects\\`), type: twitterIntent ? \"secondary\" : \"primary\", text: \"Explore projects\", style: { width: \"300px\" } }} /> </SuccessContainer> : <> <ColumnLeft> <A_24>Donation Cart</A_24> <ActionsContainer> <InnerContainer> <CheckBox {...{ id: \"masterSelector\", disabled: numCartItems === 0, checked: state.masterSelectorSelected, onClick: (e) => { const selectedProjectIds = Object.keys(cart).filter((_) => { if (allSelected) { return false; } return true; }); State.update({ selectedProjectIds, masterSelectorSelected: !allSelected }); } }} /> <SubTitle>Select all</SubTitle> </InnerContainer> <InnerContainer style={{ cursor: \"pointer\" }} onClick={() => { if (state.selectedProjectIds.length === 0) return; removeItemsFromCart(state.selectedProjectIds); State.update({ selectedProjectIds: [], masterSelectorSelected: false }); }}> <A_25 viewBox=\"0 0 12 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M2.5 14C2.0875 14 1.73437 13.8531 1.44062 13.5594C1.14687 13.2656 1 12.9125 1 12.5V2.5H0V1H4V0H8V1H12V2.5H11V12.491C11 12.9137 10.8531 13.2708 10.5594 13.5625C10.2656 13.8542 9.9125 14 9.5 14H2.5ZM9.5 2.5H2.5V12.5H9.5V2.5ZM4 11H5.5V4H4V11ZM6.5 11H8V4H6.5V11Z\" fill=\"#7B7B7B\" /> </A_25> <SubTitle>Delete</SubTitle> </InnerContainer> </ActionsContainer> {numCartItems === 0 ? <div>No items in cart</div> : Object.keys(cart).map((projectId) => { const checked = state.selectedProjectIds.includes(projectId); return <Widget loading=\" \" code={props.alem.componentsCode.CheckoutItem} props={{ ...{ ...{ cartItem: cart[projectId], checked, handleCheckboxClick: (e) => {let selectedProjectIds = state.selectedProjectIds;if (checked) {selectedProjectIds = selectedProjectIds.filter((id) => id !== projectId);} else {selectedProjectIds.push(projectId);}const updatedState = { selectedProjectIds };if (selectedProjectIds.length !== 0 && selectedProjectIds.length !== numCartItems) {updatedState.masterSelectorSelected = false;}State.update(updatedState);} }, ...props } }} />; })} </ColumnLeft> <ColumnRight> <Widget loading=\" \" code={props.alem.componentsCode.CheckoutBreakdown} props={{ ...{ ...{ updateSuccessfulDonationRecipientId: (recipientId) => State.update({ successfulDonationRecipientId: recipientId }) }, ...props } }} /> </ColumnRight> </>} </A_23>; `, DonorsLeaderboard: ` const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_27 = styled.div\\` width: 100%; display: flex; flex-direction: column; align-items: center; gap: 2rem; .transcation { display: flex; flex-direction: column; width: 100%; font-size: 14px; .header { display: flex; align-items: center; justify-content: space-between; padding: 10px; gap: 1rem; background: #f6f5f3; color: #292929; div { width: 130px; display: flex; justify-content: center; align-items: center; font-weight: 600; } } .address { width: 190px !important; margin-right: auto; justify-content: start !important; } .rank { width: 80px !important; } } @media only screen and (max-width: 768px) { .transcation { font-size: 12px; .header { padding: 10px 0; div { width: 80px; } } } } @media only screen and (max-width: 480px) { .transcation { font-size: 9px; .address { width: 120px !important; } } }\\`;const TrRow = styled.div\\` display: flex; align-items: center; justify-content: space-between; width: 100%; gap: 1rem; padding: 20px 10px; > div, > span { width: 130px; display: flex; justify-content: center; align-items: center; } .price { display: flex; gap: 1rem; align-items: center; img { width: 1.5rem; } } .address { color: #292929; display: flex; align-items: center; justify-content: flex-start; border-radius: 2px; transition: all 200ms; .profile-image { width: 2rem; height: 2rem; margin-right: 1rem; } } @media only screen and (max-width: 768px) { padding: 10px 0; > div, > span { width: 80px; } .price { gap: 8px; img { width: 1.25rem; } } .address .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0.5rem; } } @media only screen and (max-width: 480px) { .price img { width: 1rem; } }\\`;const NoResult = styled.div\\` font-size: 2rem; text-align: center;\\`; const propsLeaderboard = props; const { sponsors, sortedDonations, filter, currentTab } = propsLeaderboard; const { tab } = props.alem.useParams(); const donations = currentTab === \"sponsors\" ? sponsors : sortedDonations; const isInPot = tab === \"pot\"; const [currentPage, setCurrentPage] = useState(1); const perPage = 30; useEffect(() => { setCurrentPage(1); }, [filter]); const nearLogo = \"https://ipfs.near.social/ipfs/bafkreicdcpxua47eddhzjplmrs23mdjt63czowfsa2jnw4krkt532pa2ha\"; return donations.length ? <A_27> <div className=\"transcation\"> <div className=\"header\"> <div className=\"rank\">Rank</div> <div className=\"address\">Donor</div> <div>Amount</div> {isInPot && <div>Percentage</div>} {A_236 && !isInPot && <div>Amount (USD)</div>} </div> {donations.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation, idx) => { const { donor_id, amount, percentage_share } = donation; return <TrRow> <div className=\"rank\">#{idx + 1 + (currentPage - 1) * perPage}</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`)} className=\"address\" target=\"_blank\"> <ProfileImage style={{}} className=\"profile-image\" accountId={donor_id} /> {_address(donor_id, 15)} </a> <div className=\"price\"> <img src={nearLogo} alt=\"NEAR\" /> {amount.toFixed(2).replace(/[.,]00$/, \"\")} </div> {isInPot && <div>{percentage_share}%</div>} {A_236 && !isInPot && <div>~\\${(amount * A_236).toFixed(2)}</div>} </TrRow>; })} </div> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: donations, currentPage, perPage: perPage, bgColor: \"#292929\" }, ...props } }} /> </A_27> : <NoResult>No Donations</NoResult>; `, DonorsTrx: ` const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_28 = styled.div\\` display: flex; flex-direction: column; align-items: center; gap: 2rem; width: 100%; .transcation { display: flex; flex-direction: column; width: 100%; font-size: 14px; .header { display: flex; align-items: center; justify-content: space-between; padding: 10px; gap: 1rem; background: #f6f5f3; color: #292929; div { width: 100px; display: flex; justify-content: center; align-items: center; font-weight: 600; } } .address { width: 143px !important; } } @media only screen and (max-width: 768px) { .transcation { font-size: 12px; .header { padding: 10px 0; div { width: 80px !important; } } .address { width: 80px !important; justify-content: center; .profile-image { display: none !important; } } } } @media only screen and (max-width: 480px) { .transcation { font-size: 9px; } }\\`;const A_29 = styled.div\\` display: flex; width: 100%; justify-content: space-between; gap: 1rem; padding: 20px 10px; > div, > span { width: 100px; display: flex; justify-content: center; align-items: center; } .price { display: flex; gap: 1rem; align-items: center; img { width: 1.5rem; } } .address { color: #292929; display: flex; align-items: center; justify-content: flex-start; border-radius: 2px; transition: all 200ms; .profile-image { width: 2rem; height: 2rem; margin-right: 1rem; } } @media only screen and (max-width: 768px) { padding: 10px 0; > div, > span { width: 80px; } .price { gap: 8px; img { width: 1.25rem; } } .address .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0.5rem; } } @media only screen and (max-width: 480px) { .price img { width: 1rem; } }\\`;const A_30 = styled.div\\` font-size: 2rem; text-align: center;\\`; const props = props; const { allDonations: donations, filter } = props; const allDonations = [...donations].reverse(); const [currentPage, setCurrentPage] = useState(1); const perPage = 30; const nearLogo = \"https://ipfs.near.social/ipfs/bafkreicdcpxua47eddhzjplmrs23mdjt63czowfsa2jnw4krkt532pa2ha\"; useEffect(() => { setCurrentPage(1); }, [filter]); const NEAR_DECEMIALS = 24; const calcNetDonationAmount = (amount, decimals) => Big(amount).div(Big(\\`1e\\${decimals}\\`)); return allDonations.length ? <A_28> <div className=\"transcation\"> <div className=\"header\"> <div className=\"address\">Donor</div> <div className=\"address\">Project</div> <div>Amount</div> <div>Date</div> </div> {allDonations.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation) => { const { donor_id, recipient_id, donated_at_ms, donated_at, project_id, ft_id, total_amount } = donation; const projectId = recipient_id || project_id; const isNear = ft_id === \"near\"; const frMetaDate = !isNear ? Near.view(ft_id, \"ft_metadata\", {}) : null; const assetIcon = isNear ? nearLogo : frMetaDate.icon; const decimals = isNear ? NEAR_DECEMIALS : frMetaDate.decimals; return <A_29> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`)} className=\"address\" target=\"_blank\"> <ProfileImage style={{}} accountId={donor_id} /> {_address(donor_id)} </a> <a href={hrefWithParams(\\`?tab=project&projectId=\\${projectId}\\`)} className=\"address\" target=\"_blank\"> <ProfileImage style={{}} accountId={projectId} /> {_address(projectId)} </a> <div className=\"price\"> <img src={assetIcon} alt={ft_id} /> {decimals ? calcNetDonationAmount(total_amount, decimals).toFixed(2) : \"-\"} </div> <div>{getTimePassed(donated_at_ms || donated_at)} ago</div> </A_29>; })} </div> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: allDonations, currentPage, perPage: perPage, bgColor: \"#292929\" }, ...props } }} /> </A_28> : <A_30>No Donations</A_30>; `, Leaderboard: ` const A_26 = styled.div\\` display: flex; flex-direction: column; .leaderboard { width: 100%; h1 { font-size: 2.5rem; font-weight: 600; margin-top: 20px; } .cards { display: flex; gap: 3rem; margin-top: 2rem; margin-bottom: 5rem; > div { width: 30%; display: flex; } .top { width: 40%; scale: 1.05; } @media only screen and (max-width: 670px) { flex-direction: column; justify-content: center; > div { width: 100%; display: flex; } .top { order: -1; scale: 1; width: 100%; } } } } .filter-menu { left: 0; right: auto; }\\`;const Tabs = styled.div\\` display: flex; justify-content: space-between; flex-wrap: wrap; align-items: center; gap: 2rem; font-size: 14px; margin-bottom: 24px; .menu-item { font-weight: 600; display: flex; width: 100%; justify-content: space-between; gap: 20px; } .selected { gap: 10px; .label { text-transform: uppercase; color: #7b7b7b; } .count { color: #dd3345; } } .select { width: fit-content; }\\`;const LoadingWrapper = styled.div\\` font-size: 1.5rem; margin-top: 1rem;\\`;const Filter = styled.div\\` display: flex; flex-wrap: wrap; gap: 8px; .option { padding: 0.8em 1em; border-radius: 8px; color: #292929; box-shadow: 0px -1px 0px 0px #dbdbdb inset, 0px 0px 0px 0.5px #dbdbdb; transition: all 300ms ease-in-out; cursor: pointer; &.active, :hover { background: #292929; color: white; } } @media only screen and (max-width: 480px) { font-size: 10px; }\\`; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsdWithFallback = (amountNear, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas((amountNear * A_236).toFixed(2)) : formatWithCommas(amountNear.toString()) + (abbreviate ? \"N\" : \" NEAR\");}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_31 = styled.div\\` display: flex; flex-direction: column; align-items: center; box-shadow: 0px 2px 4px #00000081; width: 100%; position: relative; padding-bottom: 1rem; font-size: 14px; .name { font-weight: bold; color: var(--primary-color); } .description { color: #b3b3b3; } .tag { position: absolute; right: 4px; top: 4px; background: white; border-radius: 2px; width: 2rem; height: 2rem; display: flex; align-items: center; justify-content: center; img { width: 18px; height: auto; } } .background { height: 100px; width: 100%; } .profile { position: relative; transform: translateY(-50%); width: 4rem; height: 4rem; border-radius: 50%; } .amount { margin-top: 1rem; border: 1px solid #b3b3b3; padding: 4px; border-radius: 4px; }\\`; const DonorsCards = (__props__) => { const { sponsors, sortedDonations, currentTab } = __props__; const donations = currentTab === \"sponsors\" ? sponsors : sortedDonations; const Card = ({ donor }) => { const { id, rank, className, amount } = donor; const profile = Social.getr(\\`\\${id}/profile\\`); return <div className={className || \"\"}> <A_31> {profile === null ? <div className=\"spinner-border text-secondary\" role=\"status\" /> : <> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: profile.backgroundImage, className: \"background\", alt: profile.name, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreidla73cknxbeovrhgb2blax2j2qgcgcn6ibluzza3buq2mbkoqs2e\" }, ...props } }} /> <div className=\"tag\">{rank}</div> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: profile.image, className: \"profile\", alt: profile.name, style: {}, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\" }, ...props } }} /> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${id}\\`)} className=\"name\" target=\"_blank\"> {_address(profile.name ? profile.name : id)} </a> <div className=\"description\">{profile.description ? _address(profile.description, 20) : \"-\"}</div> <div className=\"amount\">{nearToUsdWithFallback(amount)} Donated</div> </>} </A_31> </div>; }; const leaderboard = [{ rank: \"#2\", id: donations[1].donor_id, amount: donations[1].amount }, { rank: <img src=\"https://ipfs.near.social/ipfs/bafkreicjk6oy6465ps32owoomppfkvimbjlnhbaldvf6ujuyhkjas6ghjq\" alt=\"top\" />, id: donations[0].donor_id, className: \"top\", amount: donations[0].amount }, { rank: \"#3\", id: donations[2].donor_id, amount: donations[2].amount }]; return <div className=\"cards\">{leaderboard.map((donor) => donor.id ? <Card donor={donor} /> : \"\")}</div>; }; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const filterByDate = (filter, donation) => { const currentTimestamp = new Date().getTime(); const oneDayTime = 24 * 60 * 60 * 1000; const donateAt = donation.donated_at_ms || donation.donated_at; const yesterday = currentTimestamp - oneDayTime; const lastWeek = currentTimestamp - oneDayTime * 7; const lastMonth = currentTimestamp - oneDayTime * 30; const lastYear = currentTimestamp - oneDayTime * 365; switch (filter) { case \"day\": if (donateAt > yesterday) return true; return false; case \"week\": if (donateAt > lastWeek) return true; return false; case \"month\": if (donateAt > lastMonth) return true; return false; case \"year\": if (donateAt > lastYear) return true; return false; case \"all\": return true; default: return true; }}; const A_103 = (donation) => { const lastDonationAmount = Big(donation.total_amount - (donation.referrer_fee || 0) - (donation.protocol_fee || 0)).div(Big(1e24)); return parseFloat(lastDonationAmount);}; const Loading = () => <LoadingWrapper>Loading...</LoadingWrapper>; const [currentTab, setTab] = useState(\"leaderboard\"); const [title, setTitle] = useState(\"\"); const [filter, setFilter] = useState(\"\"); const [allDonationsFetched, setAllDonationsFetched] = useState(false); const [fetchDonationsError, setFetchDonationsError] = useState(\"\"); const direct_donations_count = useMemo(() => { const data = DonateSDK.getConfig(); return data.total_donations_count; }, []); const getSponsorshipDonations = (potId) => PotSDK.asyncGetMatchingPoolDonations(potId); const allSponsors = useCache(() => { return PotFactorySDK.asyncGetPots().then((pots) => { if (pots) { const sponsors = pots.map(({ id }) => getSponsorshipDonations(id)); return Promise.all(sponsors).then((allSponsors) => { const sumUpSponsors = allSponsors.flat().reduce((accumulator, currentDonation) => { accumulator[currentDonation.donor_id] = { amount: (accumulator[currentDonation.donor_id].amount || 0) + A_103(currentDonation), ...currentDonation }; return accumulator; }, {}); return Object.values(sumUpSponsors); }); } else return []; }).catch((err) => { console.log(\"error fetching pots \", err); return []; }); }, \"sponsors-funding\"); const sponsors = useMemo(() => { if (allSponsors) { let sponsors = allSponsors.filter((donation) => filterByDate(filter, donation)); sponsors = allSponsors.sort((a, b) => b.amount - a.amount); return sponsors; } }, [allSponsors, filter]); const allDonationsPaginated = useCache(() => { const limit = 900; const paginations = [...Array(Math.ceil(direct_donations_count / limit)).keys()]; try { const allDonations = paginations.map((index) => DonateSDK.asyncGetDonations(limit * index, limit)); return Promise.all(allDonations); } catch (error) { console.error(\\`error getting direct donations\\`, error); setFetchDonationsError(error); return Promise.all([]); } }, \"direct-donations\"); const [allDonations, sortedDonations] = useMemo(() => { if (allDonationsPaginated) { let donations = allDonationsPaginated.flat(); donations = donations.filter((donation) => filterByDate(filter, donation)); const totalsByDonor = donations.reduce((accumulator, currentDonation) => { accumulator[currentDonation.donor_id] = { amount: (accumulator[currentDonation.donor_id].amount || 0) + (currentDonation.ft_id === \"near\" ? A_103(currentDonation) : 0), ...currentDonation }; return accumulator; }, {}); const sortedDonations = Object.values(totalsByDonor).sort((a, b) => b.amount - a.amount); setAllDonationsFetched(true); return [donations, sortedDonations]; } else { return [[], []]; } }, [allDonationsPaginated, filter]); const filterOptions = [{ text: \"All Time\", value: \"all\" }, { text: \"1Y\", value: \"year\" }, { text: \"1M\", value: \"month\" }, { text: \"1W\", value: \"week\" }, { text: \"24H\", value: \"day\" }]; const MenuItem = ({ count, children, className }) => <div className={\\`menu-item \\${className || \"\"}\\`}> <div className=\"label\">{children}</div> <div className=\"count\">{count}</div> </div>; const tabs = [{ label: \"Donor Leaderboard\", val: \"leaderboard\", count: sortedDonations.length }, { label: \"Sponsors Leaderboard\", val: \"sponsors\", count: sponsors.length }, { label: \"Donor Feed\", val: \"feed\", count: allDonations.length }]; const options = [{ tab: \"feed\", src: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.DonorsTrx} props={{ ...{ ...compProps, ...props } }} /> }, { tab: \"sponsors\", src: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.DonorsLeaderboard} props={{ ...{ ...compProps, ...props } }} /> }]; const SelectedNavComponent = options.find((option) => option.tab == currentTab).src; const sortList = tabs.map((tab) => ({ label: <MenuItem key={tab.val} count={tab.count}> {tab.label} </MenuItem>, val: tab })); return <A_26> {fetchDonationsError ? <div> <h1>Error fetching donations</h1> <p>{fetchDonationsError}</p> </div> : !allDonationsFetched ? <Loading /> : <> <div className=\"leaderboard\"> <h1>Donors Leaderboard</h1> <DonorsCards {...{ sponsors, sortedDonations, currentTab }} /> </div> <Tabs> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ ...{ sortVal: title, title: <MenuItem className=\"selected\" count={tabs[0].count}> {tabs[0].val}{\" \"} </MenuItem>, sortList: sortList, FilterMenuCustomClass: \\`filter-menu\\`, handleSortChange: ({ val: option }) => {setTitle(<MenuItem className=\"selected\" count={option.count}> {option.val} </MenuItem>);setTab(option.val);} }, ...props } }} /> <Filter> {filterOptions.map((option) => <div className={\\`option \\${filter === option.value ? \"active\" : \"\"}\\`} key={option.value} onClick={() => setFilter(option.value)}> {option.text} </div>)} </Filter> </Tabs> {currentTab === \"leaderboard\" ? <Widget loading=\" \" code={props.alem.componentsCode.DonorsLeaderboard} props={{ ...{ ...{ allDonations: allDonations, filter, sponsors, sortedDonations, currentTab }, ...props } }} /> : <SelectedNavComponent {...{ allDonations: allDonations, filter, sponsors, sortedDonations, currentTab }} />} </>} </A_26>; `, ChallengeModal: ` const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_36 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; background: white; padding: 24px 24px 12px 24px; border-top-left-radius: 6px; border-top-right-radius: 6px; font-weight: 500;\\`;const A_37 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 24px; border-top: 1px #f0f0f0 solid; background: #fafafa; gap: 8px;\\`;const ModalFooter = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-end; background: #fafafa; padding: 12px 24px 24px 24px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; gap: 24px; width: 100%;\\`; const {onClose: onClose, existingChallengeForUser: existingChallengeForUser} = props; const { potId } = props.alem.useParams(); State.init({ challengeReason: \"\", challengeReasonError: \"\" }); useEffect(() => { if (existingChallengeForUser?.reason) { State.update({ challengeReason: existingChallengeForUser?.reason }); } }, [existingChallengeForUser]); const { challengeReason, challengeReasonError } = state; const handleCancelChallenge = () => { onClose(); State.update({ challengeReason: \"\", challengeReasonError: \"\" }); }; const handleSubmitChallenge = () => { PotSDK.challengePayouts(potId, challengeReason); onClose(); }; const MAX_CHALLENGE_TEXT_LENGTH = 1000; return <ModalOverlay onOverlayClick={onClose}> <A_36>Challenge Payouts</A_36> <A_37> <div>Explain the reason for your challenge</div> <TextArea {...{ noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Type the reason for your challenge here\", value: challengeReason, onChange: challengeReason => State.update({ challengeReason }), validate: () => { if (challengeReason.length > MAX_CHALLENGE_TEXT_LENGTH) { State.update({ challengeReasonError: \\`Challenge reason must be less than \\${MAX_CHALLENGE_TEXT_LENGTH} characters\\` }); return; } State.update({ challengeReasonError: \"\" }); }, error: challengeReasonError }} /> </A_37> <ModalFooter> <Button {...{ type: \"tertiary\", text: \"Cancel\", onClick: handleCancelChallenge }} /> <Button {...{ type: \"primary\", text: \"Submit Challenge\", disabled: !challengeReason || !!challengeReasonError, onClick: handleSubmitChallenge }} /> </ModalFooter> </ModalOverlay>; `, FundModal: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; function doesUserHaveDaoFunctionCallProposalPermissions(accountId, policy) { const userRoles = policy.roles.filter(role => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); return allowed;} const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const ModalTitle = styled.div\\` color: #525252; font-size: 16px; font-weight: 400; line-height: 20px; word-wrap: break-word; margin: 8px 0px;\\`;const A_38 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const UserChipLink = styled.a\\` display: flex; flex-direction: row; margin: 0px 4px; margin-left: auto; padding: 2px 12px; gap: 4px; border-radius: 32px; background: #ebebeb; &:hover { text-decoration: none; }\\`;const TextBold = styled.div\\` color: #292929; font-size: 12px; font-weight: 600; line-height: 20px; word-wrap: break-word; text-align: center;\\`;const FeeText = styled.div\\` color: #292929; font-size: 14px; font-weight: 400; line-height: 20px; word-wrap: break-word; display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const A_39 = styled.label\\` width: 100%; font-size: 12px; line-height: 16px; word-wrap: break-word; color: #2e2e2e; display: flex; flex-direction: row; align-items: center; justify-content: center;\\`; const {potDetail: potDetail, onClose: onClose} = props; const { referrerId, potId } = props.alem.useParams(); const { MAX_DONATION_MESSAGE_LENGTH, SUPPORTED_FTS, ONE_TGAS } = constants; const { protocol_config_provider, chef_fee_basis_points, chef, base_currency, min_matching_pool_donation_amount, referral_fee_matching_pool_basis_points } = potDetail; State.init({ matchingPoolDonationAmountNear: \"\", matchingPoolDonationAmountNearError: \"\", matchingPoolDonationMessage: \"\", matchingPoolDonationMessageError: \"\", bypassProtocolFee: false, bypassChefFee: false, fundAsDao: false, daoAddress: \"\", daoAddressError: \"\", daoPolicy: {} }); const { matchingPoolDonationAmountNear, matchingPoolDonationAmountNearError, matchingPoolDonationMessage, matchingPoolDonationMessageError, bypassProtocolFee, bypassChefFee, fundAsDao, daoAddress, daoAddressError } = state; Big.PE = 100; const FIFTY_TGAS = \"50000000000000\"; const THREE_HUNDRED_TGAS = \"300000000000000\"; const MIN_DAO_PROPOSAL_DEPOSIT_FALLBACK = \"100000000000000000000000\"; const protocolConfigContractId = protocol_config_provider.split(\":\")[0]; const protocolConfigViewMethodName = protocol_config_provider.split(\":\")[1]; const protocolConfig = Near.view(protocolConfigContractId, protocolConfigViewMethodName, {}); const protocolFeeRecipientProfile = Social.getr(\\`\\${protocolConfig?.account_id}/profile\\`); const chefProfile = Social.getr(\\`\\${chef}/profile\\`); const chefFeeAmountNear = bypassChefFee ? 0 : matchingPoolDonationAmountNear * potDetail?.chef_fee_basis_points / 10_000 || 0; const protocolFeeAmountNear = bypassProtocolFee ? 0 : matchingPoolDonationAmountNear * protocolConfig?.basis_points / 10_000 || 0; const referrerFeeAmountNear = referrerId ? matchingPoolDonationAmountNear * referral_fee_matching_pool_basis_points / 10_000 || 0 : 0; const handleMatchingPoolDonation = () => { const args = { message: matchingPoolDonationMessage, matching_pool: true, referrer_id: referrerId || null, bypass_protocol_fee: bypassProtocolFee }; if (state.bypassChefFee) { args.custom_chef_fee_basis_points = 0; } const amountFloat = parseFloat(matchingPoolDonationAmountNear || 0); if (!amountFloat) { State.update({ matchingPoolDonationAmountNearError: \"Invalid amount\" }); return; } const amountIndivisible = SUPPORTED_FTS[base_currency.toUpperCase()].toIndivisible(amountFloat); let transactions = [{ contractName: potId, methodName: \"donate\", deposit: amountIndivisible, args, gas: ONE_TGAS.mul(100) }]; if (state.fundAsDao) { const clonedTransactions = JSON.parse(JSON.stringify(transactions)); transactions = clonedTransactions.map(tx => { const action = { method_name: tx.methodName, gas: FIFTY_TGAS, deposit: tx.deposit ? tx.deposit.toString() : \"0\", args: Buffer.from(JSON.stringify(tx.args), \"utf-8\").toString(\"base64\") }; return { ...tx, contractName: state.daoAddress, methodName: \"add_proposal\", args: { proposal: { description: \\`Contribute to matching pool for \\${potDetail.pot_name} pot (\\${potId}) on Potlock\\`, kind: { FunctionCall: { receiver_id: tx.contractName, actions: [action] } } } }, deposit: state.daoPolicy.proposal_bond || MIN_DAO_PROPOSAL_DEPOSIT_FALLBACK, gas: THREE_HUNDRED_TGAS }; }); } Near.call(transactions); }; const disabled = fundAsDao && !daoAddress || daoAddressError || !matchingPoolDonationAmountNear || !!matchingPoolDonationAmountNearError || !parseFloat(matchingPoolDonationAmountNear); return <ModalOverlay onOverlayClick={onClose}> <CheckBox label=\"Fund as DAO\" id=\"fundAsDaoSelector\" checked={fundAsDao} onClick={e => { State.update({ fundAsDao: e.target.checked }); }} /> {fundAsDao && <A_62 inputStyles={{ background: \"#FAFAFA\" }} placeholder=\"Enter DAO address\" value={daoAddress} onChange={daoAddress => State.update({ daoAddress: daoAddress.trim().toLowerCase() })} validate={() => { Near.asyncView(daoAddress, \"get_policy\", {}).then(policy => { if (!policy) { State.update({ daoAddressError: \"Invalid DAO address\" }); } if (!doesUserHaveDaoFunctionCallProposalPermissions(context.accountId || \"\", policy)) { State.update({ daoAddressError: \"Your account does not have permission to create proposals\" }); } else { State.update({ daoAddressError: \"\", daoPolicy: policy }); } }).catch(e => { State.update({ daoAddressError: \"Invalid DAO address\" }); }); }} error={daoAddressError} />} <ModalTitle> Enter matching pool contribution amount in NEAR {[\"0\", \"1\"].includes(min_matching_pool_donation_amount) ? \"(no minimum)\" : \\`(Min. \\${yoctosToNear(min_matching_pool_donation_amount)})\\`} </ModalTitle> <A_62 inputStyles={{ background: \"#FAFAFA\" }} placeholder=\"Enter amount here in NEAR\" value={matchingPoolDonationAmountNear} onChange={matchingPoolDonationAmountNear => State.update({ matchingPoolDonationAmountNear })} validate={() => { State.update({ matchingPoolDonationAmountNearError: \"\" }); }} error={matchingPoolDonationAmountNearError} /> <TextArea noLabel={true} inputRows={5} inputStyle={{ marginTop: \"0.45rem\", background: \"#FAFAFA\" }} placeholder=\"Enter an optional message\" value={matchingPoolDonationMessage} onChange={matchingPoolDonationMessage => State.update({ matchingPoolDonationMessage })} validate={() => { if (matchingPoolDonationMessage.length > MAX_DONATION_MESSAGE_LENGTH) { State.update({ matchingPoolDonationMessageError: \\`Message must be less than \\${MAX_DONATION_MESSAGE_LENGTH} characters\\` }); return; } State.update({ matchingPoolDonationMessageError: \"\" }); }} error={matchingPoolDonationMessageError} /> <A_38> <CheckBox id=\"bypassProtocolFeeSelector\" checked={bypassProtocolFee} onClick={e => { State.update({ bypassProtocolFee: e.target.checked }); }} /> <A_39 htmlFor=\"bypassProtocolFeeSelector\"> Bypass {protocolConfig?.basis_points / 100 || \"-\"}% protocol fee to{\" \"} <UserChipLink href={hrefWithParams(\\`?tab=profile&accountId=\\${protocolConfig?.account_id}\\`)} target=\"_blank\"> <ProfileImage accountId={protocolConfig?.account_id} style={{ height: \"12px\", width: \"12px\" }} /> <TextBold>{_address(protocolFeeRecipientProfile?.name || protocolConfig?.account_id)}</TextBold> </UserChipLink> </A_39> </A_38> {chef && chef_fee_basis_points > 0 && <A_38 style={{ marginTop: \"6px\" }}> <CheckBox id=\"bypassChefFeeSelector\" checked={bypassChefFee} onClick={e => { State.update({ bypassChefFee: e.target.checked }); }} /> <A_39 htmlFor=\"bypassChefFeeSelector\"> Bypass {chef_fee_basis_points / 100 || \"-\"}% chef fee to <UserChipLink href={hrefWithParams(\\`?tab=profile&accountId=\\${chef}\\`)} target=\"_blank\"> <ProfileImage accountId={chef} style={{ height: \"12px\", width: \"12px\" }} /> <TextBold>{chefProfile?.name || chef}</TextBold> </UserChipLink> </A_39> </A_38>} <A_38 style={{ marginTop: \"12px\" }}> <FeeText>Protocol fee: {protocolFeeAmountNear} NEAR</FeeText> </A_38> {chef && chef_fee_basis_points > 0 && <A_38 style={{ marginTop: \"12px\" }}> <FeeText>Chef fee: {chefFeeAmountNear} NEAR</FeeText> </A_38>} <A_38 style={{ marginTop: \"6px\" }}> {referrerId && <FeeText> Referrer fee (to {referrerId}): {referrerFeeAmountNear} NEAR </FeeText>} </A_38> <A_38 style={{ marginTop: \"6px\" }}> <FeeText> Net donation amount:{\" \"} {(matchingPoolDonationAmountNear - protocolFeeAmountNear - chefFeeAmountNear - referrerFeeAmountNear).toFixed(2)}{\" \"} NEAR </FeeText> </A_38> <A_38 style={{ justifyContent: \"flex-end\", marginTop: \"12px\" }}> <Button type=\"primary\" disabled={disabled} text={\\`\\${fundAsDao ? \"Create proposal to contribute \" : \"Contribute\"}\\${matchingPoolDonationAmountNear ? \\` \\${matchingPoolDonationAmountNear} \\${base_currency.toUpperCase()}\\` : \"\"} to matching pool\\`} onClick={disabled ? () => {} : handleMatchingPoolDonation} /> </A_38> </ModalOverlay>; `, NewApplicationModal: ` function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const handleSendApplication = (potId, potDetail, setApplicationSuccess, isDao) => { const FIFTY_TGAS = \"50000000000000\"; const MIN_PROPOSAL_DEPOSIT_FALLBACK = \"100000000000000000000000\"; const THREE_HUNDRED_TGAS = \"300000000000000\"; const { ONE_TGAS, SUPPORTED_FTS: { NEAR } } = constants; const args = { message: state.applicationMessage }; let deposit = NEAR.toIndivisible(\"0.01\"); const extraDeposit = Big(state.applicationMessage.length * 0.0001).mul(Big(10).pow(24)); deposit = deposit.plus(extraDeposit); let transactions = [{ contractName: potId, methodName: \"apply\", deposit, args, gas: ONE_TGAS.mul(100) }]; if (isDao) { const clonedTransactions = JSON.parse(JSON.stringify(transactions)); transactions = clonedTransactions.map(tx => { const action = { method_name: tx.methodName, gas: FIFTY_TGAS, deposit: tx.deposit ? tx.deposit.toString() : \"0\", args: Buffer.from(JSON.stringify(tx.args), \"utf-8\").toString(\"base64\") }; return { ...tx, contractName: state.daoAddress, methodName: \"add_proposal\", args: { proposal: { description: \\`Application to PotLock pot: \\${potDetail.pot_name} (\\${potId})\\`, kind: { FunctionCall: { receiver_id: tx.contractName, actions: [action] } } } }, deposit: state.daoPolicy.proposal_bond || MIN_PROPOSAL_DEPOSIT_FALLBACK, gas: THREE_HUNDRED_TGAS }; }); } Near.call(transactions); const pollIntervalMs = 1000; const pollId = setInterval(() => { PotSDK.asyncGetApplications(potId).then(applications => { const application = applications.find(application => application.project_id === (isDao ? state.daoAddress : context.accountId)); if (application) { clearInterval(pollId); setApplicationSuccess(true); } }); }, pollIntervalMs);}; function doesUserHaveDaoFunctionCallProposalPermissions(accountId, policy) { const userRoles = policy.roles.filter(role => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); return allowed;} const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_40 = styled.div\\` color: #525252; font-size: 16px; font-weight: 400; line-height: 20px; word-wrap: break-word; margin-bottom: 8px;\\`;const A_41 = styled.div\\` display: flex; flex-direction: row; align-items: center;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const {potDetail: potDetail, onClose: onClose, setIsDao: setIsDao, isDao: isDao, setApplicationSuccess: setApplicationSuccess, setRegistryStatus: setRegistryStatus, registryStatus: registryStatus} = props; const MAX_APPLICATION_MESSAGE_LENGTH = 1000; State.init({ applicationMessage: \"\", applicationMessageError: \"\", daoAddress: \"\", daoPolicy: \"\", daoAddressError: \"\" }); const updateState = State.update; const { applicationMessage, applicationMessageError, daoAddress, daoAddressError } = state; const accountId = context.accountId || \"\"; const { potId } = props.alem.useParams(); const verifyIsOnRegistry = address => { Near.asyncView(\"lists.potlock.near\", \"get_registrations_for_registrant\", { registrant_id: address }).then(registrations => { const registration = registrations.find(registration => registration.list_id === 1); if (registration) { setRegistryStatus(registration.status); } }); }; useEffect(() => { if (!state.isDao) { verifyIsOnRegistry(context.accountId || \"\"); } }, []); const textAreaProps = { noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Your application message here...\", value: applicationMessage, onChange: applicationMessage => updateState({ applicationMessage }), validate: () => { if (applicationMessage.length > MAX_APPLICATION_MESSAGE_LENGTH) { updateState({ applicationMessageError: \\`Application message must be less than \\${MAX_APPLICATION_MESSAGE_LENGTH} characters\\` }); return; } updateState({ applicationMessageError: \"\" }); }, error: applicationMessageError }; const checkBoxProps = { id: \"isDaoSelector\", checked: isDao, onClick: e => { setIsDao(e.target.checked); if (!e.target.checked) { verifyIsOnRegistry(accountId || \"\"); } }, label: \"I'm applying as a DAO\" }; const textProps = { label: \"DAO address *\", placeholder: \"E.g. mydao.sputnikdao.near\", value: daoAddress, onChange: daoAddress => updateState({ daoAddress, daoAddressError: \"\" }), validate: () => { Near.asyncView(daoAddress, \"get_policy\", {}).then(policy => { const hasPermissions = !policy ? false : doesUserHaveDaoFunctionCallProposalPermissions(accountId, policy); updateState({ daoAddressError: hasPermissions ? \"\" : \"You don't have required permissions to submit proposals to this DAO.\", daoPolicy: policy }); verifyIsOnRegistry(daoAddress); }).catch(e => { updateState({ daoAddressError: \"Invalid DAO address\" }); }); }, error: daoAddressError }; const isError = applicationMessageError || daoAddressError; const registrationApproved = registryStatus === \"Approved\"; const registrationApprovedOrNoRegistryProvider = registrationApproved || !potDetail?.registry_provider; return <ModalOverlay onOverlayClick={onClose}> <A_40> Application message <span style={{ color: \"#DD3345\" }}>*</span> </A_40> <TextArea {...textAreaProps} /> <A_41 style={{ margin: \"12px 0px\" }}> <CheckBox {...checkBoxProps} /> </A_41> {isDao && <A_62 {...textProps} />} <A_41 style={{ justifyContent: \"flex-end\", marginTop: \"12px\" }}> <Button type=\"primary\" text={isDao ? \"Propose to Send Application\" : registrationApprovedOrNoRegistryProvider ? \"Send application\" : \"Register to apply\"} onClick={(isDao || registrationApprovedOrNoRegistryProvider) && !isError ? () => { handleSendApplication(potId, potDetail, setApplicationSuccess, isDao); } : () => {}} disabled={isError} href={isDao || registrationApprovedOrNoRegistryProvider ? \"\" : hrefWithParams(\\`?tab=createproject\\`)} target={isDao || registrationApprovedOrNoRegistryProvider ? \"_self\" : \"_blank\"} /> </A_41> </ModalOverlay>; `, A_44: ` const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_42 = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 12px; border-top: 1px solid #292929; border-right: 1px solid #292929; border-bottom: 2px solid #292929; border-left: 1px solid #292929; overflow: hidden; .header { font-size: 18px; font-weight: 600; background: #fef6ee; padding: 1rem; span { color: #ee8949; } } .sort { display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 1rem; font-size: 11px; background: #fef6ee; .title { font-weight: 500; letter-spacing: 0.44px; text-transform: uppercase; } .sort-btn { font-weight: 500; display: flex; align-items: center; gap: 0.5rem; cursor: pointer; } }\\`;const A_43 = styled.div\\` display: flex; align-items: center; font-size: 14px; padding: 1rem; gap: 8px; border-bottom: 1px solid #c7c7c7; &:last-of-type { border-bottom: none; } .address { display: flex; text-decoration: none; align-items: center; font-weight: 600; gap: 8px; margin-left: 24px; flex: 1; color: #292929; transition: color 200ms ease-in; :hover { color: #dd3345; } } .profile-image { width: 18px; height: 18px; border-radius: 50%; display: flex !important; }\\`; const { donations: donations, totalAmount: totalAmount, totalUniqueDonors: totalUniqueDonors, title: title, allPayouts: allPayouts, potDetail: potDetail } = props; const [usdToggle, setUsdToggle] = useState(false); const { SUPPORTED_FTS } = constants; return <A_42> <div className=\"header\"> {totalAmount} <span>raised from</span> {totalUniqueDonors} <span>{title === \"sponsors\" ? \"sponsors\" : \"donors\"}</span> </div> <div className=\"sort\"> <div className=\"title\">Top {title} </div> <div className=\"sort-btn\" style={{ cursor: A_236 ? \"pointer\" : \"default\" }} onClick={() => A_236 ? setUsdToggle(!usdToggle) : \"\"}> {A_236 && <svg width=\"12\" height=\"14\" viewBox=\"0 0 12 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9 10.7575V5.5H7.5V10.7575H5.25L8.25 13.75L11.25 10.7575H9ZM3.75 0.25L0.75 3.2425H3V8.5H4.5V3.2425H6.75L3.75 0.25ZM9 10.7575V5.5H7.5V10.7575H5.25L8.25 13.75L11.25 10.7575H9ZM3.75 0.25L0.75 3.2425H3V8.5H4.5V3.2425H6.75L3.75 0.25Z\" fill=\"#7B7B7B\" /> </svg>} {usdToggle ? \"USD\" : \"NEAR\"} </div> </div> {donations.map(({ projectId, donor_id, matchingAmount, net_amount }, idx) => { const id = donor_id || projectId; const nearAmount = formatWithCommas(SUPPORTED_FTS[potDetail.base_currency.toUpperCase()].fromIndivisible(net_amount || matchingAmount)); const profile = Social.getr(\\`\\${id}/profile\\`); const matchedAmout = usdToggle ? yoctosToUsdWithFallback(matchingAmount || net_amount, true) : nearAmount; const url = projectId ? \\`?tab=project&projectId=\\${projectId}\\` : \\`?tab=profile&accountId=\\${donor_id}\\`; return <A_43> <div>#{idx + 1}</div> <a className=\"address\" href={hrefWithParams(url)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ image: profile?.image, className: \"profile-image\", fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\", style: {}, ...props } }} /> {_address(profile?.name || id, 15)} </a> <div> {matchedAmout} {usdToggle ? \" \" : \"N\"} </div> </A_43>; })} </A_42>; `, PoolAllocationTable: ` const calculatePayouts = (allPotDonations, totalMatchingPool, blacklistedAccounts) => { const { NADABOT_CONTRACT_ID } = constants; return new Promise((resolve, reject) => { console.log(\"Calculting payouts; ignoring blacklisted donors &/or projects: \", blacklistedAccounts.join(\", \")); console.log(\"totalMatchingPool: \", totalMatchingPool); const projectContributions = []; const allDonors = new Set(); for (const d of allPotDonations) { if (blacklistedAccounts.includes(d.donor_id) || blacklistedAccounts.includes(d.project_id)) { continue; } const amount = new Big(d.total_amount); const val = [d.project_id, d.donor_id, amount]; projectContributions.push(val); allDonors.add(d.donor_id); } const limit = 100; let curIndex = 0; let humanScores = {}; let promises = []; while (curIndex < allDonors.size) { promises.push(Near.asyncView(NADABOT_CONTRACT_ID, \"get_human_score_batch\", { account_ids: Array.from(allDonors).slice(curIndex, curIndex + limit) })); curIndex += limit; } Promise.all(promises).then(res => { for (const r of res) { humanScores = { ...humanScores, ...r }; } }).catch(e => { console.error(\"error fetching human scores. Continuing anyway: \", e); }).finally(() => { console.log(\"human scores: \", humanScores); const contributions = {}; for (const [proj, user, amount] of projectContributions) { if (!humanScores[user] || !humanScores[user].is_human) { console.log(\"skipping non-human: \", user); continue; } if (!contributions[proj]) { contributions[proj] = {}; } contributions[proj][user] = Big(contributions[proj][user] || 0).plus(amount); } console.log(\"contributions: \", contributions); const pairTotals = {}; for (const contribz of Object.values(contributions)) { for (const [k1, v1] of Object.entries(contribz)) { if (!pairTotals[k1]) { pairTotals[k1] = {}; } for (const [k2, v2] of Object.entries(contribz)) { if (!pairTotals[k1][k2]) { pairTotals[k1][k2] = Big(0); } pairTotals[k1][k2] = pairTotals[k1][k2].plus(v1.times(v2).sqrt()); } } } const threshold = Big(\"25000000000000000000000000\"); const totalPot = Big(totalMatchingPool); let bigtot = Big(0); const totals = []; for (const [proj, contribz] of Object.entries(contributions)) { let tot = Big(0); let _num = 0; let _sum = Big(0); for (const [k1, v1] of Object.entries(contribz)) { _num += 1; _sum = _sum.plus(v1); for (const [k2, v2] of Object.entries(contribz)) { if (k2 > k1 || Object.keys(contribz).length === 1) { const sqrt = v1.times(v2).sqrt(); tot = tot.plus(sqrt.div(pairTotals[k1][k2].div(threshold))); } } } bigtot = bigtot.plus(tot); totals.push({ id: proj, number_contributions: _num, contribution_amount_str: _sum.toFixed(0), matching_amount_str: tot.toFixed(0) }); } console.log(\"totals before: \", totals); if (bigtot.gte(totalPot)) { console.log(\"NORMALIZING\"); for (const t of totals) { t.matching_amount_str = Big(t.matching_amount_str).div(bigtot).times(totalPot).toFixed(0); } } let totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } let residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"first round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(\"0\"))) { for (let i = 0; i < totals.length; i++) { let proportion = Big(totals[i].matching_amount_str).div(totalAllocatedBeforeRounding); let additionalAllocation = proportion.times(residual); totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(additionalAllocation).toFixed(0); } console.log(\"CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION\"); totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"second round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(0))) { totals.sort((a, b) => Big(b.matching_amount_str).minus(Big(a.matching_amount_str))); if (residual.gt(Big(0))) { totals[0].matching_amount_str = Big(totals[0].matching_amount_str).plus(residual).toFixed(0); } else { for (let i = 0; i < totals.length; i++) { if (Big(totals[i].matching_amount_str).gt(residual.abs())) { totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(residual).toFixed(0); break; } } } totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"Residual after final adjustment: \", residual.toFixed(0)); } } const payouts = totals.reduce((acc, t) => { acc[t.id] = { totalAmount: t.contribution_amount_str, matchingAmount: t.matching_amount_str, donorCount: t.number_contributions }; return acc; }, {}); resolve(payouts); }); });}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsdWithFallback = (amountNear, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas((amountNear * A_236).toFixed(2)) : formatWithCommas(amountNear.toString()) + (abbreviate ? \"N\" : \" NEAR\");}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const { potDetail: potDetail, allDonations: allDonations } = props; const { SUPPORTED_FTS } = constants; const { base_currency, total_public_donations, matching_pool_balance, public_donations_count } = potDetail; const [projectsId, setProjectsId] = useState(null); const [allPayouts, setAllPayouts] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); const { potId } = props.alem.useParams(); if (!projectsId) { PotSDK.asyncGetApprovedApplications(potId).then((projects) => { setProjectsId(projects); }); } let sponsorshipDonations = PotSDK.getMatchingPoolDonations(potId); if (sponsorshipDonations) sponsorshipDonations.sort((a, b) => b.net_amount - a.net_amount); const calcMatchedAmount = (donations) => { if (donations) { let total = Big(0); donations?.forEach((donation) => { total = total.plus(Big(donation.net_amount)); }); const amount = SUPPORTED_FTS[base_currency.toUpperCase() || \"NEAR\"].fromIndivisible(total.toString()); return amount; } }; const uniqueDonorIds = allDonations ? new Set(allDonations.map((donation) => donation.donor_id)) : new Set([]); const donorsCount = uniqueDonorIds.size; if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => { if (data) { const listOfFlagged = []; data?.forEach((adminFlaggedAcc) => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); listOfFlagged.push(...addresses); }); setFlaggedAddresses(listOfFlagged); } }).catch((err) => console.log(\"error getting the flagged accounts \", err)); } const sortAndSetPayouts = (payouts) => { payouts.sort((a, b) => { return b.matchingAmount - a.matchingAmount; }); setAllPayouts(payouts.slice(0, 5)); }; if (!allPayouts && allDonations?.length > 0 && flaggedAddresses) { let allPayouts = []; if (potDetail.payouts.length) { allPayouts = potDetail.payouts.map((payout) => { const { project_id, amount } = payout; return { projectId: project_id, matchingAmount: amount }; }); sortAndSetPayouts(allPayouts); } else { calculatePayouts(allDonations, matching_pool_balance, flaggedAddresses).then((calculatedPayouts) => { allPayouts = Object.entries(calculatedPayouts).map(([projectId, { matchingAmount }]) => { return { projectId, matchingAmount }; }); sortAndSetPayouts(allPayouts); }); } } return allPayouts?.length > 0 ? <Widget loading=\" \" code={props.alem.componentsCode.A_44} props={{ ...{ title: \"matching pool allocations\", totalAmount: yoctosToUsdWithFallback(total_public_donations, true), totalUniqueDonors: donorsCount, donations: allPayouts, ...props } }} /> : sponsorshipDonations.length > 0 ? <Widget loading=\" \" code={props.alem.componentsCode.A_44} props={{ ...{ title: \"sponsors\", totalAmount: nearToUsdWithFallback(calcMatchedAmount(sponsorshipDonations)), totalUniqueDonors: new Set(sponsorshipDonations.map((obj) => obj.donor_id)).size, donations: sponsorshipDonations.slice(0, 5), ...props } }} /> : \"\"; `, A_48: ` const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useDonationModal = () => useContext(\"donation-modal\"); const calculatePayouts = (allPotDonations, totalMatchingPool, blacklistedAccounts) => { const { NADABOT_CONTRACT_ID } = constants; return new Promise((resolve, reject) => { console.log(\"Calculting payouts; ignoring blacklisted donors &/or projects: \", blacklistedAccounts.join(\", \")); console.log(\"totalMatchingPool: \", totalMatchingPool); const projectContributions = []; const allDonors = new Set(); for (const d of allPotDonations) { if (blacklistedAccounts.includes(d.donor_id) || blacklistedAccounts.includes(d.project_id)) { continue; } const amount = new Big(d.total_amount); const val = [d.project_id, d.donor_id, amount]; projectContributions.push(val); allDonors.add(d.donor_id); } const limit = 100; let curIndex = 0; let humanScores = {}; let promises = []; while (curIndex < allDonors.size) { promises.push(Near.asyncView(NADABOT_CONTRACT_ID, \"get_human_score_batch\", { account_ids: Array.from(allDonors).slice(curIndex, curIndex + limit) })); curIndex += limit; } Promise.all(promises).then(res => { for (const r of res) { humanScores = { ...humanScores, ...r }; } }).catch(e => { console.error(\"error fetching human scores. Continuing anyway: \", e); }).finally(() => { console.log(\"human scores: \", humanScores); const contributions = {}; for (const [proj, user, amount] of projectContributions) { if (!humanScores[user] || !humanScores[user].is_human) { console.log(\"skipping non-human: \", user); continue; } if (!contributions[proj]) { contributions[proj] = {}; } contributions[proj][user] = Big(contributions[proj][user] || 0).plus(amount); } console.log(\"contributions: \", contributions); const pairTotals = {}; for (const contribz of Object.values(contributions)) { for (const [k1, v1] of Object.entries(contribz)) { if (!pairTotals[k1]) { pairTotals[k1] = {}; } for (const [k2, v2] of Object.entries(contribz)) { if (!pairTotals[k1][k2]) { pairTotals[k1][k2] = Big(0); } pairTotals[k1][k2] = pairTotals[k1][k2].plus(v1.times(v2).sqrt()); } } } const threshold = Big(\"25000000000000000000000000\"); const totalPot = Big(totalMatchingPool); let bigtot = Big(0); const totals = []; for (const [proj, contribz] of Object.entries(contributions)) { let tot = Big(0); let _num = 0; let _sum = Big(0); for (const [k1, v1] of Object.entries(contribz)) { _num += 1; _sum = _sum.plus(v1); for (const [k2, v2] of Object.entries(contribz)) { if (k2 > k1 || Object.keys(contribz).length === 1) { const sqrt = v1.times(v2).sqrt(); tot = tot.plus(sqrt.div(pairTotals[k1][k2].div(threshold))); } } } bigtot = bigtot.plus(tot); totals.push({ id: proj, number_contributions: _num, contribution_amount_str: _sum.toFixed(0), matching_amount_str: tot.toFixed(0) }); } console.log(\"totals before: \", totals); if (bigtot.gte(totalPot)) { console.log(\"NORMALIZING\"); for (const t of totals) { t.matching_amount_str = Big(t.matching_amount_str).div(bigtot).times(totalPot).toFixed(0); } } let totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } let residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"first round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(\"0\"))) { for (let i = 0; i < totals.length; i++) { let proportion = Big(totals[i].matching_amount_str).div(totalAllocatedBeforeRounding); let additionalAllocation = proportion.times(residual); totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(additionalAllocation).toFixed(0); } console.log(\"CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION\"); totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"second round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(0))) { totals.sort((a, b) => Big(b.matching_amount_str).minus(Big(a.matching_amount_str))); if (residual.gt(Big(0))) { totals[0].matching_amount_str = Big(totals[0].matching_amount_str).plus(residual).toFixed(0); } else { for (let i = 0; i < totals.length; i++) { if (Big(totals[i].matching_amount_str).gt(residual.abs())) { totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(residual).toFixed(0); break; } } } totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"Residual after final adjustment: \", residual.toFixed(0)); } } const payouts = totals.reduce((acc, t) => { acc[t.id] = { totalAmount: t.contribution_amount_str, matchingAmount: t.matching_amount_str, donorCount: t.number_contributions }; return acc; }, {}); resolve(payouts); }); });}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_45 = styled.div\\` display: flex; flex-wrap: wrap; gap: 2rem; padding: 64px 4rem 80px; .pool-table { max-width: 514px; width: 100%; } @media only screen and (max-width: 1068px) { flex-direction: column; .pool-table { margin: auto; } } @media only screen and (max-width: 768px) { padding: 3rem 0; .pool-table { max-width: 100%; } }\\`;const HeaderWrapper = styled.div\\` display: flex; flex-direction: column; gap: 24px; flex: 1;\\`;const A_46 = styled.div\\` font-size: 40px; font-weight: 500; font-family: \"Lora\";\\`;const A_47 = styled.div\\` max-width: 498px; line-height: 1.5em; a { color: #7b7b7b; font-weight: 600; }\\`;const Fund = styled.div\\` display: flex; flex-direction: column; gap: 8px; > div { display: flex; gap: 8px; align-items: baseline; div { font-weight: 600; } } .near-price { font-size: 24px; }\\`;const ButtonsWrapper = styled.div\\` display: flex; flex-wrap: wrap; gap: 2rem; a, button { width: 180px; padding: 16px; } @media only screen and (max-width: 480px) { flex-direction: column; gap: 1rem; a, button { width: 100%; } }\\`;const Referral = styled.div\\` font-size: 14px; gap: 12px; display: flex; align-items: center;\\`; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const { potDetail: potDetail, allDonations: allDonations } = props; const { admins, chef, owner, pot_name, pot_description, matching_pool_balance, public_round_end_ms, public_round_start_ms, application_start_ms, application_end_ms, cooldown_end_ms, all_paid_out } = potDetail; const { IPFS_BASE_URL, NADA_BOT_URL } = constants; const { potId } = props.alem.useParams(); const { setDonationModalProps } = useDonationModal(); const NADABOT_ICON_URL = IPFS_BASE_URL + \"bafkreiecgkoybmplo4o542fphclxrhh4nlof5uit3lkzyv4eo2qymrpsru\"; const accountId = context.accountId || \"\"; const [isMatchingPoolModalOpen, setIsMatchingPoolModalOpen] = useState(false); const [isApplicationModalOpen, setIsApplicationModalOpen] = useState(false); const [showChallengePayoutsModal, setShowChallengePayoutsModal] = useState(false); const [projects, setProjects] = useState(null); const [registryStatus, setRegistryStatus] = useState(null); const [isDao, setIsDao] = useState(null); const [applicationSuccess, setApplicationSuccess] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); const verifyIsOnRegistry = (address) => { Near.asyncView(\"lists.potlock.near\", \"get_registrations_for_registrant\", { registrant_id: address }).then((registrations) => { const registration = registrations.find((registration) => registration.list_id === 1); if (registration) { setRegistryStatus(registration.status); } }); }; useEffect(() => { if (!isDao) { verifyIsOnRegistry(context.accountId || \"\"); } }, []); const projectNotRegistered = registryStatus === null; const userIsAdminOrGreater = admins.includes(accountId) || owner === accountId; const userIsChefOrGreater = userIsAdminOrGreater || chef === accountId; const existingApplication = PotSDK.getApplicationByProjectId(potId, context.accountId); useEffect(() => { if (!projects) { PotSDK.asyncGetApprovedApplications(potId).then((projects) => { setProjects(projects); }); } }, []); const applicationExists = existingApplication || applicationSuccess; const now = Date.now(); const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; const publicRoundEnded = now > public_round_end_ms; const applicationOpen = now >= application_start_ms && now < application_end_ms; const canApply = applicationOpen && !applicationExists && !userIsChefOrGreater; const potLink = \\`https://bos.potlock.io/?tab=pot&potId=\\${potId}\\${context.accountId && \\`&referrerId=\\${context.accountId}\\`}\\`; const canPayoutsBeProcessed = userIsAdminOrGreater && now >= cooldown_end_ms && !all_paid_out; const canPayoutsBeSet = userIsChefOrGreater && !all_paid_out && publicRoundEnded; const payoutsChallenges = PotSDK.getPayoutsChallenges(potId); if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => { const listOfFlagged = []; data.forEach((adminFlaggedAcc) => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); listOfFlagged.push(...addresses); }); setFlaggedAddresses(listOfFlagged); }).catch((err) => console.log(\"error getting the flagged accounts \", err)); } const handleSetPayouts = () => { if (allDonations && flaggedAddresses !== null) { calculatePayouts(allDonations, matching_pool_balance, flaggedAddresses).then((calculatedPayouts) => { const payouts = Object.entries(calculatedPayouts).map(([projectId, { matchingAmount }]) => ({ project_id: projectId, amount: matchingAmount })).filter((payout) => payout.amount !== \"0\"); PotSDK.chefSetPayouts(potId, payouts); }); } else { console.log(\"error fetching donations or flagged addresses\"); } }; const handleProcessPayouts = () => { PotSDK.adminProcessPayouts(potId); }; const existingChallengeForUser = (payoutsChallenges || []).find((challenge) => challenge.challenger_id === context.accountId); const canDonate = projects.length > 0 && publicRoundOpen && context.accountId; const registrationApproved = registryStatus === \"Approved\"; return <A_45> <HeaderWrapper> <A_46>{pot_name}</A_46> <A_47> <Markdown text={pot_description} /> </A_47> <Fund> <div className=\"label\">Matching Funds Available:</div> <div> <div className=\"near-price\">{yoctosToNear(matching_pool_balance, true)}</div> {A_236 && <div className=\"usd-price\"> {yoctosToUsdWithFallback(matching_pool_balance, true)}</div>} </div> </Fund> <ButtonsWrapper> {canDonate && <Button type=\"primary\" text={\"Donate\"} href={canDonate ? \"\" : NADA_BOT_URL} onClick={canDonate ? () => { setDonationModalProps({ potId, potDetail, projects, multiple: true }); } : () => {}} target={canDonate ? \"_self\" : \"_blank\"} iconSrc={canDonate ? \"\" : NADABOT_ICON_URL} />} {now < public_round_end_ms && <Button type=\"secondary\" text=\"Fund matching pool\" onClick={() => setIsMatchingPoolModalOpen(true)} />} {canApply && <Button type={registrationApproved || projectNotRegistered ? \"primary\" : \"tertiary\"} text={registryStatus && !registrationApproved ? \\`Project Registration \\${registryStatus}\\` : \"Apply to pot\"} style={{ marginRight: \"24px\" }} disabled={registryStatus && !registrationApproved} onClick={() => setIsApplicationModalOpen(true)} />} {now > public_round_end_ms && now < cooldown_end_ms && <Button type=\"secondary\" text={existingChallengeForUser ? \"Update challenge\" : \"Challenge payouts\"} onClick={() => setShowChallengePayoutsModal(true)} />} {canPayoutsBeSet && <Button {...{ text: \"Set Payouts\", onClick: handleSetPayouts }} />} {canPayoutsBeProcessed && <Button type=\"primary\" text=\"Process Payouts\" onClick={handleProcessPayouts} />} </ButtonsWrapper> <Referral> <Widget loading=\" \" code={props.alem.componentsCode.CopyIcon} props={{ ...{ textToCopy: potLink, ...props } }} /> Earn referral fees </Referral> </HeaderWrapper> <div className=\"pool-table\"> <Widget loading=\" \" code={props.alem.componentsCode.PoolAllocationTable} props={{ ...{ allDonations: allDonations, potDetail: potDetail, ...props } }} /> </div> {isApplicationModalOpen && <Widget loading=\" \" code={props.alem.componentsCode.NewApplicationModal} props={{ ...{ onClose: () => setIsApplicationModalOpen(false), setIsDao: setIsDao, isDao: isDao, registryStatus: registryStatus, setRegistryStatus: setRegistryStatus, setApplicationSuccess: setApplicationSuccess, potDetail: potDetail, ...props } }} />} {isMatchingPoolModalOpen && <Widget loading=\" \" code={props.alem.componentsCode.FundModal} props={{ ...{ potDetail: potDetail, onClose: () => {setIsMatchingPoolModalOpen(false);}, ...props } }} />} {showChallengePayoutsModal && <Widget loading=\" \" code={props.alem.componentsCode.ChallengeModal} props={{ ...{ existingChallengeForUser: existingChallengeForUser, onClose: () => setShowChallengePayoutsModal(false), ...props } }} />} </A_45>; `, TimeLeft: ` const {daysLeft: daysLeft} = props; const [timeLeft, setTimeLeft] = useState(\"-\"); function formatTimeLeft(targetTimestamp) { const now = new Date().getTime(); const timeRemaining = targetTimestamp - now; if (timeRemaining <= 0) { return \"Time's up!\"; } const days = Math.floor(timeRemaining / (1000 * 60 * 60 * 24)); const hours = Math.floor(timeRemaining % (1000 * 60 * 60 * 24) / (1000 * 60 * 60)); const minutes = Math.floor(timeRemaining % (1000 * 60 * 60) / (1000 * 60)); const seconds = Math.floor(timeRemaining % (1000 * 60) / 1000); const formattedTime = \\`\\${days ? days + \"d\" : \"\"} \\${hours ? hours + \"h\" : \"\"} \\${minutes ? minutes + \"m\" : \"\"} \\${seconds ? seconds + \"s\" : \"\"}\\`; return formattedTime.trim(); } useEffect(() => { const intervelId = setInterval(() => { const time = formatTimeLeft(daysLeft); setTimeLeft(time); const now = new Date().getTime(); if (now > daysLeft) { clearInterval(intervelId); } }, 1000); }, []); return timeLeft; `, HeaderStatus: ` const Wrapper = styled.div\\` border-top: 1px solid rgb(199 199 199 / 50%); border-bottom: 1px solid rgb(199 199 199 / 50%); position: relative; display: flex; align-items: center; margin-top: -1px; pointer-events: none; .spread-indicator { height: auto; width: 12px; transition: all 300ms ease-in-out; display: none; } @media only screen and (max-width: 1100px) { pointer-events: all; cursor: pointer; .spread-indicator { display: block; } }\\`;const State = styled.div\\` display: flex; align-items: center; position: relative; gap: 1rem; font-size: 14px; white-space: nowrap; span { font-weight: 600; color: #dd3345; }\\`;const Loader = styled.div\\` position: relative; background: #dbdbdb; border-radius: 1px; height: 4px; width: 130px; @media only screen and (max-width: 1400px) { width: 90px; } @media only screen and (max-width: 1100px) { height: 40px; width: 4px; position: absolute; left: 10px; z-index: 0; top: 50%; }\\`;const ProgressBarWrapper = styled.div\\` position: relative; display: flex; .circle { width: 24px; height: 24px; transform: rotate(-90deg); } .check { width: 12px; position: absolute; transform: translate(-50%, -50%); top: 50%; left: 50%; } @media only screen and (max-width: 1100px) { z-index: 1; background: white; padding: 2px 0; }\\`; const ProgressBar = ({ progress, completed, started}) => <ProgressBarWrapper> <svg viewBox=\"0 0 160 160\" className=\"circle\"> <circle r=\"70\" cx=\"80\" cy=\"80\" fill=\"transparent\" stroke={completed ? \"#629D13\" : started ? \"#000000\" : \"#C7C7C7\"} strokeWidth=\"12px\"> </circle> <circle r=\"70\" cx=\"80\" cy=\"80\" fill=\"transparent\" stroke=\"#C7C7C7\" strokeWidth=\"12px\" strokeDasharray=\"439.6px\" strokeDashoffset={439.6 * progress + \"px\"}> </circle> </svg> <svg className=\"check\" viewBox=\"0 0 12 9\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M3.72667 7.05333L0.946667 4.27333L0 5.21333L3.72667 8.94L11.7267 0.94L10.7867 0L3.72667 7.05333Z\" style={{ fill: completed ? \"#629D13\" : started ? \"#7B7B7B\" : \"#C7C7C7\" }} /> </svg> </ProgressBarWrapper>; const statsList = potDetail => { const { application_start_ms, application_end_ms, public_round_start_ms, public_round_end_ms, cooldown_end_ms, all_paid_out } = potDetail; const now = Date.now(); const stats = [{ label: \"Applications round\", daysLeft: application_end_ms, started: now >= application_start_ms, completed: now > application_end_ms, progress: now > application_end_ms ? 1 : (now - application_start_ms) / (application_end_ms - application_start_ms) }, { label: \"Matching round\", daysLeft: public_round_end_ms, started: now >= public_round_start_ms, completed: now > public_round_end_ms, progress: now > public_round_end_ms ? 1 : (now - public_round_start_ms) / (public_round_end_ms - public_round_start_ms) }, { label: \"Challenge period\", daysLeft: cooldown_end_ms, started: now >= public_round_end_ms, completed: now > cooldown_end_ms && !!cooldown_end_ms, progress: now > cooldown_end_ms && !!cooldown_end_ms ? 1 : (cooldown_end_ms - now) / (public_round_end_ms - cooldown_end_ms) }, { label: \"Payouts completed\", daysLeft: null, started: null, completed: all_paid_out, progress: all_paid_out ? 1 : 0 }]; return stats;}; const { potDetail: potDetail } = props; const [mobileMenuActive, setMobileMenuActive] = useState(false); const stats = statsList(potDetail); const getIndexOfActive = () => { let index = 0; stats.forEach((state, idx) => { if (state.started && !state.completed) { index = idx; } }); if (index === null) return 3; return index; }; const containerHeight = 181; const showActiveState = getIndexOfActive() * (containerHeight / 4); const Container = styled.div\\` display: flex; width: 100%; justify-content: center; transition: all 300ms ease-in-out; .mobile-selected { display: flex; justify-content: space-between; gap: 1rem; margin: 1rem 0; transition: all 300ms ease-in-out; } @media only screen and (max-width: 1100px) { justify-content: left; height: \\${containerHeight / 4}px; overflow: hidden; .mobile-selected { margin: 10px 0; transform: translateY(\\${-showActiveState}px); flex-direction: column; } } \\`; return <Wrapper onClick={() => setMobileMenuActive(!mobileMenuActive)}> <Container style={mobileMenuActive ? { height: containerHeight + \"px\" } : {}}> <div className=\"mobile-selected\" style={mobileMenuActive ? { transform: \"translateY(0px)\" } : {}}> {stats.map(({ label, daysLeft, progress, started, completed }, idx) => { return <State style={{ color: completed || started ? \"#000\" : \"#7b7b7b\" }} key={label}> <ProgressBar progress={progress} started={started} completed={completed} /> <div> {label} {!daysLeft && started && <span>pending </span>} {started && !completed && daysLeft && <span> ends in <Widget loading=\" \" code={props.alem.componentsCode.TimeLeft} props={{ ...{ daysLeft: daysLeft, ...props } }} /> </span>} {idx === 0 && !started && \" hasn’t started\"} </div> <Loader style={{ background: completed ? \"#629D13\" : \"#dbdbdb\", display: idx === 3 ? \"none\" : \"flex\" }} /> </State>; })} </div> </Container> <svg className=\"spread-indicator\" style={{ rotate: mobileMenuActive ? \"180deg\" : \"0deg\" }} viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.59 0.294922L6 4.87492L1.41 0.294922L0 1.70492L6 7.70492L12 1.70492L10.59 0.294922Z\" fill=\"#7B7B7B\" /> </svg> </Wrapper>; `, ConfigForm: ` const A_49 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; margin-bottom: 24px;\\`;const ModalHeaderLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const IconContainer = styled.div\\` width: 40px; height: 40px; background: #f0f0f0; border-radius: 50%; display: flex; justify-content: center; align-items: center; margin-right: 16px;\\`;const A_50 = styled.svg\\` width: 20px; height: 20px; cursor: pointer; transition: 300ms ease-in-out; :hover { rotate: 180deg; }\\`;const A_51 = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 600;\\`;const ModalDescription = styled.p\\` color: #2e2e2e; font-size: 16px; font-weight: 400;\\`;const A_52 = styled.div\\` height: 24px;\\`;const MembersCount = styled.span\\` color: #2e2e2e; font-weight: 600;\\`;const MembersText = styled.div\\` color: #7b7b7b; font-size: 12px; font-weight: 400;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const ModalMultiAccount = (__props__) => { const { onClose, titleText, descriptionText, unitText, inputValue, onInputChange, handleAddAccount, handleRemoveAccount, accountError, accountIds } = __props__; return <ModalOverlay onOverlayClick={onClose}> <A_49> <ModalHeaderLeft> <IconContainer> <A_50 viewBox=\"0 0 24 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M16.24 7.65C15.07 7.13 13.63 6.75 12 6.75C10.37 6.75 8.93 7.14 7.76 7.65C6.68 8.13 6 9.21 6 10.39V12H18V10.39C18 9.21 17.32 8.13 16.24 7.65ZM8.07 10C8.16 9.77 8.34 9.58 8.56 9.48C9.66 8.99 10.82 8.75 11.99 8.75C13.17 8.75 14.32 9 15.42 9.48C15.65 9.58 15.82 9.77 15.91 10H8.07Z\" fill=\"#151A23\" /> <path d=\"M1.22 8.58C0.48 8.9 0 9.62 0 10.43V12H4.5V10.39C4.5 9.56 4.73 8.78 5.13 8.1C4.76 8.04 4.39 8 4 8C3.01 8 2.07 8.21 1.22 8.58Z\" fill=\"#151A23\" /> <path d=\"M22.78 8.58C21.93 8.21 20.99 8 20 8C19.61 8 19.24 8.04 18.87 8.1C19.27 8.78 19.5 9.56 19.5 10.39V12H24V10.43C24 9.62 23.52 8.9 22.78 8.58Z\" fill=\"#151A23\" /> <path d=\"M12 6C13.66 6 15 4.66 15 3C15 1.34 13.66 0 12 0C10.34 0 9 1.34 9 3C9 4.66 10.34 6 12 6ZM12 2C12.55 2 13 2.45 13 3C13 3.55 12.55 4 12 4C11.45 4 11 3.55 11 3C11 2.45 11.45 2 12 2Z\" fill=\"#151A23\" /> <path d=\"M3.9999 2.49687L1.49677 5L3.9999 7.50313L6.50303 5L3.9999 2.49687Z\" fill=\"#151A23\" /> <path d=\"M20 3L17.5 7H22.5L20 3Z\" fill=\"#151A23\" /> </A_50> </IconContainer> <A_51>{titleText}</A_51> </ModalHeaderLeft> <A_50 viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" onClick={onClose}> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#7B7B7B\" /> </A_50> </A_49> <ModalDescription>{descriptionText}</ModalDescription> <A_62 {...{ placeholder: \"NEAR account ID\", value: inputValue, onChange: onInputChange, postInputChildren: <Button {...{ type: \"primary\", text: \"Add\", onClick: handleAddAccount, style: { borderRadius: \\`0px 4px 4px 0px\\` }, submit: true }} />, handleKeyPress: (e) => { if (e.key === \"Enter\") { handleAddAccount(); } }, error: accountError }} /> <A_52 /> <MembersText> <MembersCount>{accountIds.length} </MembersCount> {accountIds.length == 1 ? unitText : \\`\\${unitText}s\\`} </MembersText> <AccountsList {...{ accountIds, allowRemove: true, handleRemoveAccount }} /> </ModalOverlay>;}; const A_53 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_54 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const Error = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const InputContainer = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_55 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const Input = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`; const DateInput = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_53> {label && <A_54>{label}</A_54>} <InputContainer> {__props__.preInputChildren && __props__.preInputChildren} <Input type={__props__.selectTime ? \"datetime-local\" : \"date\"} placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? (() => {})} style={__props__.inputStyles || {}} /> {__props__.postInputChildren && __props__.postInputChildren} </InputContainer> <Error className={error ? \"show\" : \"\"}>{error}</Error> </A_53>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const MembersListItem = styled.div\\` padding: 16px 0px; border-top: 1px #f0f0f0 solid; display: flex; flex-direction: row; align-items: center; justify-content: space-between;\\`;const MembersListItemLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 16px;\\`;const MembersListItemText = styled.div\\` font-size: 16px; font-weight: 400; color: #2e2e2e;\\`;const RemoveMember = styled.a\\` color: #2e2e2e; font-size: 14px; font-weight: 600; visibility: hidden; cursor: pointer; opacity: 0; transition: opacity 0.2s ease-in-out; &:hover { text-decoration: none; } \\${MembersListItem}:hover & { visibility: visible; opacity: 1; }\\`; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const AccountsList = __props__ => { const { accountIds, allowRemove, handleRemoveAccount } = __props__; return <> {accountIds.map(accountId => { return <MembersListItem> <MembersListItemLeft> <ProfileImage {...{ accountId, style: { width: \"40px\", height: \"40px\", margin: \"0 -8px 0 0\", borderRadius: \"50%\", background: \"white\" }, imageClassName: \"rounded-circle w-100 h-100 d-block\", thumbnail: false, tooltip: true }} /> <MembersListItemText>@{accountId}</MembersListItemText> </MembersListItemLeft> {allowRemove && <RemoveMember onClick={() => handleRemoveAccount(accountId)}>Remove</RemoveMember>} </MembersListItem>; })} </>;}; const A_56 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_57 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_58 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_59 = styled.div\\` display: flex; flex-direction: row; width: 100%; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`;const A_60 = styled.div\\` display: flex; justify-content: center; align-items: center; height: 100%; text-align: center; padding: 14px 16px; border-right: 1px #f0f0f0 solid; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`;const A_61 = styled.input\\` border: none; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; gap: 0.5em; color: #101828; width: 100%; border-radius: 4px;\\`;const PercentageSign = styled.span\\` display: flex; align-items: center; padding: 0 0.75em; color: #7b7b7b; font-size: 16px; font-weight: 400;\\`; const A_62 = (__props__) => { const label = __props__.label ?? \"\"; const placeholder = __props__.placeholder ?? \"\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const onBlur = __props__.onBlur ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_56> {label && <A_57>{label}</A_57>} <A_59> {__props__.preInputChildren && __props__.preInputChildren} <A_61 type=\"text\" placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={(value) => { validate(); if (onBlur) onBlur(value); }} disabled={!!__props__.disabled} onKeyDown={__props__.handleKeyPress ?? null} style={__props__.inputStyles || {}} name={__props__.name} /> {__props__.percent && <PercentageSign>%</PercentageSign>} {__props__.postInputChildren && __props__.postInputChildren} </A_59> <A_58 className={error ? \"show\" : \"\"}>{error}</A_58> </A_56>;}; const validateNearAddress = address => { const NEAR_ACCOUNT_ID_REGEX = /^(?=.{2,64}$)(?!.*\\\\.\\\\.)(?!.*-$)(?!.*_$)[a-z\\\\d._-]+$/i; let isValid = NEAR_ACCOUNT_ID_REGEX.test(address); if (address.length < 64 && !address.endsWith(\".near\")) { isValid = false; } return isValid;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_63 = styled.div\\` display: flex; flex-direction: column; padding: 32px 0px; width: 100%; @media screen and (max-width: 880px) { padding: 10px 10px; }\\`;const A_64 = styled.div\\` height: 2px; width: 100%; background-color: #ebebeb; @media screen and (max-width: 768px) { display: none; }\\`;const A_65 = styled.div\\` display: flex; flex-direction: row; margin: 48px 0; @media screen and (max-width: 768px) { flex-direction: column; gap: 32px; }\\`;const A_66 = styled.div\\` width: 30%; display: flex; flex-direction: column; gap: 16px; @media screen and (max-width: 768px) { width: 100%; }\\`;const A_67 = styled.div\\` width: 70%; display: flex; flex-direction: column; gap: 26px; @media screen and (max-width: 768px) { width: 100%; }\\`;const A_68 = styled.div\\` color: #2e2e2e; font-size: 16; font-weight: 600; word-wrap: break-word;\\`;const A_69 = styled.div\\` color: #2e2e2e; font-size: 16; font-weight: 400; word-wrap: break-word;\\`;const A_70 = styled.div\\` display: flex; flex-direction: row; gap: 24px; align-items: end; justify-content: flex-start; @media screen and (max-width: 768px) { flex-direction: column; align-items: flex-start; }\\`;const CheckboxWrapper = styled.div\\` display: flex; @media screen and (max-width: 768px) { flex-direction: row; }\\`;const A_71 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const {potDetail: potDetail, style: style} = props; const { potId } = props.alem.useParams(); const { NADABOT_HUMAN_METHOD, ONE_TGAS, NADABOT_CONTRACT_ID, SUPPORTED_FTS: { NEAR } } = constants; const potFactoryContractId = PotFactorySDK.getContractId(); const protocolConfig = PotFactorySDK.getProtocolConfig(); const DEFAULT_REGISTRY_PROVIDER = \\`\\${ListsSDK.getContractId()}:is_registered\\`; const DEFAULT_SYBIL_WRAPPER_PROVIDER = \\`\\${NADABOT_CONTRACT_ID}:\\${NADABOT_HUMAN_METHOD}\\`; const CURRENT_SOURCE_CODE_VERSION = \"0.1.0\"; const SOURCE_CODE_LINK = \"https://github.com/PotLock/core\"; const MAX_POT_NAME_LENGTH = 64; const MAX_POT_DESCRIPTION_LENGTH = 256; const MAX_MAX_PROJECTS = 100; const MAX_REFERRAL_FEE_MATCHING_POOL_BASIS_POINTS = 1000; const MAX_REFERRAL_FEE_PUBLIC_ROUND_BASIS_POINTS = 1000; const MAX_CHEF_FEE_BASIS_POINTS = 1000; Big.PE = 100; const isUpdate = !!potDetail; const convertToUTCTimestamp = localDateTime => { if (!localDateTime) { return; } return new Date(localDateTime).getTime(); }; const formatTimestampForDateTimeLocal = timestamp => { const date = new Date(timestamp); const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, \"0\"); const day = date.getDate().toString().padStart(2, \"0\"); const hours = date.getHours().toString().padStart(2, \"0\"); const minutes = date.getMinutes().toString().padStart(2, \"0\"); return \\`\\${year}-\\${month}-\\${day}T\\${hours}:\\${minutes}\\`; }; State.init({ owner: isUpdate ? potDetail.owner : context.accountId, ownerError: \"\", admin: \"\", admins: isUpdate ? potDetail.admins.map(accountId => ({ accountId })) : [], adminsError: \"\", isAdminsModalOpen: false, name: isUpdate ? potDetail.pot_name : \"\", nameError: \"\", customHandle: isUpdate ? potId.split(\\`.\\${potFactoryContractId}\\`)[0] : \"\", customHandleError: \"\", description: isUpdate ? potDetail.pot_description : \"\", descriptionError: \"\", referrerFeeMatchingPoolPercent: isUpdate ? potDetail.referral_fee_matching_pool_basis_points / 100 : \"\", referrerFeeMatchingPoolPercentError: \"\", referrerFeePublicRoundPercent: isUpdate ? potDetail.referral_fee_public_round_basis_points / 100 : \"\", referrerFeePublicRoundPercentError: \"\", protocolFeeBasisPoints: isUpdate ? potDetail.protocol_fee_basis_points : \"\", protocolFeeBasisPointsError: \"\", applicationStartDate: isUpdate ? formatTimestampForDateTimeLocal(potDetail.application_start_ms) : \"\", applicationStartDateError: \"\", applicationEndDate: isUpdate ? formatTimestampForDateTimeLocal(potDetail.application_end_ms) : \"\", applicationEndDateError: \"\", matchingRoundStartDate: isUpdate ? formatTimestampForDateTimeLocal(potDetail.public_round_start_ms) : \"\", matchingRoundStartDateError: \"\", matchingRoundEndDate: isUpdate ? formatTimestampForDateTimeLocal(potDetail.public_round_end_ms) : \"\", matchingRoundEndDateError: \"\", chef: isUpdate ? potDetail.chef : \"\", chefError: \"\", chefFeePercent: isUpdate ? potDetail.chef_fee_basis_points / 100 : \"\", chefFeePercentError: \"\", maxProjects: isUpdate ? potDetail.max_projects : \"\", maxProjectsError: \"\", baseCurrency: isUpdate ? potDetail.base_currency : \"\", baseCurrencyError: \"\", minMatchingPoolDonationAmount: NEAR.fromIndivisible(isUpdate ? potDetail.min_matching_pool_donation_amount : \"1\"), minMatchingPoolDonationAmountError: \"\", useNadabotSybil: isUpdate ? potDetail.sybil_wrapper_provider == DEFAULT_SYBIL_WRAPPER_PROVIDER : true, usePotlockRegistry: isUpdate ? potDetail.registry_provider == DEFAULT_REGISTRY_PROVIDER : true, latestSourceCodeCommitHash: \"\", deploymentSuccess: false }); if (!isUpdate && !state.latestSourceCodeCommitHash) { const res = fetch(\"https://api.github.com/repos/PotLock/core/commits\"); if (res.ok && res.body.length > 0) { State.update({ latestSourceCodeCommitHash: res.body[0].sha }); } } const getPotDetailArgsFromState = () => { const args = { owner: state.owner, admins: state.admins.filter(admin => !admin.remove).map(admin => admin.accountId), chef: state.chef || null, pot_name: state.name, pot_description: state.description, max_projects: parseInt(state.maxProjects) || null, application_start_ms: convertToUTCTimestamp(state.applicationStartDate), application_end_ms: convertToUTCTimestamp(state.applicationEndDate), public_round_start_ms: convertToUTCTimestamp(state.matchingRoundStartDate), public_round_end_ms: convertToUTCTimestamp(state.matchingRoundEndDate), min_matching_pool_donation_amount: NEAR.toIndivisible(state.minMatchingPoolDonationAmount).toString(), registry_provider: state.usePotlockRegistry ? DEFAULT_REGISTRY_PROVIDER : null, sybil_wrapper_provider: state.useNadabotSybil ? DEFAULT_SYBIL_WRAPPER_PROVIDER : null, custom_sybil_checks: null, custom_min_threshold_score: null, referral_fee_matching_pool_basis_points: parseInt((state.referrerFeeMatchingPoolPercent * 100).toFixed(0)), referral_fee_public_round_basis_points: parseInt((state.referrerFeePublicRoundPercent * 100).toFixed(0)), chef_fee_basis_points: parseInt((state.chefFeePercent * 100).toFixed(0)), source_metadata: isUpdate ? null : { version: CURRENT_SOURCE_CODE_VERSION, commit_hash: state.latestSourceCodeCommitHash, link: SOURCE_CODE_LINK } }; return args; }; const canDeploy = useMemo(() => { if (!state.owner || state.ownerError || !state.name || state.nameError || !state.description || state.descriptionError || !state.referrerFeeMatchingPoolPercent || state.referrerFeeMatchingPoolPercentError || !state.applicationStartDate || state.applicationStartDateError || !state.applicationEndDate || state.applicationEndDateError || !state.matchingRoundStartDate || state.matchingRoundStartDateError || !state.matchingRoundEndDate || state.matchingRoundEndDateError || !state.chef || state.chefError || !state.chefFeePercent || state.chefFeePercentError || !state.maxProjects || state.maxProjectsError) { return false; } return true; }, [state]); const handleDeploy = () => { const deployArgs = getPotDetailArgsFromState(); console.log(\"deployArgs: \", deployArgs); Near.asyncView(potFactoryContractId, \"calculate_min_deployment_deposit\", { args: deployArgs }).then(amount => { const amountYoctos = Big(amount).plus(Big(\"20000000000000000000000\")); const args = { pot_args: deployArgs }; if (state.customHandle) { args.pot_handle = state.customHandle; } const transactions = [{ contractName: potFactoryContractId, methodName: \"deploy_pot\", deposit: amountYoctos, args, gas: ONE_TGAS.mul(300) }]; const now = Date.now(); Near.call(transactions); const pollIntervalMs = 1000; const pollId = setInterval(() => { PotFactorySDK.asyncGetPots().then(pots => { const pot = pots.find(pot => pot.deployed_by === context.accountId && pot.deployed_at_ms > now); if (pot) { clearInterval(pollId); State.update({ deploymentSuccess: true }); } }); }, pollIntervalMs); }); }; const handleUpdate = () => { const updateArgs = getPotDetailArgsFromState(); const depositFloat = JSON.stringify(updateArgs).length * 0.00003; const deposit = Big(depositFloat).mul(Big(10).pow(24)); const transactions = [{ contractName: potId, methodName: \"admin_dangerously_set_pot_config\", deposit, args: { update_args: updateArgs }, gas: ONE_TGAS.mul(100) }]; Near.call(transactions); }; const validateAndUpdatePercentages = (percent, stateKey, errorKey, maxVal) => { const updates = { [errorKey]: \"\" }; if (!percent) { updates[stateKey] = \"0\"; } else { const split = percent.split(\".\"); if (split.length > 2) { return; } if (split.length === 2 && split[1].length > 2) { return; } if (percent.endsWith(\".\") && percent.indexOf(\".\") === percent.length - 1) { State.update({ [stateKey]: percent }); return; } const percentFloat = parseFloat(percent); if (percentFloat) { updates[stateKey] = percentFloat.toString(); if (percentFloat > maxVal) { updates[errorKey] = \\`Maximum \\${maxVal}%\\`; } } } State.update(updates); }; const handleAddAdmin = () => { let isValid = validateNearAddress(state.admin); if (!isValid) { State.update({ adminsError: \"Invalid NEAR account ID\" }); return; } if (!state.admins.find(admin => admin.accountId == state.admin && !admin.remove)) { const newAdmin = { accountId: state.admin.toLowerCase() }; const admins = [...state.admins, newAdmin]; State.update({ admins, admin: \"\", adminsError: \"\" }); } }; const handleRemoveAdmin = accountId => { State.update({ admins: state.admins.map(admin => { if (admin.accountId == accountId) { return { ...admin, remove: true }; } return admin; }) }); }; const userIsOwner = context.accountId === potDetail?.owner; const userIsAdmin = isUpdate && potDetail.admins.includes(context.accountId || \"\"); const isAdminOrGreater = userIsOwner || userIsAdmin; const FormSectionLeft = (title, description) => { return <A_66> <A_68>{title}</A_68> <A_69>{description}</A_69> </A_66>; }; return <A_63> <A_65> {FormSectionLeft(\"Pot details\", \"\")} <A_67> <A_62 {...{ label: \"Owner *\", placeholder: \\`E.g. \\${context.accountId}\\`, value: state.owner, onChange: owner => State.update({ owner, ownerError: \"\" }), validate: () => { const valid = validateNearAddress(state.owner); State.update({ ownerError: valid ? \"\" : \"Invalid NEAR account ID\" }); }, error: state.ownerError, disabled: isUpdate ? !userIsOwner : true }} /> <A_71>Admins</A_71> <AccountsList {...{ accountIds: state.admins.filter(account => !account.remove).map(account => account.accountId), allowRemove: isUpdate ? userIsOwner : true, handleRemoveAccount: handleRemoveAdmin }} /> {(!isUpdate || userIsOwner) && <Button {...{ type: \"tertiary\", text: \"Add admins\", style: { width: \"fit-content\" }, onClick: () => State.update({ isAdminsModalOpen: true }) }} />} <A_62 {...{ label: \"Name *\", placeholder: \"E.g. DeFi Center\", value: state.name, onChange: name => State.update({ name, nameError: \"\" }), validate: () => { const valid = state.name.length <= MAX_POT_NAME_LENGTH; State.update({ nameError: valid ? \"\" : \\`Name must be \\${MAX_POT_NAME_LENGTH} characters or less\\` }); }, error: state.nameError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_62 {...{ label: \"Custom handle (optional - will slugify name by default)\", placeholder: \"e.g. my-pot-handle\", value: state.customHandle, onChange: customHandle => State.update({ customHandle, customHandleError: \"\" }), validate: () => { const suffix = \\`.\\${potFactoryContractId}\\`; const fullAddress = \\`\\${state.customHandle}\\${suffix}\\`; let customHandleError = \"\"; if (fullAddress.length > 64) { customHandleError = \\`Handle must be \\${64 - suffix.length} characters or less\\`; } else { const valid = validateNearAddress(fullAddress); customHandleError = valid ? \"\" : \\`Invalid handle (can only contain lowercase alphanumeric symbols + _ or -)\\`; } State.update({ customHandleError }); }, error: state.customHandleError, disabled: isUpdate }} /> <TextArea {...{ label: \"Description\", placeholder: \"Type description\", value: state.description, onChange: description => State.update({ description }), validate: () => { const valid = state.description.length <= MAX_POT_DESCRIPTION_LENGTH; State.update({ descriptionError: valid ? \"\" : \\`Description must be \\${MAX_POT_DESCRIPTION_LENGTH} characters or less\\` }); }, error: state.descriptionError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_70> <A_62 {...{ label: \"Referrer fee % (matching pool)\", placeholder: \"0\", percent: true, value: state.referrerFeeMatchingPoolPercent, onChange: percent => { validateAndUpdatePercentages(percent, \"referrerFeeMatchingPoolPercent\", \"referrerFeeMatchingPoolPercentError\", MAX_REFERRAL_FEE_MATCHING_POOL_BASIS_POINTS / 100); }, validate: () => {}, error: state.referrerFeeMatchingPoolPercentError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_62 {...{ label: \"Referrer fee % (public round)\", placeholder: \"0\", percent: true, value: state.referrerFeePublicRoundPercent, onChange: percent => { validateAndUpdatePercentages(percent, \"referrerFeePublicRoundPercent\", \"referrerFeePublicRoundPercentError\", MAX_REFERRAL_FEE_PUBLIC_ROUND_BASIS_POINTS / 100); }, validate: () => {}, error: state.referrerFeeMatchingPoolPercentError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_62 {...{ label: \"Protocol fee %\", value: protocolConfig ? \\`\\${protocolConfig.basis_points / 100}\\` : \"-\", disabled: true, percent: true }} /> </A_70> <DateInput {...{ label: \"Application start date\", selectTime: true, value: state.applicationStartDate, onChange: date => { State.update({ applicationStartDate: date }); }, validate: () => { const now = new Date().getTime(); const applicationStartDate = new Date(state.applicationStartDate).getTime(); const applicationEndDate = new Date(state.applicationEndDate).getTime(); const valid = applicationStartDate > now && (!applicationEndDate || applicationStartDate < applicationEndDate); State.update({ applicationStartDateError: valid ? \"\" : \"Invalid application start date\" }); }, error: state.applicationStartDateError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <DateInput {...{ label: \"Application end date\", selectTime: true, value: state.applicationEndDate, onChange: date => State.update({ applicationEndDate: date }), validate: () => { const valid = (!state.matchingRoundStartDate || state.applicationEndDate < state.matchingRoundStartDate) && (!state.applicationStartDate || state.applicationEndDate > state.applicationStartDate); State.update({ applicationEndDateError: valid ? \"\" : \"Invalid application end date\" }); }, error: state.applicationEndDateError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <DateInput {...{ label: \"Matching round start date\", selectTime: true, value: state.matchingRoundStartDate, onChange: date => State.update({ matchingRoundStartDate: date }), validate: () => { const valid = (!state.applicationEndDate || state.matchingRoundStartDate > state.applicationEndDate) && (!state.matchingRoundEndDate || state.matchingRoundStartDate < state.matchingRoundEndDate); State.update({ matchingRoundStartDateError: valid ? \"\" : \"Invalid round start date\" }); }, error: state.matchingRoundStartDateError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <DateInput {...{ label: \"Matching round end date\", selectTime: true, value: state.matchingRoundEndDate, onChange: date => State.update({ matchingRoundEndDate: date }), validate: () => { const valid = !state.matchingRoundStartDate || state.matchingRoundEndDate > state.matchingRoundStartDate; State.update({ matchingRoundEndDateError: valid ? \"\" : \"Invalid round end date\" }); }, error: state.matchingRoundEndDateError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_70> <A_62 {...{ label: \"Optional: Min matching pool donation amount (in NEAR)\", placeholder: \"0\", value: state.minMatchingPoolDonationAmount, onChange: amountNear => { State.update({ minMatchingPoolDonationAmount: amountNear }); }, validate: () => {}, error: state.referrerFeeMatchingPoolPercentError, disabled: isUpdate ? !isAdminOrGreater : false }} /> </A_70> </A_67> </A_65> {} <A_65> {FormSectionLeft(\"Chef details\", \"\")} <A_67> <A_70> <A_62 {...{ label: \"Assign chef\", placeholder: \"E.g. user.near\", value: state.chef, onChange: chef => State.update({ chef }), validate: () => { const valid = validateNearAddress(state.chef); State.update({ chefError: valid ? \"\" : \"Invalid NEAR account ID\" }); }, error: state.chefError, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_62 {...{ label: \"Chef fee %\", placeholder: \"0\", percent: true, value: state.chefFeePercent, onChange: percent => { validateAndUpdatePercentages(percent, \"chefFeePercent\", \"chefFeePercentError\", MAX_CHEF_FEE_BASIS_POINTS / 100); }, validate: () => {}, error: state.chefFeePercentError, disabled: isUpdate ? !isAdminOrGreater : false }} /> </A_70> </A_67> </A_65> {} <A_65> {FormSectionLeft(\"Application details\", \"\")} <A_67> <A_62 {...{ label: \"Max. approved projects\", placeholder: \"e.g. 20\", value: state.maxProjects, onChange: maxProjects => State.update({ maxProjects }), validate: () => { const valid = parseInt(state.maxProjects) <= MAX_MAX_PROJECTS; State.update({ maxProjectsError: valid ? \"\" : \\`Maximum \\${MAX_MAX_PROJECTS}\\` }); }, error: state.maxProjectsError, disabled: isUpdate ? !isAdminOrGreater : false }} /> </A_67> </A_65> <A_65> {FormSectionLeft(\"Project Registration\", \"\")} <A_67> <A_70> <CheckboxWrapper> <CheckBox {...{ id: \"registrationSelector\", checked: state.usePotlockRegistry, onClick: e => { State.update({ usePotlockRegistry: e.target.checked }); }, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_71 htmlFor=\"sybilSelector\">Require approval on PotLock registry (recommended)</A_71> </CheckboxWrapper> </A_70> </A_67> </A_65> <A_65> {FormSectionLeft(\"Donor Sybil Resistance\", \"\")} <A_67> <A_70> <CheckboxWrapper> <CheckBox {...{ id: \"sybilSelector\", checked: state.useNadabotSybil, onClick: e => { State.update({ useNadabotSybil: e.target.checked }); }, disabled: isUpdate ? !isAdminOrGreater : false }} /> <A_71 htmlFor=\"sybilSelector\">🤖 nada.bot human verification (recommended)</A_71> </CheckboxWrapper> </A_70> <A_70 style={{ justifyContent: \"flex-end\", marginTop: \"36px\" }}> {!isUpdate && isAdminOrGreater && <Button {...{ type: \"tertiary\", text: \"Cancel\", style: style || {}, onClick: () => {} }} />} {(isUpdate && isAdminOrGreater || !isUpdate) && <Button {...{ type: \"primary\", text: isUpdate ? \"Save changes\" : \"Deploy\", style: style || {}, onClick: isUpdate ? handleUpdate : handleDeploy }} />} </A_70> </A_67> </A_65> {state.isAdminsModalOpen && <ModalMultiAccount {...{ onClose: () => State.update({ isAdminsModalOpen: false }), titleText: \"Add admins\", descriptionText: \"Add NEAR account IDs for your admins.\", inputValue: state.admin, onInputChange: admin => { State.update({ admin, adminsError: \"\" }); }, handleAddAccount: handleAddAdmin, handleRemoveAccount: handleRemoveAdmin, accountError: state.adminsError, accountIds: state.admins.map(admin => admin.accountId), unitText: \"admin\" }} />} </A_63>; `, Settings: ` const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_72 = styled.div\\` display: flex; flex-direction: column; width: 100%;\\`;const A_73 = styled.div\\` font-weight: 600; font-size: 22px;\\`;const PrviewContainer = styled.div\\` display: flex; flex-direction: column; max-width: 922px;\\`;const AdminsWrapper = styled.div\\` position: absolute; display: flex; flex-direction: column; opacity: 0; pointer-events: none; padding-top: 5px; transition: all 300ms; top: 100%; .list { background: white; color: #292929; border-radius: 4px; overflow: hidden; box-shadow: 0px 0px 1px 0px rgba(41, 41, 41, 0.74), 0px 3px 3px 0px rgba(123, 123, 123, 0.12), 0px 6px 6px 0px rgba(123, 123, 123, 0.12); a { display: flex; align-items: center; gap: 0.5rem; padding: 12px 16px; color: #292929; transition: 300ms; .profile-image { width: 24px; height: 24px; box-shadow: 0px 0px 1px 0px #a6a6a6 inset; border: 2px solid #f8d3b0; border-radius: 50%; } &:hover { background: #292929; text-decoration: none; color: white; } } } .tip-icon { display: flex; justify-content: center; z-index: 1; svg { stroke: rgb(41 41 41 / 21%); } }\\`;const Admins = styled.div\\` display: flex; font-size: 11px; gap: 2rem; .owner { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; .address { display: flex; gap: 0.5rem; align-items: center; div { font-size: 14px; font-weight: 500; } .profile-image { width: 24px; height: 24px; border-radius: 50%; } } } .admins { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; .avaters { display: flex; gap: 0.5rem; } .profile-image { width: 24px; height: 24px; } .icons-tolltip { position: relative; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; color: white; border-radius: 50%; border: 2px solid #f8d3b0; background: #b8182d; &:hover { \\${AdminsWrapper} { opacity: 1; pointer-events: all; } } } } .edit { display: flex; gap: 0.5rem; color: #dd3345; align-items: center; cursor: pointer; margin-left: auto; } @media only screen and (max-width: 768px) { flex-wrap: wrap; .edit { width: 100%; } }\\`;const Detail = styled.div\\` display: flex; flex-direction: column; gap: 1rem; border-radius: 12px; border: 1px solid #c7c7c7; padding: 3rem; margin-top: 1.5rem; .row-field { display: flex; align-items: center; gap: 2rem; font-size: 14px; } .label { font-weight: 500; max-width: 238px; width: 100%; text-align: left; } .input { color: #7b7b7b; } @media only screen and (max-width: 768px) { padding: 1rem; gap: 1.5rem; .row-field { flex-direction: column; align-items: flex-start; gap: 4px; } }\\`; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const A_74 = (timestamp) => { const date = new Date(timestamp); const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, \"0\"); const day = date.getDate().toString().padStart(2, \"0\"); const hours = date.getHours().toString().padStart(2, \"0\"); const minutes = date.getMinutes().toString().padStart(2, \"0\"); return \\`\\${year}-\\${month}-\\${day}T\\${hours}:\\${minutes}\\`;}; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const getFields = (potId, potDetail) => { const { owner, chef, admins, pot_name, pot_description, max_projects, application_start_ms, application_end_ms, public_round_start_ms, public_round_end_ms, sybil_wrapper_provider, referral_fee_matching_pool_basis_points, referral_fee_public_round_basis_points, chef_fee_basis_points, min_matching_pool_donation_amount, registry_provider } = potDetail; const { SUPPORTED_FTS: { NEAR } } = constants; const potFactoryContractId = PotFactorySDK.getContractId(); return [{ label: \"Name\", val: pot_name }, { label: \"Custom handle\", val: potId.split(\\`.\\${potFactoryContractId}\\`)[0] }, { label: \"Description\", val: pot_description }, { label: \"Referrer fee % (matching pool)\", val: referral_fee_matching_pool_basis_points / 100 + \"%\" }, { label: \"Referrer fee % (public round)\", val: referral_fee_public_round_basis_points / 100 + \"%\" }, { label: \"Application date\", val: \\`\\${A_74(application_start_ms)} - \\${A_74(application_end_ms)}\\` }, { label: \"Matching round date\", val: \\`\\${A_74(public_round_start_ms)} - \\${A_74(public_round_end_ms)}\\` }, { label: \"Min matching pool donation\", val: NEAR.fromIndivisible(min_matching_pool_donation_amount) }, { label: \"Chef fee\", val: chef_fee_basis_points / 100 + \"%\" }, { label: \"Assigned Chef\", val: chef }, { label: \"Max. approved projects\", val: max_projects }, { label: \"Registry Provider\", val: registry_provider }, { label: \"Donor Sybil Resistance\", val: sybil_wrapper_provider ? \"🤖 nada.bot human verified\" : \"none\" }];}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const { potDetail: potDetail } = props; const { owner, admins } = potDetail; const { potId } = props.alem.useParams(); const [editSettings, setEditSettings] = useState(false); const userIsAdminOrGreater = PotSDK.isUserPotAdminOrGreater(potId, context.accountId); const fields = getFields(potId, potDetail); const AdminsTooltip = () => <AdminsWrapper> <div className=\"tip-icon\"> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 5.24537e-07L-2.54292e-07 8L12 8L6 5.24537e-07Z\" fill=\"white\" /> </svg> </div> <div className=\"list\"> {admins.slice(0, admins.length).map((admin) => <a href={hrefWithParams(\\`?tab=profile&accountId=\\${admin}\\`)} target=\"_blank\"> <ProfileImage style={{}} className=\"profile-image\" accountId={admin} /> <div>{admin}</div> </a>)} </div> </AdminsWrapper>; return editSettings ? <A_72> <A_73>Edit Pot settings</A_73> <Widget loading=\" \" code={props.alem.componentsCode.ConfigForm} props={{ ...{ potDetail: potDetail, ...props } }} /> </A_72> : <PrviewContainer> <Admins> <div className=\"owner\"> <div>Owner</div> <div className=\"address\"> <ProfileImage style={{}} className=\"profile-image\" accountId={owner} /> <div>{_address(owner, 15)}</div> </div> </div> {admins.length > 0 && <div className=\"admins\"> <div>Admins</div> <div className=\"avaters\"> {admins.slice(0, 4).map((admin, idx) => <OverlayTrigger placement=\"bottom\" overlay={<Tooltip id={\\`tooltip-\\${idx}\\`}>{admin}</Tooltip>} key={admin}> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${admin}\\`)} target=\"_blank\"> <ProfileImage style={{}} className=\"profile-image\" accountId={admin} /> </a> </OverlayTrigger>)} {admins.length > 4 && <div className=\"icons-tolltip\"> +{admins.length - 4} <AdminsTooltip /> </div>} </div> </div>} {userIsAdminOrGreater && <div className=\"edit\" onClick={() => setEditSettings(true)}> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0.25 13.7501H3.0625L11.3575 5.45508L8.545 2.64258L0.25 10.9376V13.7501ZM1.75 11.5601L8.545 4.76508L9.235 5.45508L2.44 12.2501H1.75V11.5601Z\" fill=\"#DD3345\" /> <path d=\"M11.7777 0.469375C11.4852 0.176875 11.0127 0.176875 10.7202 0.469375L9.34766 1.84187L12.1602 4.65438L13.5327 3.28187C13.8252 2.98937 13.8252 2.51688 13.5327 2.22438L11.7777 0.469375Z\" fill=\"#DD3345\" /> </svg> Edit Pot </div>} </Admins> <Detail> {fields.map((field) => <div className=\"row-field\" key={field.label}> <div className=\"label\">{field.label}</div> <div className=\"input\">{field.val ?? \"-\"}</div> </div>)} </Detail> </PrviewContainer>; `, ChallangeResolveModal: ` const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_75 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; background: white; padding: 24px 24px 12px 24px; border-top-left-radius: 6px; border-top-right-radius: 6px; font-weight: 500;\\`;const A_76 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 24px; border-top: 1px #f0f0f0 solid; background: #fafafa; gap: 8px;\\`;const A_77 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-end; background: #fafafa; padding: 12px 24px 24px 24px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; gap: 24px; width: 100%;\\`;const HeaderItemText = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const {adminModalChallengerId: adminModalChallengerId, onClose: onClose} = props; State.init({ challengeAdminNotes: \"\", challengeAdminNotesError: \"\", resolveChallenge: false }); const { challengeAdminNotes, challengeAdminNotesError, resolveChallenge } = state; const { potId } = props.alem.useParams(); const payoutsChallenges = PotSDK.getPayoutsChallenges(potId); const MAX_CHALLENGE_TEXT_LENGTH = 1000; const handleAdminUpdateChallenge = () => { PotSDK.adminUpdatePayoutsChallenge(potId, adminModalChallengerId, challengeAdminNotes, resolveChallenge); State.update({ challengeAdminNotes: \"\", challengeAdminNotesError: \"\", resolveChallenge: false }); onClose(); }; const handleCancelAdminUpdateChallenge = () => { State.update({ challengeAdminNotes: \"\", challengeAdminNotesError: \"\", resolveChallenge: false }); onClose(); }; return <ModalOverlay onOverlayClick={onClose}> <A_75>Update Challenge from {adminModalChallengerId}</A_75> <A_76> <HeaderItemText>Challenge Reason:</HeaderItemText> <div> {payoutsChallenges.find(challenge => challenge.challenger_id === adminModalChallengerId).reason} </div> <TextArea {...{ noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Respond to the challenge here\", value: challengeAdminNotes, onChange: challengeAdminNotes => State.update({ challengeAdminNotes }), validate: () => { if (challengeAdminNotes.length > MAX_CHALLENGE_TEXT_LENGTH) { State.update({ challengeAdminNotesError: \\`Notes must be less than \\${MAX_CHALLENGE_TEXT_LENGTH} characters\\` }); return; } State.update({ challengeAdminNotesError: \"\" }); }, error: challengeAdminNotesError }} /> <CheckBox {...{ label: \"Resolve this challenge?\", checked: resolveChallenge, onClick: e => { State.update({ resolveChallenge: e.target.checked }); } }} /> </A_76> <A_77> <Button {...{ type: \"tertiary\", text: \"Cancel\", onClick: handleCancelAdminUpdateChallenge }} /> <Button {...{ type: \"primary\", text: \"Submit\", disabled: !challengeAdminNotes || !!challengeAdminNotesError, onClick: handleAdminUpdateChallenge }} /> </A_77> </ModalOverlay>; `, PayoutsChallenges: ` const AdminIcon = __props__ => <svg {...__props__} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <mask id=\"path-1-inside-1_10044_14103\" fill=\"white\"> <path d=\"M0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12Z\" /> </mask> <path d=\"M0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12Z\" fill=\"#656565\" /> <path d=\"M-0.666667 12C-0.666667 5.00439 5.00439 -0.666667 12 -0.666667C18.9956 -0.666667 24.6667 5.00439 24.6667 12H23.3333C23.3333 5.74077 18.2592 0.666667 12 0.666667C5.74077 0.666667 0.666667 5.74077 0.666667 12H-0.666667ZM24.6667 12.6667C24.6667 19.6623 18.9956 25.3333 12 25.3333C5.00439 25.3333 -0.666667 19.6623 -0.666667 12.6667L0.666667 12C0.666667 17.891 5.74077 22.6667 12 22.6667C18.2592 22.6667 23.3333 17.891 23.3333 12L24.6667 12.6667ZM12 25.3333C5.00439 25.3333 -0.666667 19.6623 -0.666667 12.6667V12C-0.666667 5.00439 5.00439 -0.666667 12 -0.666667V0.666667C5.74077 0.666667 0.666667 5.74077 0.666667 12C0.666667 17.891 5.74077 22.6667 12 22.6667V25.3333ZM12 -0.666667C18.9956 -0.666667 24.6667 5.00439 24.6667 12V12.6667C24.6667 19.6623 18.9956 25.3333 12 25.3333V22.6667C18.2592 22.6667 23.3333 17.891 23.3333 12C23.3333 5.74077 18.2592 0.666667 12 0.666667V-0.666667Z\" fill=\"#292929\" mask=\"url(#path-1-inside-1_10044_14103)\" /> <path d=\"M9.20029 12.7527L11.087 10.866L6.40695 6.19268C5.36695 7.23268 5.36695 8.91935 6.40695 9.96601L9.20029 12.7527ZM13.7203 11.546C14.7403 12.0193 16.1736 11.686 17.2336 10.626C18.507 9.35268 18.7536 7.52601 17.7736 6.54601C16.8003 5.57268 14.9736 5.81268 13.6936 7.08601C12.6336 8.14601 12.3003 9.57935 12.7736 10.5993L6.26695 17.106L7.20695 18.046L11.8003 13.466L16.387 18.0527L17.327 17.1127L12.7403 12.526L13.7203 11.546Z\" fill=\"white\" /> </svg>; const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_78 = styled.div\\` display: flex; flex-direction: column;\\`;const A_79 = styled.div\\` font-size: 18px; display: flex; align-items: center; gap: 1.5rem; width: fit-content; cursor: pointer; margin-bottom: 1.5rem; div:first-of-type { font-weight: 600; } svg { rotate: 180deg; transition: all 300ms ease-in-out; }\\`;const A_80 = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; transition: max-height 400ms ease-in-out; overflow: hidden; max-height: 1000px; opacity: 1; &.hidden { opacity: 0; max-height: 0; }\\`;const Challenge = styled.div\\` display: flex; padding: 1rem; border-bottom: 1px solid #c7c7c7; font-size: 14px; &:last-of-type { border-bottom: none; } .vertical-line { height: 100%; background: #c7c7c7; width: 1px; transform: translateX(0.75rem); z-index: -1; } .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0.5rem; } .admin-icon { margin-right: 0.75rem; svg { width: 1.5rem; height: 1.5rem; } } .content { display: flex; flex-direction: column; } .header { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; } .id { font-weight: 600; color: #292929; transition: 200ms; :hover { text-decoration: none; color: #dd3345; } } .title { font-weight: 600; color: #8b5af8; } .reason { color: #7b7b7b; margin-top: 0.5rem; margin-bottom: 1rem; padding-left: 2.5rem; background: white; } .admin-header { display: flex; align-items: center; gap: 0.25rem; } .dot { background: #7b7b7b; width: 5px; height: 5px; border-radius: 50%; } .resolved-state { font-weight: 600; } .resolve-btn { cursor: pointer; background: none; border: none; } @media only screen and (max-width: 480px) { .profile-image { margin-right: 0; } .admin-icon { margin-right: 0.25rem; } .reason { padding-left: 2rem; } .date { width: 100%; margin-top: -0.5rem; padding-left: 2rem; } }\\`;const A_81 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; background: white; padding: 24px 24px 12px 24px; border-top-left-radius: 6px; border-top-right-radius: 6px; font-weight: 500;\\`;const A_82 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 24px; border-top: 1px #f0f0f0 solid; background: #fafafa; gap: 8px;\\`;const A_83 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-end; background: #fafafa; padding: 12px 24px 24px 24px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; gap: 24px; width: 100%;\\`;const A_84 = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const Line = styled.div\\` width: 100%; height: 1px; background: #c7c7c7; margin: 3rem 0;\\`; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const { potId } = props.alem.useParams(); const payoutsChallenges = PotSDK.getPayoutsChallenges(potId); const userIsAdminOrGreater = PotSDK.isUserPotAdminOrGreater(potId, context.accountId); State.init({ adminModalChallengerId: \"\", toggleChallenges: false }); const { adminModalChallengerId, toggleChallenges } = state; return !payoutsChallenges ? \"Loading...\" : payoutsChallenges.length === 0 ? \"\" : <> <A_78> <A_79 onClick={() => State.update({ toggleChallenges: !toggleChallenges })}> <div>Payout Challenges</div> <div>{payoutsChallenges?.length}</div> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" style={{ rotate: toggleChallenges ? \"0deg\" : \"180deg\" }} fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294922L0 6.29492L1.41 7.70492L6 3.12492L10.59 7.70492L12 6.29492L6 0.294922Z\" fill=\"#151A23\" /> </svg> </A_79> <A_80 className={\\`\\${!toggleChallenges ? \"hidden\" : \"\"}\\`}> {payoutsChallenges.map(({ challenger_id, admin_notes, created_at, reason, resolved }) => <Challenge key={challenger_id}> {} <div className=\"content\"> <div className=\"header\"> <ProfileImage className=\"profile-image\" accountId={challenger_id} /> <a className=\"id\" href={hrefWithParams(\\`?tab=profile&accountId=\\${challenger_id}\\`)}> {challenger_id} </a> <div className=\"title\">Challenged payout</div> <div className=\"date\"> {getTimePassed(created_at)}</div> </div> <div className=\"reason\">{reason}</div> <div className=\"admin-header\"> <div className=\"admin-icon\"> <AdminIcon /> </div> <div className=\"resolved-state\" style={{ color: resolved ? \"#4a7714\" : \"#C7C7C7\" }}> {resolved ? \"Resolved\" : \"Unresolved\"} </div> {resolved ? <> <div className=\"dot\" /> <div>1 Response</div> </> : userIsAdminOrGreater ? <> <div className=\"dot\" /> <button className=\"resolve-btn\" onClick={() => State.update({ adminModalChallengerId: challenger_id })}> Reply </button> </> : \"\"} </div> <div className=\"reason\">{admin_notes}</div> </div> </Challenge>)} </A_80> {} {adminModalChallengerId && <Widget loading=\" \" code={props.alem.componentsCode.ChallangeResolveModal} props={{ ...{ adminModalChallengerId: adminModalChallengerId, onClose: () => State.update({ adminModalChallengerId: \"\" }), ...props } }} />} </A_78> <Line /> </>; `, FlaggedAccounts: ` const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_85 = styled.div\\` width: 100%; height: 1px; background: #c7c7c7; margin: 3rem 0;\\`;const A_86 = styled.div\\` display: flex; flex-direction: column; width: 100%;\\`;const A_87 = styled.div\\` font-size: 18px; display: flex; align-items: center; gap: 1.5rem; width: fit-content; cursor: pointer; margin-bottom: 1.5rem; div:first-of-type { font-weight: 600; } svg { rotate: 180deg; transition: all 300ms ease-in-out; }\\`;const A_88 = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; transition: max-height 400ms ease-in-out; overflow: hidden; max-height: 1000px; overflow-y: scroll; opacity: 1; &.hidden { opacity: 0; max-height: 0; }\\`;const Flag = styled.div\\` display: flex; padding: 1rem; border-bottom: 1px solid #c7c7c7; font-size: 14px; &:last-of-type { border-bottom: none; } .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0.5rem; } .content { display: flex; flex-direction: column; } .header { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; } .id { font-weight: 600; color: #292929; a { transition: 200ms; font-weight: 600; color: #292929; :hover { text-decoration: none; color: #dd3345; } } } .flagged-account { color: #ed464f; font-weight: 600; :hover { text-decoration: none; } } .role { color: #656565; font-weight: 600; } .reason { color: #656565; margin-top: 0.5rem; margin-bottom: 1rem; padding-left: 2.5rem; background: white; } .dot { background: #656565; width: 5px; height: 5px; border-radius: 50%; } @media only screen and (max-width: 480px) { .profile-image { margin-right: 0; } .reason { padding-left: 2rem; } }\\`; const {potDetail: potDetail} = props; const { potId } = props.alem.useParams(); const [flaggedAddresses, setFlaggedAddresses] = useState(null); const [toggleView, setToggleView] = useState(null); if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then(data => { const listOfFlagged = []; data.forEach(adminFlaggedAcc => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); addresses.forEach(address => { listOfFlagged.push({ address: address, reason: adminFlaggedAcc.potFlaggedAcc[address], flaggedBy: adminFlaggedAcc.flaggedBy, role: adminFlaggedAcc.role }); }); }); setFlaggedAddresses(listOfFlagged); }).catch(err => console.log(\"error getting the flagged accounts \", err)); } return !flaggedAddresses ? \"Loading...\" : flaggedAddresses.length === 0 ? \"\" : <> <A_86> <A_87 onClick={() => setToggleView(!toggleView)}> <div>Flagged Accounts</div> <div>{flaggedAddresses?.length}</div> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" style={{ rotate: toggleView ? \"0deg\" : \"180deg\" }} fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294922L0 6.29492L1.41 7.70492L6 3.12492L10.59 7.70492L12 6.29492L6 0.294922Z\" fill=\"#151A23\" /> </svg> </A_87> <A_88 className={\\`\\${!toggleView ? \"hidden\" : \"\"}\\`}> {flaggedAddresses.map(({ role, flaggedBy, address, reason }) => <Flag key={flaggedBy}> {} <div className=\"content\"> <div className=\"header\"> <ProfileImage className=\"profile-image\" accountId={flaggedBy} /> <div className=\"role\">{role}</div> <div className=\"dot\" /> <div className=\"id\"> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${flaggedBy}\\`)} target=\"_blank\"> {flaggedBy}{\" \"} </a> has flagged </div> <a className=\"flagged-account\" href={hrefWithParams(\\`?tab=profile&accountId=\\${address}\\`)} target=\"_blank\"> {address} </a> </div> <div className=\"reason\">{reason}</div> </div> </Flag>)} </A_88> </A_86> <A_85 /> </>; `, Payouts: ` const Arrow = styled.svg\\` width: 12px; rotate: 180deg; transition: all 200ms; display: none; @media screen and (max-width: 768px) { display: block; }\\`;const ArrowDown = __props__ => <Arrow {...__props__} viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294983L0 6.29498L1.41 7.70498L6 3.12498L10.59 7.70498L12 6.29498L6 0.294983Z\" fill=\"#7B7B7B\" /> </Arrow>; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const calculatePayouts = (allPotDonations, totalMatchingPool, blacklistedAccounts) => { const { NADABOT_CONTRACT_ID } = constants; return new Promise((resolve, reject) => { console.log(\"Calculting payouts; ignoring blacklisted donors &/or projects: \", blacklistedAccounts.join(\", \")); console.log(\"totalMatchingPool: \", totalMatchingPool); const projectContributions = []; const allDonors = new Set(); for (const d of allPotDonations) { if (blacklistedAccounts.includes(d.donor_id) || blacklistedAccounts.includes(d.project_id)) { continue; } const amount = new Big(d.total_amount); const val = [d.project_id, d.donor_id, amount]; projectContributions.push(val); allDonors.add(d.donor_id); } const limit = 100; let curIndex = 0; let humanScores = {}; let promises = []; while (curIndex < allDonors.size) { promises.push(Near.asyncView(NADABOT_CONTRACT_ID, \"get_human_score_batch\", { account_ids: Array.from(allDonors).slice(curIndex, curIndex + limit) })); curIndex += limit; } Promise.all(promises).then(res => { for (const r of res) { humanScores = { ...humanScores, ...r }; } }).catch(e => { console.error(\"error fetching human scores. Continuing anyway: \", e); }).finally(() => { console.log(\"human scores: \", humanScores); const contributions = {}; for (const [proj, user, amount] of projectContributions) { if (!humanScores[user] || !humanScores[user].is_human) { console.log(\"skipping non-human: \", user); continue; } if (!contributions[proj]) { contributions[proj] = {}; } contributions[proj][user] = Big(contributions[proj][user] || 0).plus(amount); } console.log(\"contributions: \", contributions); const pairTotals = {}; for (const contribz of Object.values(contributions)) { for (const [k1, v1] of Object.entries(contribz)) { if (!pairTotals[k1]) { pairTotals[k1] = {}; } for (const [k2, v2] of Object.entries(contribz)) { if (!pairTotals[k1][k2]) { pairTotals[k1][k2] = Big(0); } pairTotals[k1][k2] = pairTotals[k1][k2].plus(v1.times(v2).sqrt()); } } } const threshold = Big(\"25000000000000000000000000\"); const totalPot = Big(totalMatchingPool); let bigtot = Big(0); const totals = []; for (const [proj, contribz] of Object.entries(contributions)) { let tot = Big(0); let _num = 0; let _sum = Big(0); for (const [k1, v1] of Object.entries(contribz)) { _num += 1; _sum = _sum.plus(v1); for (const [k2, v2] of Object.entries(contribz)) { if (k2 > k1 || Object.keys(contribz).length === 1) { const sqrt = v1.times(v2).sqrt(); tot = tot.plus(sqrt.div(pairTotals[k1][k2].div(threshold))); } } } bigtot = bigtot.plus(tot); totals.push({ id: proj, number_contributions: _num, contribution_amount_str: _sum.toFixed(0), matching_amount_str: tot.toFixed(0) }); } console.log(\"totals before: \", totals); if (bigtot.gte(totalPot)) { console.log(\"NORMALIZING\"); for (const t of totals) { t.matching_amount_str = Big(t.matching_amount_str).div(bigtot).times(totalPot).toFixed(0); } } let totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } let residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"first round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(\"0\"))) { for (let i = 0; i < totals.length; i++) { let proportion = Big(totals[i].matching_amount_str).div(totalAllocatedBeforeRounding); let additionalAllocation = proportion.times(residual); totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(additionalAllocation).toFixed(0); } console.log(\"CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION\"); totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"second round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(0))) { totals.sort((a, b) => Big(b.matching_amount_str).minus(Big(a.matching_amount_str))); if (residual.gt(Big(0))) { totals[0].matching_amount_str = Big(totals[0].matching_amount_str).plus(residual).toFixed(0); } else { for (let i = 0; i < totals.length; i++) { if (Big(totals[i].matching_amount_str).gt(residual.abs())) { totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(residual).toFixed(0); break; } } } totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"Residual after final adjustment: \", residual.toFixed(0)); } } const payouts = totals.reduce((acc, t) => { acc[t.id] = { totalAmount: t.contribution_amount_str, matchingAmount: t.matching_amount_str, donorCount: t.number_contributions }; return acc; }, {}); resolve(payouts); }); });}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_89 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; width: 100%; @media screen and (min-width: 375px) and (max-width: 768px) { width: 99%; } @media screen and (max-width: 390px) { width: 98%; }\\`;const OuterTextContainer = styled.div\\` display: flex; flex-direction: row; gap: 10px; @media screen and (max-width: 768px) { padding-right: 10px; }\\`;const OuterText = styled.div\\` color: #7b7b7b; font-size: 14px; font-weight: 500; text-transform: uppercase; line-height: 24px; letter-spacing: 1.12px; word-wrap: break-word;\\`;const Count = styled.div\\` color: #dd3345; font-size: 14px; font-weight: 600; line-height: 24px;\\`;const TableContainer = styled.div\\` display: flex; flex-direction: column; align-items: center; border-radius: 6px; border: 1px solid #7b7b7b; width: 100%; overflow-x: auto; flex-wrap: nowrap;\\`;const A_90 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; gap: 2rem; padding: 0.5rem 1rem; border-bottom: 1px solid rgba(199, 199, 199, 0.5); @media only screen and (max-width: 768px) { display: none; }\\`;const HeaderItem = styled.div\\` display: flex; flex-direction: row; align-items: space-between; justify-content: flex-start; justify-content: space-between; width: 110px; justify-content: right; &.project { flex: 1; justify-content: left; } @media only screen and (max-width: 768px) { display: none; &.project { display: flex; } }\\`;const A_91 = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const MobileAmount = styled.div\\` width: 100%; margin-left: 2rem; display: none; max-height: 0px; overflow: hidden; transition: all 200ms; span { font-weight: 600; } @media screen and (max-width: 768px) { order: 2; display: block; }\\`;const A_92 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; width: 100%; padding: 1rem; gap: 2rem; border-top: 1px solid rgba(199, 199, 199, 0.5); position: relative; .toggle-check { cursor: pointer; position: absolute; left: 0; top: 0; height: 100%; width: 100%; opacity: 0; display: none; } .toggle-check:checked ~ svg { rotate: 0deg; } .toggle-check:checked + \\${MobileAmount} { max-height: 100px; } @media screen and (max-width: 768px) { flex-wrap: wrap; gap: 0.5rem; .toggle-check { display: block; } }\\`;const RowItem = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; width: 110px; justify-content: right; &:hover { text-decoration: none; } &.project { flex: 1; display: flex; gap: 1rem; justify-content: left; transition: 200ms; a { color: #292929; font-weight: 600; transition: 200ms; &:hover { color: #dd3345; text-decoration: none; } } } @media screen and (max-width: 768px) { &.project { gap: 0.5rem; } &.donors, &.amount { display: none; } }\\`;const RowText = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; word-wrap: break-word; span { color: #7b7b7b; font-weight: 600; display: none; } @media screen and (max-width: 768px) { span { display: inline; } &:last-of-type { display: flex; gap: 4px; } }\\`;const SearchBarContainer = styled.div\\` display: flex; align-items: center; gap: 16px; width: 100%; background: #f6f5f3; padding: 0.5rem 1rem; @media only screen and (max-width: 768px) { gap: 8px; }\\`;const SearchBar = styled.input\\` background: none; width: 100%; outline: none; border: none; &:focus { outline: none; border: none; }\\`;const SearchIcon = styled.div\\` display: flex; width: 24px; height: 24px; align-items: center; justify-content: center;\\`;const InfoContainer = styled.div\\` display: flex; align-items: center; justify-content: center; padding: 8px; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; gap: 1rem; margin-left: auto; margin-bottom: 1.5rem;\\`;const WarningText = styled.div\\` text-align: center; color: #dd3345; font-weight: 500; font-size: 14px;\\`;const AlertSvg = styled.svg\\` width: 18px; @media screen and (max-width: 768px) { width: 1rem; }\\`; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const { potDetail: potDetail, allDonations: allDonations } = props; const { potId } = props.alem.useParams(); State.init({ allPayouts: null, filteredPayouts: null, flaggedAddresses: null }); const { allPayouts, filteredPayouts, flaggedAddresses } = state; if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => { const listOfFlagged = []; data.forEach((adminFlaggedAcc) => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); listOfFlagged.push(...addresses); }); State.update({ flaggedAddresses: listOfFlagged }); }).catch((err) => console.log(\"error getting the flagged accounts \", err)); } if (!allPayouts && allDonations && potDetail && flaggedAddresses) { calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((calculatedPayouts) => { if (potDetail.payouts.length) { const synthesizedPayouts = potDetail.payouts.map((payout) => { const { project_id, amount } = payout; const { totalAmount, donorCount } = calculatedPayouts[project_id]; return { projectId: project_id, totalAmount, matchingAmount: amount, donorCount }; }); State.update({ allPayouts: synthesizedPayouts, filteredPayouts: synthesizedPayouts }); } else { const allPayouts = Object.entries(calculatedPayouts).map(([projectId, { totalAmount, matchingAmount, donorCount }]) => { return { projectId, totalAmount, matchingAmount, donorCount }; }); allPayouts.sort((a, b) => { return b.matchingAmount - a.matchingAmount; }); State.update({ allPayouts, filteredPayouts: allPayouts }); } }); } const searchPayouts = (searchTerm) => { const filteredPayouts = allPayouts.filter((payout) => { const { projectId } = payout; const searchFields = [projectId]; return searchFields.some((field) => field.toLowerCase().includes(searchTerm.toLowerCase())); }); filteredPayouts.sort((a, b) => { return b.matchingAmount - a.matchingAmount; }); return filteredPayouts; }; const MAX_ACCOUNT_ID_DISPLAY_LENGTH = 10; return <A_89> <Widget loading=\" \" code={props.alem.componentsCode.FlaggedAccounts} props={{ ...{ potDetail: potDetail, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.PayoutsChallenges} props={{ ...{ ...props } }} /> {!potDetail.all_paid_out && <InfoContainer> <AlertSvg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7.25 4.25H8.75V5.75H7.25V4.25ZM7.25 7.25H8.75V11.75H7.25V7.25ZM8 0.5C3.86 0.5 0.5 3.86 0.5 8C0.5 12.14 3.86 15.5 8 15.5C12.14 15.5 15.5 12.14 15.5 8C15.5 3.86 12.14 0.5 8 0.5ZM8 14C4.6925 14 2 11.3075 2 8C2 4.6925 4.6925 2 8 2C11.3075 2 14 4.6925 14 8C14 11.3075 11.3075 14 8 14Z\" fill=\"#EE8949\" /> </AlertSvg> <WarningText> {potDetail.cooldown_end_ms ? \"These payouts have been set on the contract but have not been paid out yet.\" : \"These payouts are estimated amounts only and have not been set on the contract yet.\"} </WarningText> </InfoContainer>} <TableContainer> <A_90> <HeaderItem className=\"project\"> <A_91>Project</A_91> </HeaderItem> <HeaderItem> <A_91>Total Raised</A_91> </HeaderItem> <HeaderItem> <A_91>Unique Donors</A_91> </HeaderItem> <HeaderItem> <A_91>Pool Allocation</A_91> </HeaderItem> </A_90> <SearchBarContainer> <SearchIcon> <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.7549 14.2549H14.9649L14.6849 13.9849C15.6649 12.8449 16.2549 11.3649 16.2549 9.75488C16.2549 6.16488 13.3449 3.25488 9.75488 3.25488C6.16488 3.25488 3.25488 6.16488 3.25488 9.75488C3.25488 13.3449 6.16488 16.2549 9.75488 16.2549C11.3649 16.2549 12.8449 15.6649 13.9849 14.6849L14.2549 14.9649V15.7549L19.2549 20.7449L20.7449 19.2549L15.7549 14.2549ZM9.75488 14.2549C7.26488 14.2549 5.25488 12.2449 5.25488 9.75488C5.25488 7.26488 7.26488 5.25488 9.75488 5.25488C12.2449 5.25488 14.2549 7.26488 14.2549 9.75488C14.2549 12.2449 12.2449 14.2549 9.75488 14.2549Z\" fill=\"#C7C7C7\" /> </svg> </SearchIcon> <SearchBar placeholder=\"Search payouts\" onChange={({ target: { value } }) => { const filteredPayouts = searchPayouts(value); State.update({ filteredPayouts }); }} /> </SearchBarContainer> {!filteredPayouts ? <div>Loading</div> : filteredPayouts.length === 0 ? <A_92 style={{ padding: \"12px\" }}>No payouts to display</A_92> : filteredPayouts.map((payout, index) => { const { projectId, donorCount, matchingAmount, totalAmount } = payout; return <A_92 key={index}> <RowItem className=\"project\"> <ProfileImage style={{ height: \"24px\", width: \"24px\" }} className=\"profile-image\" accountId={projectId} /> <a href={\\`?tab=project&projectId=\\${projectId}\\`} target={\"_blank\"}> {projectId.length > MAX_ACCOUNT_ID_DISPLAY_LENGTH ? projectId.slice(0, MAX_ACCOUNT_ID_DISPLAY_LENGTH) + \"...\" : projectId} </a> </RowItem> {} <RowItem className=\"amount\"> <RowText>{yoctosToNear(totalAmount, true)}</RowText> </RowItem> <input type=\"checkbox\" className=\"toggle-check\" /> <MobileAmount> <span>{yoctosToNear(totalAmount, true)}</span> raised from <span>{donorCount}</span> unique donors </MobileAmount> {} <RowItem className=\"donors\"> <RowText>{donorCount}</RowText> </RowItem> {} <RowItem> <RowText> {yoctosToNear(matchingAmount, true)} <span>Allocated</span> </RowText> </RowItem> <ArrowDown /> </A_92>; })} </TableContainer> </A_89>; `, SponsorsTable: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_93 = styled.div\\` width: 100%; display: flex; flex-direction: column; align-items: center; gap: 2rem; border-radius: 6px; border: 1px solid #7b7b7b; overflow: hidden; .transcation { display: flex; flex-direction: column; width: 100%; font-size: 14px; .header { display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 1rem; gap: 1rem; color: #7b7b7b; div { display: flex; align-items: center; font-weight: 600; &:last-of-type { justify-content: flex-end; } } } .address { width: 190px; margin-right: auto; justify-content: start !important; } .rank { width: 40px; margin-right: 2rem; justify-content: center; } } @media only screen and (max-width: 768px) { .transcation { font-size: 12px; .header { padding: 0.5rem; } .rank { margin-right: 0; width: 30px; } } } @media only screen and (max-width: 480px) { .transcation .address { width: 135px; flex: 1; } }\\`;const A_94 = styled.div\\` display: flex; align-items: center; justify-content: space-between; width: 100%; gap: 1rem; padding: 1rem; border-top: 1px solid #c7c7c7; > div { display: flex; align-items: center; } .address { color: #292929; font-weight: 600; display: flex; align-items: center; justify-content: flex-start; border-radius: 2px; transition: all 200ms; .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 1rem; } } .sponsors-amount { justify-content: flex-end; font-weight: 600; display: flex; align-items: center; gap: 1rem; } @media only screen and (max-width: 768px) { padding: 1rem 0.5rem; }\\`;const Percentage = styled.div\\` background: #ebebeb; box-shadow: 0px -1px 0px 0px #dbdbdb inset, 0px 0px 0px 0.5px #dbdbdb; border-radius: 4px; padding: 2px 4px; min-width: 60px; text-align: right;\\`;const A_95 = styled.div\\` font-size: 1.125rem; text-align: center;\\`; const { sponsors: sponsors } = props; const { tab } = props.alem.useParams(); const isInPot = tab === \"pot\"; const [currentPage, setCurrentPage] = useState(1); const perPage = 30; useEffect(() => { setCurrentPage(1); }, []); let totalDonations = 0; sponsors.forEach((donation) => { totalDonations += donation.amount; }); return sponsors.length ? <A_93> <div className=\"transcation\"> <div className=\"header\"> <div className=\"rank\">Rank</div> <div className=\"address\">Donor</div> <div>Amount</div> {A_236 && !isInPot && <div>Amount (USD)</div>} </div> {sponsors.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation, idx) => { const { donor_id, amount, percentage_share } = donation; return <A_94> <div className=\"rank\">#{idx + 1 + (currentPage - 1) * perPage}</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`)} className=\"address\" target=\"_blank\"> <ProfileImage style={{}} accountId={donor_id} /> <OverlayTrigger placement=\"top\" overlay={<Tooltip>{donor_id}</Tooltip>}> <div> {_address(donor_id, 15)}</div> </OverlayTrigger> </a> <div className=\"sponsors-amount\"> {amount.toFixed(2).replace(/[.,]00$/, \"\")}N{\" \"} <Percentage>{percentage_share === \"0\" ? \"<0.01\" : percentage_share}%</Percentage>{\" \"} </div> </A_94>; })} </div> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: sponsors, currentPage, perPage: perPage }, ...props } }} /> </A_93> : <A_95>No Sponsors</A_95>; `, Sponsors: ` const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_96 = styled.div\\` display: flex; gap: 2rem; min-height: 430px; width: 100%; .col { display: flex; flex-direction: column; gap: 2rem; } .item { position: relative; display: flex; justify-content: center; gap: 1rem; flex-direction: column; border-radius: 12px; background: #fef6ee; height: 50%; padding: 24px; font-size: 14px; &.first { box-shadow: 0px 1px 2px -1px rgba(0, 0, 0, 0.08), 0px 1px 1px -1px rgba(0, 0, 0, 0.12); border: 2px solid #dd3345; align-items: center; text-align: center; height: 100%; .profile-image { width: 64px; height: 64px; } .footer { flex-direction: column; gap: 1rem; } .amount { font-size: 32px; font-family: \"Lora\"; } } .profile-image { width: 40px; height: 40px; border-radius: 50%; display: flex !important; @media only screen and (max-width: 480px) { width: 40px; height: 40px; } } .name { white-space: nowrap; font-weight: 600; color: #292929; transition: all 300ms; font-size: 1rem; :hover { color: #dd3345; text-decoration: none; } } .footer { display: flex; align-items: center; justify-content: space-between; width: 100%; } .amount { font-size: 22px; font-weight: 600; } .percentage { font-size: 1rem; background: #f8d3b0; padding: 4px 8px; border-radius: 4px; font-weight: 600; height: fit-content; } } @media only screen and (max-width: 960px) { gap: 1rem; flex-direction: column; .col { gap: 1rem; } .col:nth-child(2) { order: -1; } }\\`; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const Sponsor = ({ donation: { amount, donor_id, percentage_share }, colIdx}) => { const profile = Social.getr(\\`\\${donor_id}/profile\\`); return <div className={\\`item \\${colIdx === 2 && \"first\"}\\`}> <ProfileImage style={{}} profile={profile} /> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`)} target=\"_blank\" className=\"name\"> {_address(profile.name || donor_id, 15)} </a> <div>{_address(profile.description, colIdx === 2 ? 120 : 35)}</div> <div className=\"footer\"> <div className=\"amount\">{amount} NEAR</div> <div className=\"percentage\">{percentage_share}%</div> </div> </div>;};const SponsorsBoard = (__props__) => { const { donations } = __props__; const sponsorsLeaderboard = [donations.slice(1, 3), donations.slice(0, 1), donations.slice(3, 5)].filter((subList) => subList.length > 0); return <A_96> {sponsorsLeaderboard.map((donationsCol, colIdx) => <div className=\"col\"> {donationsCol.map((donation, idx) => <Sponsor donation={donation} colIdx={colIdx + 1} idx={idx + 1} />)} </div>)} </A_96>;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_97 = styled.div\\` display: flex; flex-direction: column; align-items: center; gap: 24px; width: 100%; @media screen and (min-width: 375px) and (max-width: 768px) { width: 99%; } @media screen and (max-width: 390px) { width: 98%; }\\`;const A_98 = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; margin-top: 35px; padding-bottom: 1rem;\\`; const { potDetail: potDetail } = props; const { potId } = props.alem.useParams(); let sponsorshipDonations = PotSDK.getMatchingPoolDonations(potId); const { SUPPORTED_FTS } = constants; State.init({ sponsorshipDonations: null }); if (sponsorshipDonations && !state.sponsorshipDonations) { sponsorshipDonations = sponsorshipDonations.reduce((accumulator, currentDonation) => { accumulator[currentDonation.donor_id] = { amount: parseFloat(accumulator[currentDonation.donor_id].amount || 0) + parseFloat(SUPPORTED_FTS.NEAR.fromIndivisible(currentDonation.net_amount)), ...currentDonation }; return accumulator; }, {}); const total = parseFloat(SUPPORTED_FTS.NEAR.fromIndivisible(potDetail.matching_pool_balance)); sponsorshipDonations = Object.values(sponsorshipDonations).sort((a, b) => b.amount - a.amount); sponsorshipDonations = sponsorshipDonations.map((donation) => { return { ...donation, percentage_share: (donation.amount / total * 100).toFixed(2).replace(/[.,]00$/, \"\") }; }); State.update({ sponsorshipDonations }); } if (!state.sponsorshipDonations) return <div className=\"spinner-border text-secondary\" role=\"status\" />; return <A_97> <SponsorsBoard donations={state.sponsorshipDonations.slice(0, 6)} /> <A_98> <Widget loading=\" \" code={props.alem.componentsCode.SponsorsTable} props={{ ...{ sponsors: state.sponsorshipDonations, ...props } }} /> </A_98> </A_97>; `, FlagModal: ` const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const BannerBg = __props__ => <svg {...__props__} viewBox=\"0 0 145 152\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <rect x=\"157.654\" y=\"-37\" width=\"20\" height=\"161.118\" rx=\"10\" transform=\"rotate(45 157.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"189.654\" y=\"-37\" width=\"20\" height=\"245.972\" rx=\"10\" transform=\"rotate(45 189.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"221.654\" y=\"-37\" width=\"20\" height=\"164.654\" rx=\"10\" transform=\"rotate(45 221.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"125.654\" y=\"-37\" width=\"20\" height=\"177.702\" rx=\"10\" transform=\"rotate(45 125.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"93.6543\" y=\"-37\" width=\"20\" height=\"78.4889\" rx=\"10\" transform=\"rotate(45 93.6543 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> </svg>; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const A_100 = styled.div\\` display: flex; flex-direction: column; border-radius: 12px; background: #fff; font-size: 14px; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); overflow: hidden; border-radius: 6px;\\`;const Banner = styled.div\\` position: relative; display: flex; flex-direction: column; padding: 1.5rem 2rem; gap: 0.5rem; overflow: hidden; background: #dd3345; color: white; font-size: 22px; .left-pattern { position: absolute; left: 0; top: 0; width: 30%; transform: translate(-10%, -10%) scaleX(-1); pointer-events: none; } .right-pattern { position: absolute; right: 0; top: 0; width: 30%; transform: translate(10%, -10%); pointer-events: none; } @media only screen and (max-width: 480px) { padding: 1.125rem; }\\`;const HeaderIcons = styled.div\\` display: flex; align-items: center; justify-content: space-between; svg { width: 14px; cursor: pointer; transition: all 300ms ease-in-out; } .close-icon { margin-left: auto; &:hover { rotate: 90deg; } } div { cursor: pointer; display: flex; }\\`;const Content = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem 2rem; gap: 1.5rem; @media only screen and (max-width: 480px) { padding: 1.125rem; }\\`;const A_101 = styled.div\\` font-weight: 500; margin-bottom: 0.5rem;\\`;const Limit = styled.div\\` color: #7b7b7b; text-align: right;\\`;const InfoCard = styled.div\\` display: flex; align-items: center; gap: 1rem; padding: 0.75rem 1rem; width: 100%; border-radius: 6px; border: 1px solid #ebebeb; background: #f6f5f3;\\`;const A_102 = styled.div\\` display: grid; gap: 1.5rem; grid-template-columns: repeat(2, 1fr); button { font-weight: 500; } .cancel { border: none; background: none; color: #dd3345; }\\`; const props = props; const MAX_REASON_LENGTH = 250; const SOCIAL_CONTRACT_ID = \"social.near\"; const accountId = context.accountId || \"\"; const [reason, setReason] = useState(\"\"); const [reasonErr, setReasonErr] = useState(\"\"); const { onClose, flagAddress, potId, setSuccessFlag } = props; const onCancel = () => { onClose(); setReason(\"\"); }; const fetchSocialProfile = () => { return Near.asyncView(SOCIAL_CONTRACT_ID, \"get\", { keys: [\\`\\${accountId}/profile/**\\`] }); }; const handleSuccess = () => { const flsgSuccess = setInterval(() => { fetchSocialProfile().then(profileData => { const profile = profileData[accountId].profile; const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (potFlaggedAcc[flagAddress]) { setSuccessFlag({ account: flagAddress, reason }); onCancel(); clearInterval(flsgSuccess); } }); }, 1000); setTimeout(() => { onCancel(); clearInterval(flsgSuccess); }, 60000); }; const handleFlag = () => { fetchSocialProfile().then(profileData => { const profile = profileData[accountId].profile; const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; const socialArgs = { data: { [accountId]: { profile: { pLBlacklistedAccounts: JSON.stringify({ ...pLBlacklistedAccounts, [potId]: { ...potFlaggedAcc, [flagAddress]: reason } }) } } } }; const socialTransaction = { contractName: SOCIAL_CONTRACT_ID, methodName: \"set\", args: socialArgs }; Near.asyncView(SOCIAL_CONTRACT_ID, \"get_account\", { account_id: accountId }).then(account => { let depositFloat = JSON.stringify(socialArgs).length * 0.00015; if (!account) { depositFloat += 0.1; } socialTransaction.deposit = Big(depositFloat).mul(Big(10).pow(24)); Near.call(socialTransaction); handleSuccess(); }); }); }; return <ModalOverlay onOverlayClick={onCancel}> <A_100> <div> <Banner> <BannerBg className=\"left-pattern\" /> <BannerBg className=\"right-pattern\" /> <HeaderIcons> <svg onClick={() => onCancel()} className=\"close-icon\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#FCCFCF\" /> </svg> </HeaderIcons> Flag {flagAddress} </Banner> </div> <Content> <div> <TextArea {...{ label: \"Reason\", inputRows: 4, inputStyle: { background: \"#FAFAFA\" }, placeholder: \\`Type description\\`, value: reason, onChange: reason => setReason(reason), validate: () => { if (reason.length > MAX_REASON_LENGTH) { setReasonErr(\\`Reason must be less than \\${MAX_REASON_LENGTH} characters\\`); return; } setReasonErr(\"\"); }, error: reasonErr }} /> <Limit> {reason.length}/{MAX_REASON_LENGTH} </Limit> </div> <InfoCard> <div> <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9 5H11V7H9V5ZM9 9H11V15H9V9ZM10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18Z\" fill=\"#7B7B7B\" /> </svg> </div> <div>Flagging this account will remove their donations when calculating payouts for this pot</div> </InfoCard> <A_102> <button className=\"cancel\" onClick={onCancel}> Cancel </button> <Button {...{ type: \"primary\", text: \"Confirm\", disabled: !reason || !!reasonErr, onClick: handleFlag }} /> </A_102> </Content> </A_100> </ModalOverlay>; `, DonationsTable: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_99 = styled.div\\` padding: 16px 32px 40px; svg { display: block; cursor: pointer; width: 14px; transition: all 300ms ease-in-out; margin: 5px 0; margin-left: auto; &:hover { rotate: 90deg; } } .title { margin-bottom: 1rem; font-size: 16px; color: #7b7b7b; font-weight: 600; span { font-weight: 600; color: #292929; } } .reason { font-size: 14px; color: #7b7b7b; }\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const FlagSuccessModal = (__props__) => { const { onClose, successFlag } = __props__; return <ModalOverlay onOverlayClick={onClose}> <A_99> <svg viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" onClick={onClose}> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#7B7B7B\" /> </svg> <div className=\"title\"> <span> {successFlag.address} </span> has been flagged </div> <div className=\"reason\">{successFlag.reason}</div> </A_99> </ModalOverlay>;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const A_129 = (__props__) => <svg {...__props__} style={{ rotate: !__props__.active ? \"0deg\" : \"180deg\"}} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 6L1.0575 7.0575L5.25 2.8725V12H6.75V2.8725L10.935 7.065L12 6L6 0L0 6Z\" fill=\"#7B7B7B\" /> </svg>; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_103 = (donation) => { const lastDonationAmount = Big(donation.total_amount - (donation.referrer_fee || 0) - (donation.protocol_fee || 0)).div(Big(1e24)); return parseFloat(lastDonationAmount);}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const FlagBtn = ({ isFlagged, isProject, address, setUpdateFlaggedAddresses, updateFlaggedAddresses, setFlagAddress}) => { const accountId = context.accountId; const { potId } = props.alem.useParams(); const handleFlag = (e, address, isFlagged) => { const SOCIAL_CONTRACT_ID = \"social.near\"; e.preventDefault(); if (isFlagged) { Near.asyncView(SOCIAL_CONTRACT_ID, \"get\", { keys: [\\`\\${accountId}/profile/**\\`] }).then((profileData) => { const profile = profileData[accountId].profile; const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; delete potFlaggedAcc[address]; const socialArgs = { data: { [accountId]: { profile: { pLBlacklistedAccounts: JSON.stringify({ ...pLBlacklistedAccounts, [potId]: { ...potFlaggedAcc } }) } } } }; const depositFloat = JSON.stringify(socialArgs).length * 0.00015; const socialTransaction = { contractName: SOCIAL_CONTRACT_ID, methodName: \"set\", args: socialArgs, deposit: Big(depositFloat).mul(Big(10).pow(24)) }; Near.call(socialTransaction); setTimeout(() => { setUpdateFlaggedAddresses(!updateFlaggedAddresses); }, 3000); }); } else { setFlagAddress(address); } }; return isFlagged && accountId === isFlagged.flaggedBy ? <A_109 className=\"flag\" onClick={(e) => handleFlag(e, address, isFlagged)}> <svg width=\"16\" height=\"18\" viewBox=\"0 0 16 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7.86 2.5L8.26 4.5H13.5V10.5H10.14L9.74 8.5H2.5V2.5H7.86ZM9.5 0.5H0.5V17.5H2.5V10.5H8.1L8.5 12.5H15.5V2.5H9.9L9.5 0.5Z\" fill=\"#7B7B7B\" /> </svg> <div>Unflag {isProject ? \"project\" : \"donor\"}</div> </A_109> : <A_109 className=\"flag\" onClick={(e) => handleFlag(e, address, isFlagged)}> <svg width=\"16\" height=\"18\" viewBox=\"0 0 16 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7.86 2.5L8.26 4.5H13.5V10.5H10.14L9.74 8.5H2.5V2.5H7.86ZM9.5 0.5H0.5V17.5H2.5V10.5H8.1L8.5 12.5H15.5V2.5H9.9L9.5 0.5Z\" fill=\"#7B7B7B\" /> </svg> <div>Flag {isProject ? \"project\" : \"donor\"}</div> </A_109>;}; const A_104 = styled.div\\` display: flex; flex-direction: column; width: 100%; margin-top: 24px; padding-bottom: 1rem; border-radius: 6px; border: 1px solid #7b7b7b; align-items: center; gap: 2rem; .transcation { display: flex; flex-direction: column; width: 100%; font-size: 14px; } .header { display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 1rem; gap: 2rem; color: #292929; border-bottom: 1px solid rgba(199, 199, 199, 0.5); div { width: 100px; display: flex; justify-content: center; align-items: center; font-weight: 600; &.sort { cursor: pointer; gap: 8px; svg { transition: rotate 300ms; } } } .price { width: 70px; margin-right: 5rem; } } .address { /* width: 143px !important; */ flex: 1; justify-content: flex-start !important; } @media only screen and (max-width: 992px) { .header .price { margin-right: 0; } } @media only screen and (max-width: 768px) { .header { display: none; } }\\`;const A_105 = styled.div\\` display: flex; align-items: center; gap: 1rem; width: 100%; font-size: 14px; background: #f6f5f3; padding: 0.5rem 1rem; @media only screen and (max-width: 780px) { gap: 0.5rem; }\\`;const A_106 = styled.input\\` background: none; width: 100%; outline: none; border: none; &:focus { outline: none; border: none; }\\`;const A_107 = styled.div\\` display: flex; width: 24px; height: 24px; align-items: center; justify-content: center;\\`;const A_108 = styled.div\\` display: flex; width: 100%; justify-content: space-between; gap: 2rem; padding: 1rem; border-top: 1px solid rgb(199 199 199 / 50%); > div { width: 100px; display: flex; justify-content: center; align-items: center; } .price { display: flex; gap: 1rem; align-items: center; font-weight: 600; width: 74px; margin-right: 5rem; justify-content: flex-start; span { display: none; } img { width: 1.125rem; } } .address { position: relative; color: #292929; display: flex; align-items: center; justify-content: flex-start; border-radius: 2px; transition: all 200ms; font-weight: 600; .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 1rem; } :hover { text-decoration: none; .flag { opacity: 1; pointer-events: all; } } } .project-mobile-view { position: relative; width: fit-content; color: #7b7b7b; border-radius: 14px; padding: 4px 6px; border: 1px solid var(--Neutral-200, #dbdbdb); background: var(--Neutral-100, #ebebeb); cursor: pointer; display: none; align-items: center; gap: 0.5rem; margin-left: 4px; .profile-image { width: 1.125rem; height: 1.125rem; display: flex !important; } .flag { left: -50%; .tip-icon { padding-left: 50%; } } :hover { text-decoration: none; .flag { opacity: 1; pointer-events: all; } } @media only screen and (max-width: 768px) { display: flex; } } .date span { display: none; } @media only screen and (max-width: 992px) { .price { margin-right: 0; } } @media only screen and (max-width: 768px) { flex-wrap: wrap; .project { display: none !important; } .price { min-width: 120px; gap: 8px; width: fit-content; justify-content: flex-start; span { display: inline-block; } } .date { width: 100%; justify-content: start; gap: 4px; span { display: inline; } } .address .profile-image { margin-right: 0.5rem; } }\\`;const A_109 = styled.div\\` display: flex; align-content: center; gap: 12px; margin-left: auto; opacity: 0; pointer-events: none; font-weight: 500; transition: 300ms ease-in-out; @media only screen and (max-width: 992px) { opacity: 1; div { display: none; } } @media only screen and (max-width: 768px) { margin-left: 0.5rem; }\\`;const FlagTooltipWrapper = styled.div\\` position: absolute; display: flex; flex-direction: column; opacity: 0; pointer-events: none; transition: all 300ms ease 0s; top: 100%; background: white; z-index: 1; box-shadow: 0px 0px 1px 0px rgba(41, 41, 41, 0.74), 0px 3px 3px 0px rgba(123, 123, 123, 0.12), 0px 6px 6px 0px rgba(123, 123, 123, 0.12); border-radius: 4px; padding: 1rem; max-width: 550px; width: max-content; margin-top: 8px; cursor: default; .content { display: flex; gap: 1rem; .content-info { display: flex; flex-direction: column; gap: 0.5rem; } .profile-image { width: 1.5rem; height: 1.5rem; margin-right: 0; } .title { display: flex; align-items: center; gap: 8px; } .role { font-weight: 600; color: #7b7b7b; } .dot { width: 5px; height: 5px; background: #7b7b7b; border-radius: 50%; } .admin { font-weight: 600; display: flex; gap: 4px; } .text { color: #7b7b7b; } .flaged { color: #ed464f; font-weight: 600; &:hover { text-decoration: none; } } } .tip-icon { display: flex; z-index: 1; position: absolute; top: 0; height: 8px; transform: translateY(-100%); width: 100%; justify-content: flex-start; left: 0; padding-left: 2.5rem; svg { stroke: rgb(41 41 41 / 21%); } } @media only screen and (max-width: 768px) { width: 300px; padding: 0.5rem; font-size: 12px; .content .profile-image { display: none !important; } }\\`; const props = props; const accountId = context.accountId; const { filteredDonations, filter, handleSearch, sortDonation, currentFilter, potDetail } = props; const { potId } = props.alem.useParams(); const { admins, owner, chef, all_paid_out } = potDetail; const nearLogo = \"https://ipfs.near.social/ipfs/bafkreicdcpxua47eddhzjplmrs23mdjt63czowfsa2jnw4krkt532pa2ha\"; const [currentPage, setCurrentPage] = useState(1); const [flagAddress, setFlagAddress] = useState(null); const [successFlag, setSuccessFlag] = useState(null); const [updateFlaggedAddresses, setUpdateFlaggedAddresses] = useState(false); const [flaggedAddresses, setFlaggedAddresses] = useState([]); const perPage = 30; useEffect(() => { setCurrentPage(1); }, [filter]); useEffect(() => { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => setFlaggedAddresses(data)).catch((err) => console.log(\"error getting the flagged accounts \", err)); }, [successFlag, updateFlaggedAddresses]); const potAdmins = [owner, chef, ...admins]; const hasAuthority = potAdmins.includes(accountId) && !all_paid_out; const checkIfIsFlagged = (address) => flaggedAddresses.find((obj) => obj.potFlaggedAcc[address]); const FlagTooltip = ({ flag, href, address }) => <FlagTooltipWrapper className=\"flag\" onClick={(e) => e.preventDefault()}> <div className=\"tip-icon\"> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 5.24537e-07L-2.54292e-07 8L12 8L6 5.24537e-07Z\" fill=\"white\" /> </svg> </div> <div className=\"content\"> <ProfileImage style={{}} accountId={flag.flaggedBy} /> <div className=\"content-info\"> <div className=\"title\"> <div className=\"role\">{flag.role}</div> <div className=\"dot\" /> <div className=\"admin\"> {_address(flag.flaggedBy)} has flagged <a href={href} className=\"flaged\" target=\"_blank\" onClick={(e) => e.stopPropagation()}> {_address(address)} </a> </div> </div> <div className=\"text\">{flag.potFlaggedAcc[address]}</div> </div> </div> </FlagTooltipWrapper>; const AddressItem = ({ href, address, isFlagged, isProject, className }) => <a href={href} className={className} target=\"_blank\" onClick={(e) => { isFlagged ? e.preventDefault() : null; }}> <ProfileImage style={{}} accountId={address} /> <div style={{ color: isFlagged ? \"#ed464f\" : \"#292929\", fontWeight: \"600\" }}> {_address(address)} </div> {isFlagged && <FlagTooltip flag={isFlagged} href={href} address={address} />} {hasAuthority && <FlagBtn isProject={isProject} address={address} isFlagged={isFlagged} setUpdateFlaggedAddresses={setUpdateFlaggedAddresses} updateFlaggedAddresse={updateFlaggedAddresses} setFlagAddress={setFlagAddress} />} </a>; return <A_104> <div className=\"transcation\"> <div className=\"header\"> <div className=\"address\" onClick={() => { setSuccessFlag({ address: \"re.near\", reason: \"test tEST Tetset\" }); }}> Donor </div> <div className=\"address\">Project</div> <div className=\"sort price\" onClick={() => sortDonation(\"price\")}> Amount {currentFilter === \"price\" && <A_129 active={filter.price} />} </div> <div className=\"sort\" onClick={() => sortDonation(\"date\")}> Date {currentFilter === \"date\" && <A_129 active={!filter.date} />} </div> </div> <A_105> <A_107> <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.7549 14.2549H14.9649L14.6849 13.9849C15.6649 12.8449 16.2549 11.3649 16.2549 9.75488C16.2549 6.16488 13.3449 3.25488 9.75488 3.25488C6.16488 3.25488 3.25488 6.16488 3.25488 9.75488C3.25488 13.3449 6.16488 16.2549 9.75488 16.2549C11.3649 16.2549 12.8449 15.6649 13.9849 14.6849L14.2549 14.9649V15.7549L19.2549 20.7449L20.7449 19.2549L15.7549 14.2549ZM9.75488 14.2549C7.26488 14.2549 5.25488 12.2449 5.25488 9.75488C5.25488 7.26488 7.26488 5.25488 9.75488 5.25488C12.2449 5.25488 14.2549 7.26488 14.2549 9.75488C14.2549 12.2449 12.2449 14.2549 9.75488 14.2549Z\" fill=\"#C7C7C7\" /> </svg> </A_107> <A_106 placeholder=\"Search donations\" onChange={handleSearch} /> </A_105> {filteredDonations.length > 0 ? filteredDonations.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation) => { const { donor_id, recipient_id, donated_at_ms, donated_at, project_id } = donation; const projectId = recipient_id || project_id; const isDonorFlagged = checkIfIsFlagged(donor_id); const isProjectFlagged = checkIfIsFlagged(projectId); const projectHref = hrefWithParams(\\`?tab=project&projectId=\\${projectId}\\`); const profileHref = hrefWithParams(\\`?tab=profile&accountId=\\${donor_id}\\`); return <A_108 key={donated_at_ms || donated_at_ms}> {} <AddressItem address={donor_id} isFlagged={isDonorFlagged} href={profileHref} isProject={false} className=\"address\" /> {} <AddressItem address={projectId} isFlagged={isProjectFlagged} href={projectHref} isProject={true} className=\"address project\" /> <div className=\"price\"> <span>Donated</span> <img src={nearLogo} alt=\"NEAR\" /> {A_103(donation).toFixed(2)} </div> <div className=\"date\"> {getTimePassed(donated_at_ms || donated_at)} ago <span> to </span> <AddressItem address={projectId} isFlagged={isProjectFlagged} href={projectHref} isProject={true} className=\"project-mobile-view\" /> </div> </A_108>; }) : <A_108>No donations</A_108>} </div> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: filteredDonations, currentPage, perPage: perPage }, ...props } }} /> {flagAddress != null && <Widget loading=\" \" code={props.alem.componentsCode.FlagModal} props={{ ...{ ...{ flagAddress: flagAddress, setSuccessFlag, onClose: () => setFlagAddress(null) }, ...props } }} />} {successFlag != null && <FlagSuccessModal {...{ successFlag: successFlag, onClose: () => setSuccessFlag(null) }} />} </A_104>; `, Donations: ` const A_129 = (__props__) => <svg {...__props__} style={{ rotate: !__props__.active ? \"0deg\" : \"180deg\"}} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 6L1.0575 7.0575L5.25 2.8725V12H6.75V2.8725L10.935 7.065L12 6L6 0L0 6Z\" fill=\"#7B7B7B\" /> </svg>; const A_110 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; width: 100%; @media screen and (min-width: 375px) and (max-width: 768px) { width: 99%; } @media screen and (max-width: 390px) { width: 98%; }\\`;const A_111 = styled.div\\` display: flex; flex-direction: row; gap: 1.5rem; align-items: center; @media screen and (max-width: 768px) { padding-right: 10px; }\\`;const A_112 = styled.div\\` font-size: 18px; font-weight: 600;\\`;const DonationsCount = styled.div\\` font-size: 16px;\\`;const A_113 = styled.div\\` display: flex; flex-direction: column; align-items: center; border: 0.5px rgba(41, 41, 41, 0.5) solid; box-shadow: 0px 4px 12px -4px rgba(82, 82, 82, 0.2); border-radius: 2px; width: 100%; overflow-x: auto; flex-wrap: nowrap;\\`;const Sort = styled.div\\` display: none; justify-content: space-between; width: 100%; margin-top: 1.5rem; div { display: flex; align-items: center; font-weight: 500; cursor: pointer; gap: 8px; color: #7b7b7b; &.active { color: #292929; } svg { transition: rotate 300ms; } } @media screen and (max-width: 768px) { display: flex; }\\`; const props = props; const { allDonations, potDetail } = props; State.init({ filteredDonations: [], currentFilter: \"date\", filter: { date: false, price: false } }); const { filteredDonations, currentFilter, filter } = state; useEffect(() => { if (allDonations && filteredDonations.length === 0) { const sortedDonations = [...allDonations].reverse(); State.update({ filteredDonations: sortedDonations }); } }, [allDonations]); if (!allDonations) return <div className=\"spinner-border text-secondary\" role=\"status\" />; const searchDonations = (searchTerm) => { const filteredDonations = allDonations.filter((donation) => { const { donor_id, project_id } = donation; const searchFields = [donor_id, project_id]; return searchFields.some((field) => field.toLowerCase().includes(searchTerm.toLowerCase())); }); return filteredDonations; }; const sortDonation = (type) => { const sort = !filter[type]; State.update({ currentFilter: type, filter: { ...filter, [type]: sort } }); if (type === \"price\") { const sortedDonations = filteredDonations.sort((a, b) => sort ? b.total_amount - a.total_amount : a.total_amount - b.total_amount); State.update({ filteredDonations: sortedDonations }); } else if (type === \"date\") { const sortedDonations = filteredDonations.sort((a, b) => { return sort ? b.donated_at - a.donated_at : a.donated_at - b.donated_at; }); State.update({ filteredDonations: sortedDonations }); } }; const handleSearch = ({ target: { value } }) => { const filteredDonations = searchDonations(value); State.update({ filteredDonations }); }; return <A_110> <A_111> <A_112>All donations</A_112> <DonationsCount>{allDonations.length}</DonationsCount> </A_111> <Sort> <div className={\\`\\${currentFilter === \"date\" ? \"active\" : \"\"}\\`} onClick={() => sortDonation(\"date\")}> Sort Date {currentFilter === \"date\" && <A_129 active={!filter.date} />} </div> <div onClick={() => sortDonation(\"price\")} className={\\`\\${currentFilter === \"price\" ? \"active\" : \"\"}\\`}> Sort Amount {currentFilter === \"price\" && <A_129 active={filter.price} />} </div> </Sort> <Widget loading=\" \" code={props.alem.componentsCode.DonationsTable} props={{ ...{ ...{ filteredDonations, filter, handleSearch, sortDonation, currentFilter, potDetail }, ...props } }} /> </A_110>; `, ApplicationReviewModal: ` const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_114 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; background: white; padding: 24px 24px 12px 24px; border-top-left-radius: 6px; border-top-right-radius: 6px; font-weight: 500;\\`;const A_115 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 24px; border-top: 1px #f0f0f0 solid; background: #fafafa; gap: 8px;\\`;const A_116 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-end; background: #fafafa; padding: 12px 24px 24px 24px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; gap: 24px; width: 100%;\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const {projectId: projectId, onClose: onClose, newStatus: newStatus} = props; State.init({ reviewMessage: \"\", reviewMessageError: \"\" }); const { reviewMessage, reviewMessageError } = state; const { potId } = props.alem.useParams(); const { ONE_TGAS, SUPPORTED_FTS: { NEAR } } = constants; const MAX_APPLICATION_MESSAGE_LENGTH = 1000; const handleCancel = () => { State.update({ reviewMessage: \"\", reviewMessageError: \"\" }); onClose(); }; const handleSubmit = () => { const args = { project_id: projectId, status: newStatus, notes: reviewMessage }; const transactions = [{ contractName: potId, methodName: \"chef_set_application_status\", deposit: NEAR.toIndivisible(0.01), args, gas: ONE_TGAS.mul(100) }]; Near.call(transactions); }; return <ModalOverlay> <A_114> {newStatus === \"Approved\" ? \"Approve \" : newStatus === \"Rejected\" ? \"Reject \" : \"\"} application from {projectId} </A_114> <A_115> <div>Leave a note *</div> <TextArea {...{ noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Type notes here\", value: reviewMessage, onChange: reviewMessage => State.update({ reviewMessage }), validate: () => { if (reviewMessage.length > MAX_APPLICATION_MESSAGE_LENGTH) { State.update({ reviewMessageError: \\`Application message must be less than \\${MAX_APPLICATION_MESSAGE_LENGTH} characters\\` }); return; } State.update({ reviewMessageError: \"\" }); }, error: reviewMessageError }} /> </A_115> <A_116> <Button {...{ type: \"tertiary\", text: \"Cancel\", onClick: handleCancel }} /> <Button {...{ type: \"primary\", text: \"Submit\", disabled: !reviewMessage || !!reviewMessageError, onClick: handleSubmit }} /> </A_116> </ModalOverlay>; `, Applications: ` function daysAgo(timestamp) { const now = Date.now(); const differenceInTime = now - timestamp; const differenceInDays = Math.floor(differenceInTime / (1000 * 3600 * 24)); return differenceInDays === 0 ? \"< 1 day ago\" : \\`\\${differenceInDays} \\${differenceInDays === 1 ? \"day\" : \"days\"} ago\\`;} const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const APPLICATIONS_FILTERS_TAGS = { Pending: { label: \"Pending\", borderColor: \"#C7C7C7\", color: \"#292929\", background: \"#F6F5F3\", icon: <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M8 0.5C3.86 0.5 0.5 3.86 0.5 8C0.5 12.14 3.86 15.5 8 15.5C12.14 15.5 15.5 12.14 15.5 8C15.5 3.86 12.14 0.5 8 0.5ZM8 14C4.685 14 2 11.315 2 8C2 4.685 4.685 2 8 2C11.315 2 14 4.685 14 8C14 11.315 11.315 14 8 14Z\" fill=\"#7B7B7B\" /> <path d=\"M4.25 9.125C4.87132 9.125 5.375 8.62132 5.375 8C5.375 7.37868 4.87132 6.875 4.25 6.875C3.62868 6.875 3.125 7.37868 3.125 8C3.125 8.62132 3.62868 9.125 4.25 9.125Z\" fill=\"#7B7B7B\" /> <path d=\"M8 9.125C8.62132 9.125 9.125 8.62132 9.125 8C9.125 7.37868 8.62132 6.875 8 6.875C7.37868 6.875 6.875 7.37868 6.875 8C6.875 8.62132 7.37868 9.125 8 9.125Z\" fill=\"#7B7B7B\" /> <path d=\"M11.75 9.125C12.3713 9.125 12.875 8.62132 12.875 8C12.875 7.37868 12.3713 6.875 11.75 6.875C11.1287 6.875 10.625 7.37868 10.625 8C10.625 8.62132 11.1287 9.125 11.75 9.125Z\" fill=\"#7B7B7B\" /> </svg> }, Approved: { label: \"Approved\", color: \"#192C07\", borderColor: \"#9ADD33\", background: \"#F7FDE8\", icon: <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M8 0.5C3.86 0.5 0.5 3.86 0.5 8C0.5 12.14 3.86 15.5 8 15.5C12.14 15.5 15.5 12.14 15.5 8C15.5 3.86 12.14 0.5 8 0.5ZM8 14C4.6925 14 2 11.3075 2 8C2 4.6925 4.6925 2 8 2C11.3075 2 14 4.6925 14 8C14 11.3075 11.3075 14 8 14ZM11.4425 4.685L6.5 9.6275L4.5575 7.6925L3.5 8.75L6.5 11.75L12.5 5.75L11.4425 4.685Z\" fill=\"#629D13\" /> </svg> }, Rejected: { label: \"Rejected\", borderColor: \"#FAA7A8\", color: \"#490813\", background: \"#FEF3F2\", icon: <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M8 0.5C3.86 0.5 0.5 3.86 0.5 8C0.5 12.14 3.86 15.5 8 15.5C12.14 15.5 15.5 12.14 15.5 8C15.5 3.86 12.14 0.5 8 0.5ZM2 8C2 4.685 4.685 2 8 2C9.3875 2 10.6625 2.4725 11.675 3.2675L3.2675 11.675C2.4725 10.6625 2 9.3875 2 8ZM8 14C6.6125 14 5.3375 13.5275 4.325 12.7325L12.7325 4.325C13.5275 5.3375 14 6.6125 14 8C14 11.315 11.315 14 8 14Z\" fill=\"#ED464F\" /> </svg> }}; const A_117 = styled.div\\` display: flex; gap: 2rem; .dropdown { display: none; } @media only screen and (max-width: 768px) { flex-direction: column; gap: 1.5rem; .dropdown { display: flex; } }\\`;const A_118 = styled.div\\` display: grid; width: 286px; border-radius: 6px; padding: 8px 0; border: 1px solid var(--Neutral-500, #7b7b7b); height: fit-content; .item { display: flex; align-items: center; gap: 12px; padding: 0.5rem 1rem; font-size: 14px; cursor: pointer; svg { opacity: 0; transition: all 300ms ease; } &.active { svg { opacity: 1; } } &:hover { svg { opacity: 1; } } } .count { color: #7b7b7b; margin-left: auto; } @media only screen and (max-width: 768px) { display: none; }\\`;const ApplicationsWrapper = styled.div\\` border-radius: 6px; border: 1px solid #7b7b7b; overflow: hidden; display: flex; flex-direction: column; max-width: 711px; width: 100%;\\`;const A_119 = styled.div\\` display: flex; position: relative; svg { position: absolute; left: 1.5rem; top: 50%; transform: translateY(-50%); } input { font-size: 14px; background: #f6f5f3; width: 100%; height: 100%; padding: 8px 24px 8px 60px; border: none; outline: none; } @media only screen and (max-width: 768px) { svg { left: 1rem; } input { padding: 8px 24px 8px 54px; } }\\`;const ApplicationRow = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem; font-size: 14px; position: relative; border-top: 1px solid #c7c7c7; .header { display: flex; gap: 1rem; justify-content: space-between; position: relative; align-items: center; } .header-info { display: flex; gap: 8px; align-items: center; cursor: auto; } .profile-image { margin-right: 8px; width: 24px; height: 24px; } .name { color: #292929; font-weight: 600; } .address { color: #7b7b7b; font-weight: 600; cursor: pointer; transition: all 300ms; position: relative; z-index: 2; &:hover { color: #292929; } } .content { display: flex; flex-direction: column; gap: 1rem; overflow: hidden; transition: all 300ms ease-in-out; max-height: 0; .message { padding-top: 1rem; } .notes { display: flex; flex-direction: column; gap: 8px; .title { color: #7b7b7b; } } button { width: fit-content; } } .arrow { rotate: 180deg; transition: all 300ms; } .toggle-check { position: absolute; top: 0; left: 0; width: 100%; height: 67px; z-index: 1; opacity: 0; cursor: pointer; } .toggle-check:checked + .header .arrow { rotate: 0deg; } .toggle-check:checked + .header + .content { max-height: 100%; } @media only screen and (max-width: 768px) { padding: 1rem; .header-info { flex-wrap: wrap; gap: 0px; } .name { margin: 0 8px; } .date { line-height: 1; width: 100%; margin-left: 2.5rem; } }\\`;const Dot = styled.div\\` width: 6px; height: 6px; background: #7b7b7b; border-radius: 50%; @media only screen and (max-width: 768px) { display: none; }\\`;const Status = styled.div\\` display: flex; padding: 6px 12px; gap: 8px; align-items: center; border-width: 1px; border-style: solid; border-radius: 4px; margin-left: auto; div { font-weight: 500; } svg { width: 1rem; } @media only screen and (max-width: 768px) { padding: 6px; div { display: none; } svg { width: 16px; } }\\`;const DropdownLabel = styled.div\\` display: flex; gap: 10px; align-items: center; .label { font-weight: 500; } .count { display: flex; align-items: center; justify-content: center; border-radius: 50%; background: #ebebeb; }\\`; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const { potDetail: potDetail } = props; const { potId } = props.alem.useParams(); State.init({ newStatus: \"\", projectId: \"\", searchTerm: \"\", allApplications: null, filteredApplications: [], filterVal: \"ALL\" }); const { newStatus, projectId, searchTerm, allApplications, filteredApplications, filterVal } = state; const applications = PotSDK.getApplications(potId); const getApplicationCount = (sortVal) => { if (!applications) return; return applications?.filter((application) => { if (sortVal === \"All\") return true; return application.status === sortVal; })?.length; }; if (applications && !allApplications) { applications.reverse(); State.update({ filteredApplications: applications, allApplications: applications }); } if (!allApplications) return <div className=\"spinner-border text-secondary\" role=\"status\" />; const { owner, admins, chef } = potDetail; const isChefOrGreater = context.accountId === chef || admins.includes(context.accountId || \"\") || context.accountId === owner; const handleApproveApplication = (projectId) => { State.update({ newStatus: \"Approved\", projectId }); }; const handleRejectApplication = (projectId) => { State.update({ newStatus: \"Rejected\", projectId }); }; const handleCloseModal = () => { State.update({ newStatus: \"\", projectId: \"\" }); }; const searchApplications = (searchTerm) => { const filteredApplications = allApplications?.filter((application) => { const { message, project_id, review_notes, status } = application; const searchFields = [message, project_id, review_notes, status]; return searchFields.some((field) => field.toLowerCase().includes(searchTerm.toLowerCase().trim())); }); return filteredApplications; }; const APPLICATIONS_FILTERS = { ALL: { label: \"All applications\", val: \"ALL\", count: getApplicationCount(\"All\") }, PENDING: { label: \"Pending applications\", val: \"PENDING\", count: getApplicationCount(\"Pending\") }, APPROVED: { label: \"Approved applications\", val: \"APPROVED\", count: getApplicationCount(\"Approved\") }, REJECTED: { label: \"Rejected applications\", val: \"REJECTED\", count: getApplicationCount(\"Rejected\") } }; const sortApplications = (key) => { if (key === \"ALL\") { return searchApplications(searchTerm); } const filtered = allApplications?.filter((application) => { return application.status === APPLICATIONS_FILTERS[key].label.split(\" \")[0]; }); return filtered; }; const handleSort = (key) => { const sorted = sortApplications(key); State.update({ filteredApplications: sorted, filterVal: key }); }; const DropdownVal = () => { const digit = APPLICATIONS_FILTERS[filterVal].count.toString().length; return <DropdownLabel> <div className=\"label\">{APPLICATIONS_FILTERS[filterVal].label}</div> <div className=\"count\" style={{ width: \\`\\${24 + (digit - 1) * 6}px\\`, height: \\`\\${24 + (digit - 1) * 6}px\\` }}> {APPLICATIONS_FILTERS[filterVal].count} </div> </DropdownLabel>; }; return <A_117> <div className=\"dropdown\"> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ ...{ sortVal: DropdownVal, showCount: true, sortList: Object.values(APPLICATIONS_FILTERS), FilterMenuCustomStyle: \\`left:0; right:auto;\\`, handleSortChange: ({ val }) => {handleSort(val);} }, ...props } }} /> </div> <A_118> {Object.keys(APPLICATIONS_FILTERS).map((key) => <div key={key} className={\\`item \\${filterVal === key ? \"active\" : \"\"}\\`} onClick={() => handleSort(key)}> <svg width=\"14\" height=\"12\" viewBox=\"0 0 14 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M4.59631 8.9057L1.46881 5.7782L0.403809 6.8357L4.59631 11.0282L13.5963 2.0282L12.5388 0.970703L4.59631 8.9057Z\" fill=\"#7B7B7B\" /> </svg> <div> {APPLICATIONS_FILTERS[key].label}</div> <div className=\"count\">{APPLICATIONS_FILTERS[key].count}</div> </div>)} </A_118> <ApplicationsWrapper> <A_119> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9.81641 8.69141H9.22391L9.01391 8.48891C9.74891 7.63391 10.1914 6.52391 10.1914 5.31641C10.1914 2.62391 8.00891 0.441406 5.31641 0.441406C2.62391 0.441406 0.441406 2.62391 0.441406 5.31641C0.441406 8.00891 2.62391 10.1914 5.31641 10.1914C6.52391 10.1914 7.63391 9.74891 8.48891 9.01391L8.69141 9.22391V9.81641L12.4414 13.5589L13.5589 12.4414L9.81641 8.69141ZM5.31641 8.69141C3.44891 8.69141 1.94141 7.18391 1.94141 5.31641C1.94141 3.44891 3.44891 1.94141 5.31641 1.94141C7.18391 1.94141 8.69141 3.44891 8.69141 5.31641C8.69141 7.18391 7.18391 8.69141 5.31641 8.69141Z\" fill=\"#7B7B7B\" /> </svg> <input type=\"text\" placeholder=\"Search applications\" className=\"search-input\" onChange={(e) => { const results = searchApplications(e.target.value); State.update({ searchTerm: e.target.value, filteredApplications: results }); }} /> </A_119> {filteredApplications.length ? filteredApplications.map(({ project_id, status, message, review_notes, submitted_at }) => { const { borderColor, color, icon, label, background } = APPLICATIONS_FILTERS_TAGS[status]; const profile = Social.getr(\\`\\${project_id}/profile\\`); return <ApplicationRow key={project_id}> <input type=\"checkbox\" className=\"toggle-check\" /> <div className=\"header\"> <div className=\"header-info\"> <ProfileImage style={{}} profile={profile} /> {profile?.name && <div className=\"name\">{_address(profile?.name, 10)}</div>} <OverlayTrigger placement=\"top\" overlay={<Tooltip>{project_id}</Tooltip>}> <a className=\"address\" href={hrefWithParams(\\`?tab=project&projectId=\\${project_id}\\`)} target=\"_blank\"> {_address(project_id, 10)} </a> </OverlayTrigger> <Dot /> <div className=\"date\">{daysAgo(submitted_at)}</div> </div> <Status style={{ borderColor, color, background }}> <div>{label}</div> {icon} </Status> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" className=\"arrow\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294922L0 6.29492L1.41 7.70492L6 3.12492L10.59 7.70492L12 6.29492L6 0.294922Z\" fill=\"#7B7B7B\" /> </svg> </div> <div className=\"content\"> <div className=\"message\">{message}</div> {review_notes && <div className=\"notes\"> <div className=\"title\">Admin notes:</div> <div>{review_notes}</div> </div>} {isChefOrGreater && <> {status !== \"Approved\" && <Button {...{ type: \"secondary\", text: \"Approve\", onClick: () => handleApproveApplication(project_id) }} />} {status !== \"Rejected\" && <Button {...{ type: \"primary\", text: \"Reject\", onClick: () => handleRejectApplication(project_id) }} />} </>} </div> </ApplicationRow>; }) : <div style={{ padding: \"1rem\" }}>No applications to display</div>} </ApplicationsWrapper> {projectId && <Widget loading=\" \" code={props.alem.componentsCode.ApplicationReviewModal} props={{ ...{ projectId: projectId, newStatus: newStatus, onClose: handleCloseModal, ...props } }} />} </A_117>; `, Projects: ` function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const getTeamMembersFromSocialProfileData = profileData => { if (!profileData) return []; const team = profileData.plTeam ? JSON.parse(profileData.plTeam) : profileData.team ? Object.entries(profileData.team).filter(([_, v]) => v !== null).map(([k, _]) => k) : []; return team;}; const getTagsFromSocialProfileData = profileData => { if (!profileData) return []; const DEPRECATED_CATEGORY_MAPPINGS = { \"social-impact\": \"Social Impact\", \"non-profit\": \"NonProfit\", climate: \"Climate\", \"public-good\": \"Public Good\", \"de-sci\": \"DeSci\", \"open-source\": \"Open Source\", community: \"Community\", education: \"Education\" }; const tags = profileData.plCategories ? JSON.parse(profileData.plCategories) : profileData.category ? [profileData.category.text ?? DEPRECATED_CATEGORY_MAPPINGS[profileData.category] ?? \"\"] : []; return tags;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const calculatePayouts = (allPotDonations, totalMatchingPool, blacklistedAccounts) => { const { NADABOT_CONTRACT_ID } = constants; return new Promise((resolve, reject) => { console.log(\"Calculting payouts; ignoring blacklisted donors &/or projects: \", blacklistedAccounts.join(\", \")); console.log(\"totalMatchingPool: \", totalMatchingPool); const projectContributions = []; const allDonors = new Set(); for (const d of allPotDonations) { if (blacklistedAccounts.includes(d.donor_id) || blacklistedAccounts.includes(d.project_id)) { continue; } const amount = new Big(d.total_amount); const val = [d.project_id, d.donor_id, amount]; projectContributions.push(val); allDonors.add(d.donor_id); } const limit = 100; let curIndex = 0; let humanScores = {}; let promises = []; while (curIndex < allDonors.size) { promises.push(Near.asyncView(NADABOT_CONTRACT_ID, \"get_human_score_batch\", { account_ids: Array.from(allDonors).slice(curIndex, curIndex + limit) })); curIndex += limit; } Promise.all(promises).then(res => { for (const r of res) { humanScores = { ...humanScores, ...r }; } }).catch(e => { console.error(\"error fetching human scores. Continuing anyway: \", e); }).finally(() => { console.log(\"human scores: \", humanScores); const contributions = {}; for (const [proj, user, amount] of projectContributions) { if (!humanScores[user] || !humanScores[user].is_human) { console.log(\"skipping non-human: \", user); continue; } if (!contributions[proj]) { contributions[proj] = {}; } contributions[proj][user] = Big(contributions[proj][user] || 0).plus(amount); } console.log(\"contributions: \", contributions); const pairTotals = {}; for (const contribz of Object.values(contributions)) { for (const [k1, v1] of Object.entries(contribz)) { if (!pairTotals[k1]) { pairTotals[k1] = {}; } for (const [k2, v2] of Object.entries(contribz)) { if (!pairTotals[k1][k2]) { pairTotals[k1][k2] = Big(0); } pairTotals[k1][k2] = pairTotals[k1][k2].plus(v1.times(v2).sqrt()); } } } const threshold = Big(\"25000000000000000000000000\"); const totalPot = Big(totalMatchingPool); let bigtot = Big(0); const totals = []; for (const [proj, contribz] of Object.entries(contributions)) { let tot = Big(0); let _num = 0; let _sum = Big(0); for (const [k1, v1] of Object.entries(contribz)) { _num += 1; _sum = _sum.plus(v1); for (const [k2, v2] of Object.entries(contribz)) { if (k2 > k1 || Object.keys(contribz).length === 1) { const sqrt = v1.times(v2).sqrt(); tot = tot.plus(sqrt.div(pairTotals[k1][k2].div(threshold))); } } } bigtot = bigtot.plus(tot); totals.push({ id: proj, number_contributions: _num, contribution_amount_str: _sum.toFixed(0), matching_amount_str: tot.toFixed(0) }); } console.log(\"totals before: \", totals); if (bigtot.gte(totalPot)) { console.log(\"NORMALIZING\"); for (const t of totals) { t.matching_amount_str = Big(t.matching_amount_str).div(bigtot).times(totalPot).toFixed(0); } } let totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } let residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"first round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(\"0\"))) { for (let i = 0; i < totals.length; i++) { let proportion = Big(totals[i].matching_amount_str).div(totalAllocatedBeforeRounding); let additionalAllocation = proportion.times(residual); totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(additionalAllocation).toFixed(0); } console.log(\"CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION\"); totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"second round residual: \", residual.toFixed(0)); if (residual.abs().gt(Big(0))) { totals.sort((a, b) => Big(b.matching_amount_str).minus(Big(a.matching_amount_str))); if (residual.gt(Big(0))) { totals[0].matching_amount_str = Big(totals[0].matching_amount_str).plus(residual).toFixed(0); } else { for (let i = 0; i < totals.length; i++) { if (Big(totals[i].matching_amount_str).gt(residual.abs())) { totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(residual).toFixed(0); break; } } } totalAllocatedBeforeRounding = Big(0); for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); console.log(\"Residual after final adjustment: \", residual.toFixed(0)); } } const payouts = totals.reduce((acc, t) => { acc[t.id] = { totalAmount: t.contribution_amount_str, matchingAmount: t.matching_amount_str, donorCount: t.number_contributions }; return acc; }, {}); resolve(payouts); }); });}; const A_120 = styled.div\\` display: flex; flex-direction: column;\\`;const A_121 = styled.div\\` font-size: 18px; display: flex; align-items: center; gap: 1.5rem; width: fit-content; margin-bottom: 1.5rem; div:first-of-type { font-weight: 600; }\\`;const A_122 = styled.div\\` display: flex; position: relative; width: 100%; border-radius: 6px; border: 0.5px solid #292929; margin-bottom: 0.5rem; svg { position: absolute; left: 1rem; top: 50%; transform: translateY(-50%); } input { font-size: 14px; background: transparent; width: 100%; height: 100%; padding: 12px 16px 12px 3rem; border: none; outline: none; } @media only screen and (max-width: 768px) { svg { left: 1rem; } input { padding: 8px 24px 8px 54px; } }\\`; const props = props; const [searchTerm, setSearchTerm] = useState(\"\"); const [filteredProjects, setFilteredProjects] = useState([]); const [projects, setProjects] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); const [payouts, setPayouts] = useState(null); const { potId } = props.alem.useParams(); const { potDetail, allDonations } = props; if (!projects) { PotSDK.asyncGetApprovedApplications(potId).then((projects) => { setProjects(projects); setFilteredProjects(projects); }); } if (!projects) return <div className=\"spinner-border text-secondary\" role=\"status\" />; const { public_round_start_ms, public_round_end_ms, referral_fee_public_round_basis_points } = potDetail; const now = Date.now(); const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; if (!flaggedAddresses) { PotSDK.getFlaggedAccounts(potDetail, potId).then((data) => { const listOfFlagged = []; data.forEach((adminFlaggedAcc) => { const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); listOfFlagged.push(...addresses); }); setFlaggedAddresses(listOfFlagged); }).catch((err) => console.log(\"error getting the flagged accounts \", err)); } if (!payouts) { if (allDonations.length && flaggedAddresses) calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((payouts) => { setPayouts(payouts ?? []); }).catch((err) => { console.log(\"error while calculating payouts \", err); setPayouts([]); }); } const searchByWords = (searchTerm) => { if (projects.length) { searchTerm = searchTerm.toLowerCase().trim(); setSearchTerm(searchTerm); const updatedProjects = projects.filter((project) => { const profile = Social.getr(\\`\\${project.project_id}/profile\\`); const fields = [project.project_id, project.status, profile.description, profile.name, getTagsFromSocialProfileData(profile).join(\" \"), getTeamMembersFromSocialProfileData(profile).join(\" \")]; return fields.some((item) => (item || \"\").toLowerCase().includes(searchTerm.toLowerCase())); }); setFilteredProjects(updatedProjects); } }; return <A_120> <A_121> <div>Projects</div> <div>{filteredProjects?.length}</div> </A_121> <A_122> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9.81641 8.69141H9.22391L9.01391 8.48891C9.74891 7.63391 10.1914 6.52391 10.1914 5.31641C10.1914 2.62391 8.00891 0.441406 5.31641 0.441406C2.62391 0.441406 0.441406 2.62391 0.441406 5.31641C0.441406 8.00891 2.62391 10.1914 5.31641 10.1914C6.52391 10.1914 7.63391 9.74891 8.48891 9.01391L8.69141 9.22391V9.81641L12.4414 13.5589L13.5589 12.4414L9.81641 8.69141ZM5.31641 8.69141C3.44891 8.69141 1.94141 7.18391 1.94141 5.31641C1.94141 3.44891 3.44891 1.94141 5.31641 1.94141C7.18391 1.94141 8.69141 3.44891 8.69141 5.31641C8.69141 7.18391 7.18391 8.69141 5.31641 8.69141Z\" fill=\"#7B7B7B\" /> </svg> <input type=\"text\" placeholder=\"Search projects\" onChange={(e) => searchByWords(e.target.value)} className=\"search-input\" /> </A_122> <Widget loading=\" \" code={props.alem.componentsCode.ListSection} props={{ ...{ shouldShuffle: true, maxCols: 3, items: filteredProjects, responsive: [{ breakpoint: 1200, items: 2 }, { breakpoint: 870, items: 1 }], renderItem: (project) => {return <Widget loading=\" \" code={props.alem.componentsCode.A_197} props={{ ...{ ...{ potDetail, projects, projectId: project.project_id, allowDonate: publicRoundOpen && project.project_id !== context.accountId, potRferralFeeBasisPoints: referral_fee_public_round_basis_points, payoutDetails: payouts[project.project_id] || { donorCount: 0, matchingAmount: \"0\", totalAmount: \"0\" } }, ...props } }} />;}, ...props } }} /> </A_120>; `, Pot: ` const A_160 = ({ navOptions, nav}) => { const getSelectedNavOption = () => { const navOption = navOptions.find((option) => option.id == nav); return navOption ?? navOptions[0]; }; const NavOptionsContainer = styled.div\\` display: flex; justify-content: flex-start; gap: 2rem; width: 100%; border-bottom: 1px solid #c7c7c7; padding: 0 4rem; .nav-option { font-size: 14px; color: #7b7b7b; padding: 10px 16px; font-weight: 500; white-space: nowrap; border-bottom: 2px solid transparent; transition: 300ms ease; &.disabled { pointer-events: none; cursor: not-allowed; } &.selected { color: #292929; border-bottom-color: #292929; } :hover { border-bottom-color: #292929; text-decoration: none; } } @media screen and (max-width: 768px) { padding: 0px 1rem; overflow-x: scroll; .nav-option.selected { order: -1; } } \\`; return <NavOptionsContainer> {navOptions.map((option) => { const selected = option.id == getSelectedNavOption().id; return option.label ? <a className={\\`nav-option \\${selected && \"selected\"} \\${option.disabled && \"disabled\"}\\`} href={option.href}> {option.label} </a> : \"\"; })} </NavOptionsContainer>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const dateNow = Date.now(); const navOptions = (potId, potDetail) => [{ label: \"Projects\", id: \"projects\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Projects} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=projects\\`) }, { label: \"Applications\", id: \"applications\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Applications} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=applications\\`) }, { label: \"Donations\", id: \"donations\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Donations} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=donations\\`) }, { label: \"Sponsors\", id: \"sponsors\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Sponsors} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=sponsors\\`) }, { label: \"Payouts\", id: \"payouts\", disabled: dateNow < potDetail.public_round_start_ms, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Payouts} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=payouts\\`) }, { label: \"Settings\", id: \"settings\", disabled: false, source: (compProps) => <Widget loading=\" \" code={props.alem.componentsCode.Settings} props={{ ...{ ...compProps, ...props } }} />, href: hrefWithParams(\\`?tab=pot&potId=\\${potId}&nav=settings\\`) }]; const A_123 = styled.div\\`\\`;const BodyContainer = styled.div\\` margin-top: 52px; padding: 0 4rem; flex: 1; width: 100%; @media screen and (max-width: 768px) { padding: 0; }\\`; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const { potId, nav: _nav } = props.alem.useParams(); Big.PE = 100; const potDetail = PotSDK.getConfig(potId); const noPot = potDetail === undefined; const loading = potDetail === null; if (loading) return <div className=\"spinner-border text-secondary\" role=\"status\" />; if (noPot) return \"No pot found\"; const now = Date.now(); const applicationNotStarted = now < potDetail.application_start_ms; const applicationOpen = now >= potDetail.application_start_ms && now < potDetail.application_end_ms; const publicRoundOpen = now >= potDetail.public_round_start_ms && now < potDetail.public_round_end_ms; const publicRoundClosed = now >= potDetail.public_round_end_ms; const payoutsPending = publicRoundClosed && !potDetail.cooldown_end_ms; let nav = _nav; if (!nav) { applicationNotStarted ? nav = \"sponsors\" : applicationOpen ? nav = \"applications\" : publicRoundOpen ? nav = \"projects\" : !payoutsPending ? nav = \"donations\" : nav = \"payouts\"; } const allDonationsPaginated = useCache(() => { const limit = 480; const donationsCount = potDetail.public_donations_count; const paginations = [...Array(Math.ceil(donationsCount / limit)).keys()]; try { const allDonations = paginations.map((index) => PotSDK.asyncGetPublicRoundDonations(potId, { from_index: index * limit, limit: limit })); return Promise.all(allDonations); } catch (error) { console.error(\\`error getting public donations\\`, error); return Promise.all([]); } }, \"pot-public-donations\"); const allDonations = allDonationsPaginated ? allDonationsPaginated.flat() : null; const options = navOptions(potId || \"\", potDetail); const SelectedNavComponent = useMemo(() => { return options.find((option) => option.id === nav).source; }, []); return <A_123> <Widget loading=\" \" code={props.alem.componentsCode.HeaderStatus} props={{ ...{ potDetail: potDetail, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.A_48} props={{ ...{ potDetail: potDetail, allDonations: allDonations, ...props } }} /> <A_160 nav={nav} navOptions={options} /> <BodyContainer> {SelectedNavComponent && <SelectedNavComponent allDonations={allDonations} potDetail={potDetail} />} </BodyContainer> </A_123>; `, PotsHome: ` const Outer = styled.div\\` display: flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; @keyframes beacon { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.3); } } animation: beacon 1.5s infinite;\\`;const Inner = styled.div\\` width: 10px; height: 10px; border-radius: 50%;\\`; const Indicator = ({ animate, colorInner, colorOuter}) => { return <Outer style={{ backgroundColor: colorOuter, animationPlayState: animate ? \"running\" : \"paused\" }}> <Inner style={{ backgroundColor: colorInner }} /> </Outer>;}; const TagContainer = styled.div\\` display: flex; justify-content: center; align-items: center; text-align: center; padding: 6px 8px; border-radius: 4px;\\`;const TagText = styled.span\\` font-size: 14px;\\`; const Tag = __props__ => { const { backgroundColor, borderColor, textColor, text } = __props__; const textStyle = __props__.textStyle || {}; return <TagContainer style={{ backgroundColor: backgroundColor || \"#ffffff\", border: \\`1px solid \\${borderColor || \"#000000\"}\\`, boxShadow: \\`0px -0.699999988079071px 0px \\${borderColor} inset\\` }}> {__props__.preElements} <TagText style={{ color: textColor || \"#000000\", ...textStyle }}> {text} </TagText> </TagContainer>;}; const nearToUsd = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then(res => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\");const yoctosToUsd = amount => { return nearToUsd ? \"~\\$\" + formatWithCommas(new Big(amount).mul(nearToUsd).div(1e24).toFixed(2)) : \"0\";}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; function daysUntil(timestamp) { const now = Date.now(); const differenceInTime = timestamp - now; const differenceInDays = Math.ceil(differenceInTime / (1000 * 3600 * 24)); return \\`\\${differenceInDays} \\${differenceInDays === 1 ? \"day\" : \"days\"}\\`;} const tagsList = potConfig => { const { application_start_ms, application_end_ms, public_round_start_ms, public_round_end_ms, cooldown_end_ms, all_paid_out } = potConfig; const now = Date.now(); const applicationOpen = now >= application_start_ms && now < application_end_ms; const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; const cooldownPending = public_round_end_ms && now >= public_round_end_ms && !cooldown_end_ms; const cooldownOpen = now >= public_round_end_ms && now < cooldown_end_ms; const payoutsPending = cooldown_end_ms && now >= cooldown_end_ms && !all_paid_out; const payoutsCompleted = all_paid_out; const tags = [{ backgroundColor: \"#EFFEFA\", borderColor: \"#33DDCB\", textColor: \"#023131\", text: \"Sponsorship Open\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#CAFDF3\", colorInner: \"#33DDCB\", animate: true }, visibility: now < application_start_ms }, { backgroundColor: \"#EFFEFA\", borderColor: \"#33DDCB\", textColor: \"#023131\", text: daysUntil(application_end_ms) + \" left to apply\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#CAFDF3\", colorInner: \"#33DDCB\", animate: true }, visibility: applicationOpen }, { backgroundColor: \"#F7FDE8\", borderColor: \"#9ADD33\", textColor: \"#192C07\", text: daysUntil(public_round_end_ms) + \" left to donate\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#D7F5A1\", colorInner: \"#9ADD33\", animate: true }, visibility: publicRoundOpen }, { backgroundColor: \"#F5F3FF\", borderColor: \"#A68AFB\", textColor: \"#2E0F66\", text: \"Cooldown pending\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#EDE9FE\", colorInner: \"#A68AFB\", animate: true }, visibility: cooldownPending }, { backgroundColor: \"#F5F3FF\", borderColor: \"#A68AFB\", textColor: \"#2E0F66\", text: \"Challenge period\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#EDE9FE\", colorInner: \"#A68AFB\", animate: true }, visibility: cooldownOpen }, { backgroundColor: \"#F7FDE8\", borderColor: \"#9ADD33\", textColor: \"#192C07\", text: \"Payouts pending\", preElementsProps: { colorOuter: \"#D7F5A1\", colorInner: \"#9ADD33\", animate: true }, textStyle: { fontWeight: 500, marginLeft: \"8px\" }, visibility: payoutsPending }, { backgroundColor: \"#464646\", borderColor: \"#292929\", textColor: \"#FFF\", text: \"Payouts completed\", preElementsProps: { colorOuter: \"#656565\", colorInner: \"#A6A6A6\", animate: false }, textStyle: { fontWeight: 500, marginLeft: \"8px\" }, visibility: payoutsCompleted }]; return tags;}; const A_133 = styled.a\\` display: flex; flex-direction: column; min-width: 320px; min-height: 300px; border-radius: 8px; background: white; box-shadow: 0px -2px 0px 0px #464646 inset, 0px 0px 0px 1px #464646; padding-bottom: 5px; height: 100%; &:hover { text-decoration: none; cursor: pointer; }\\`;const CardSection = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 16px; padding: 32px; width: 100%; height: 100%;\\`;const A_134 = styled.div\\` color: #292929; font-size: 22px; font-weight: 600; line-height: 28px; word-wrap: break-word; > div { font-weight: inherit; display: flex; align-items: baseline; } .usd-amount { font-size: 14px; font-weight: 400; margin-left: 0.25rem; } .text { font-size: 14px; color: #7b7b7b; margin-left: 0.5rem; }\\`;const A_135 = styled.div\\` color: #525252; font-size: 16px; font-weight: 400; line-height: 28px; word-wrap: break-word; a { color: rgb(123, 123, 123); }\\`;const Subtitle = styled.span\\` color: #7b7b7b; font-size: 14px; font-weight: 600; line-height: 20px; word-wrap: break-word;\\`; const MAX_DESCRIPTION_LENGTH = 100;const MAX_TITLE_LENGTH = 36;const PotCard = ({ potId}) => { const potConfig = PotSDK.getConfig(potId); if (!potConfig) return <A_133 style={{ justifyContent: \"center\", alignItems: \"center\" }}> {potConfig == null ? <div className=\"spinner-border text-secondary\" role=\"status\" /> : <div>Pot {potId} not found.</div>} </A_133>; const { pot_name, pot_description, matching_pool_balance } = potConfig; const amountNear = yoctosToNear(matching_pool_balance, true); const amountUsd = yoctosToUsd(matching_pool_balance); const description = !pot_description ? \"No description\" : pot_description.length > MAX_DESCRIPTION_LENGTH ? \\`\\${pot_description.slice(0, MAX_DESCRIPTION_LENGTH)}...\\` : pot_description; const title = !pot_name ? \"No title\" : pot_name.length > MAX_TITLE_LENGTH ? \\`\\${pot_name.slice(0, MAX_TITLE_LENGTH)}...\\` : pot_name; const tags = tagsList(potConfig); return <A_133 href={hrefWithParams(\\`?tab=pot&potId=\\${potId}\\`)}> <CardSection> <A_134>{title}</A_134> <A_135> <Markdown text={description} /> </A_135> </CardSection> <CardSection style={{ background: \"#F6F5F3\", borderTop: \"1px #7B7B7B solid\", marginTop: \"auto\", height: \"fit-content\" }}> <A_134> <div> {amountNear} {amountUsd && <span className=\"usd-amount\">{amountUsd}</span>} <span className=\"text\">in pot</span> </div> </A_134> {tags.map((tag) => tag.visibility ? <Tag {...tag} preElements={<Indicator {...tag.preElementsProps || {}} />} key={tag.text} /> : \"\")} </CardSection> </A_133>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const filterBy = { \"no-label\": [{ label: \"Application open\", val: \"application_open\" }, { label: \"Matching round open\", val: \"round_open\" }, { label: \"Application closed\", val: \"application_closed\" }, { label: \"Challenge period\", val: \"cooldown\" }]};const sortOptions = { \"no-label\": [{ label: \"Most to least in pot\", val: \"least_pots\" }, { label: \"Least to most in pot\", val: \"most_pots\" }, { label: \"Most to least donations\", val: \"most_donations\" }, { label: \"Least to most donations\", val: \"least_donations\" }]};const currentDate = Date.now();const filters = { application_not_started: round => currentDate < round.application_start_ms, application_open: round => currentDate > round.application_start_ms && currentDate < round.application_end_ms, application_closed: round => currentDate > round.application_end_ms, round_end: round => currentDate > round.public_round_end_ms, round_open: round => currentDate > round.public_round_start_ms && currentDate < round.public_round_end_ms, cooldown: round => currentDate > round.public_round_end_ms && currentDate < round.cooldown_end_ms, completed: round => round.all_paid_out};const potsSort = { active: { check: filters.round_open, time: \"public_round_end_ms\", items: [] }, cooldown: { check: filters.cooldown, time: \"cooldown_end_ms\", items: [] }, application: { check: filters.application_open, time: \"application_end_ms\", items: [] }, not_started: { check: filters.application_not_started, time: \"application_start_ms\", items: [] }, rest: { check: round => true, time: \"application_start_ms\", items: [] }}; const A_124 = styled.div\\` margin-bottom: 1rem; display: flex; align-items: center; gap: 1rem; font-size: 18px; font-weight: 600; .span { font-weight: 600; }\\`;const A_125 = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; padding-bottom: 48px; .content { display: flex; flex-direction: column; width: 100%; padding: 0 64px; margin-top: 3rem; } .header { display: flex; align-items: center; margin-bottom: 1rem; .filters { gap: 1rem; display: flex; align-items: center; .sort { width: 286px; flex-direction: column; padding: 0.5rem; gap: 0; .title { display: none; } .option { border: none; width: 100%; padding: 0.5rem; } } } } @media only screen and (max-width: 768px) { .content { padding: 0 20px; } .header { flex-direction: column; align-items: flex-start; gap: 1rem; } }\\`;const A_126 = styled.div\\` height: 1px; width: 100%; background: #c7c7c7; margin: 3rem 0;\\`; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_127 = styled.div\\` display: flex; flex-direction: column; position: relative; width: 100%; justify-content: center; min-height: 400px; overflow: hidden; .background { position: absolute; pointer-events: none; height: 100%; left: 0; top: 0; } .content { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: center; padding: 64px; } .sub-title { letter-spacing: 1.12px; font-weight: 500; font-size: 14px; margin-top: 0; margin-bottom: 24px; text-transform: uppercase; } .title { letter-spacing: -0.4px; font-weight: 500; font-size: 40px; font-family: \"Lora\"; margin: 0; } .btns { display: flex; align-items: center; gap: 2rem; margin-top: 40px; a { padding: 12px 16px; width: 180px; display: flex; align-items: center; justify-content: center; font-weight: 500; border-radius: 6px; box-shadow: 0px -2px 0px 0px #464646 inset, 0px 0px 0px 1px #464646; text-decoration: none; color: #292929; transition: all 300ms; &:first-of-type { color: white; background: #dd3345; &:hover { } } &:hover { background: #292929; color: white; } } } @media only screen and (max-width: 768px) { .content { padding: 64px 20px; } .title { font-size: 36px; } .btns { flex-direction: column; gap: 1rem; margin-top: 24px; } .line-break { display: none; } } @media only screen and (max-width: 480px) { .btns a { width: 100%; padding: 12px 0; } }\\`; const svgContent = \\`<svg width=\"1320\" height=\"332\" viewBox=\"0 0 1320 332\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"1320\" height=\"1\" fill=\"#FEF6EE\"/><mask id=\"mask0_11854_22101\" style=\"mask-type:alpha\" maskUnits=\"userSpaceOnUse\" x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\"><rect x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\" fill=\"url(#paint0_radial_11854_22101)\"/></mask><g mask=\"url(#mask0_11854_22101)\"><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" fill=\"#FEF6EE\"/><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" fill=\"#FEF6EE\"/><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" fill=\"#F8D3B0\"/><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" fill=\"#F8D3B0\"/><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" fill=\"#F8D3B0\"/><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" fill=\"#F8D3B0\"/><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" fill=\"#F8D3B0\"/><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" fill=\"#F8D3B0\"/><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" fill=\"#F8D3B0\"/><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" fill=\"#F8D3B0\"/><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" fill=\"#FEF6EE\"/><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" fill=\"#FEF6EE\"/><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" fill=\"#FEF6EE\"/><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" fill=\"#F8D3B0\"/><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" fill=\"#F8D3B0\"/><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" fill=\"#F8D3B0\"/><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" fill=\"#F8D3B0\"/><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" fill=\"#F8D3B0\"/><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" fill=\"#F8D3B0\"/><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" fill=\"#F8D3B0\"/><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" fill=\"#F8D3B0\"/><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" stroke=\"#F4B37D\"/></g></g><defs><radialGradient id=\"paint0_radial_11854_22101\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(660 -158.5) rotate(90) scale(724.5 1018.83)\"><stop stop-color=\"#FCE9D5\"/><stop offset=\"0.855072\" stop-color=\"#FEF6EE\" stop-opacity=\"0\"/></radialGradient></defs></svg>\\`;const HomeBannerStyle = { backgroundImage: \\`url(\"data:image/svg+xml;charset=utf-8,\\${encodeURIComponent(svgContent)}\")\\`, backgroundSize: \"cover\", backgroundRepeat: \"no-repeat\", backgroundColor: \"#FEF6EE\"}; const A_128 = () => { const canDeploy = PotFactorySDK.canUserDeployPot(context.accountId); return <A_127 style={{ ...HomeBannerStyle }}> <div className=\"content\"> <h3 className=\"sub-title\">Explore Pots</h3> <h1 className=\"title\"> Donate to Matching Rounds <br className=\"line-break\" /> to Get Your Contributions Amplified. </h1> <div className=\"btns\"> {canDeploy && <a href={hrefWithParams(\\`?tab=deploypot\\`)}>Deploy Pot</a>} <a href=\"https://wtfisqf.com\" target=\"_blank\"> Learn More </a> </div> </div> </A_127>;}; const [pots, setPots] = useState(null); const [inProgressRounds, setInProgressRounds] = useState([]); const [filteredRounds, setFilteredRounds] = useState([]); const [completedRounds, setCompletedRounds] = useState([]); if (!pots) { PotFactorySDK.asyncGetPots().then((pots) => { pots.forEach(({ id }) => { PotSDK.asyncGetConfig(id).then((potConfig) => setPots((prevPot) => ({ ...prevPot, [id]: { ...potConfig, id } }))); }); }); } const compareFunction = (pots) => { const listOfPots = {}; const states = Object.keys(potsSort); pots.forEach((pot) => { Object.keys(potsSort).some((type) => { const { check, items } = potsSort[type]; if (check(pot)) { potsSort[type].items = [...items, pot]; return true; } }); }); const inProgressPots = []; Object.values(potsSort).forEach(({ items, time }) => { items.sort((a, b) => a[time] - b[time]); inProgressPots.push(...items); }); return inProgressPots; }; useEffect(() => { if (pots) { const potsVal = Object.values(pots); const completed = []; let inprogress = []; potsVal.forEach((round) => { if (filters.completed(round)) { completed.push(round); } else { inprogress.push(round); } }); inprogress = compareFunction(inprogress); setFilteredRounds(inprogress); setInProgressRounds(inprogress); setCompletedRounds(completed); } }, [pots]); const handleFilter = (selected) => { const selectedList = Object.values(selected)[0]; if (selectedList.length === 0) { return setFilteredRounds(inProgressRounds); } const filteredRounds = [...inProgressRounds].filter((round) => selectedList.some((key) => { return filters[key](round) === true; })); setFilteredRounds(filteredRounds); }; const handleSort = ({ val }) => { const sortedRounds = filteredRounds; switch (val) { case \"least_pots\": sortedRounds.sort((a, b) => Big(b.matching_pool_balance) - Big(a.matching_pool_balance)); break; case \"most_pots\": sortedRounds.sort((a, b) => Big(a.matching_pool_balance) - Big(b.matching_pool_balance)); break; case \"most_donations\": sortedRounds.sort((a, b) => Big(b.total_public_donations) - Big(a.total_public_donations)); break; case \"least_donations\": sortedRounds.sort((a, b) => Big(a.total_public_donations) - Big(b.total_public_donations)); break; } setFilteredRounds(sortedRounds); }; return <A_125> <A_128 /> <div className=\"content\"> <div className=\"header\"> <A_124 style={{ marginRight: \"auto\", marginBottom: 0 }}> Active Pots <span>{filteredRounds.length}</span> </A_124> <div className=\"filters\"> <Widget loading=\" \" code={props.alem.componentsCode.FilterDropdown} props={{ ...{ options: filterBy, onClick: handleFilter, multipleOptions: true, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.FilterDropdown} props={{ ...{ options: sortOptions, onClick: handleSort, label: \"Sort\", menuClass: \"sort\", labelIcon: \"right\", ...props } }} /> </div> </div> {filteredRounds.length === 0 && <div>No pots</div>} <Widget loading=\" \" code={props.alem.componentsCode.ListSection} props={{ ...{ items: filteredRounds, renderItem: (pot) => <PotCard potId={pot.id} key={pot.id} />, maxCols: 3, responsive: [{ breakpoint: 1114, items: 2 }, { breakpoint: 768, items: 1 }], ...props } }} /> <A_126 /> <A_124> Completed Pots <span>{completedRounds.length}</span> </A_124> <Widget loading=\" \" code={props.alem.componentsCode.ListSection} props={{ ...{ items: completedRounds, renderItem: (pot) => <PotCard potId={pot.id} key={pot.id} />, maxCols: 3, responsive: [{ breakpoint: 1114, items: 2 }, { breakpoint: 768, items: 1 }], ...props } }} /> </div> </A_125>; `, A_131: ` const Container = styled.div\\` display: flex; flex-direction: column; gap: 1.5rem;\\`;const Title = styled.div\\` font-size: 24px; font-weight: 600;\\`;const Funding = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; background: #fff; overflow: hidden; .header { border-bottom: 0.5px solid #7b7b7b; padding: 0.5rem 1rem; div { font-weight: 600; } @media screen and (max-width: 768px) { .tab { display: none; } .funding { display: block; } } } .funding-row { padding: 1rem; } .header, .funding-row { display: flex; justify-content: space-between; gap: 2rem; font-size: 14px; flex-wrap: wrap; @media screen and (max-width: 768px) { gap: 4px; } } .tab { display: flex; align-items: center; gap: 0.5rem; width: 156px; justify-content: left; &.sort { cursor: pointer; svg { transition: rotate 300ms; } } @media screen and (max-width: 768px) { white-space: nowrap; width: 60px; } } .funding { flex: 1; } .price { gap: 1rem; font-weight: 600; justify-content: left; svg { width: 1.5em; } } .date { justify-content: right; } @media screen and (max-width: 768px) { .price { gap: 0.5rem; } .date { width: 100%; justify-content: left; color: #7b7b7b; margin-left: 2.5rem; } }\\`;const FundingSrc = styled.div\\` display: flex; align-items: center; gap: 1rem; flex: 1; max-width: 100%; gap: 1rem; .profile-image { width: 24px; height: 24px; } .funding-src { display: flex; flex-direction: column; .pot-name { color: inherit; font-weight: inherit; display: none; } a { color: #292929; transition: 300ms; font-weight: 600; :hover { text-decoration: none; color: #dd3345; } } .type { color: #7b7b7b; } } @media screen and (max-width: 768px) { .funding-src .type { display: none; } .funding-src .pot-name { display: inline-block; } }\\`;const SearchBar = styled.div\\` display: flex; align-items: center; background: #f6f5f3; position: relative; svg { width: 18px; left: 1rem; top: 50%; transform: translateY(-50%); position: absolute; pointer-events: none; } input { width: 100%; height: 100%; padding: 1rem; padding-left: 50px; border: none; background: transparent; :focus { outline: none; } }\\`;const Stats = styled.div\\` display: flex; flex-wrap: wrap; margin: 24px 0; align-items: center; .item { display: flex; height: fit-content; gap: 8px; padding-right: 1rem; align-items: center; :nth-child(2) { border-right: 1px solid #7b7b7b; border-left: 1px solid #7b7b7b; padding-left: 1rem; } :nth-child(3) { padding-left: 1rem; } .item-value { font-weight: 600; } @media screen and (max-width: 768px) { display: none; } } .dropdown { margin-left: auto; .dropdown-menu-custom { left: auto; right: 0; } @media screen and (max-width: 480px) { margin-right: auto; margin-left: 0; } }\\`;const Sort = styled.div\\` display: none; justify-content: space-between; width: 100%; div { display: flex; align-items: center; font-weight: 500; cursor: pointer; gap: 8px; color: #7b7b7b; svg { transition: rotate 300ms; } &.active { color: #292929; } } @media screen and (max-width: 768px) { display: flex; }\\`;const DropdownLabelWrapper = styled.div\\` display: flex; gap: 10px; align-items: center; .label { font-weight: 500; } .count { display: flex; align-items: center; justify-content: center; border-radius: 50%; background: #ebebeb; }\\`;const ImgIcon = styled.img\\` width: 21px; height: 21px;\\`; const A_129 = (__props__) => <svg {...__props__} style={{ rotate: !__props__.active ? \"0deg\" : \"180deg\"}} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 6L1.0575 7.0575L5.25 2.8725V12H6.75V2.8725L10.935 7.065L12 6L6 0L0 6Z\" fill=\"#7B7B7B\" /> </svg>; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const PotIcon = __props__ => <svg {...__props__} viewBox=\"0 0 20 21\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M10 3C10.5523 3 11 2.55228 11 2C11 1.44772 10.5523 1 10 1C9.44772 1 9 1.44772 9 2C9 2.55228 9.44772 3 10 3ZM12 2C12 2.37912 11.8945 2.7336 11.7113 3.03569C14.6721 3.33449 17.0882 5.47841 17.7921 8.3C17.9279 8.84425 18 9.41371 18 10H16.3H3.7H2C2 9.41371 2.07208 8.84425 2.20786 8.3C2.9118 5.47841 5.3279 3.33449 8.28871 3.03569C8.10549 2.7336 8 2.37912 8 2C8 0.895431 8.89543 0 10 0C11.1046 0 12 0.895431 12 2ZM9 4.7C6.66751 4.7 4.68694 6.20674 3.97852 8.3H16.0215C15.3131 6.20674 13.3325 4.7 11 4.7H9ZM0 11H2H4H16H18H20V13H18V19C18 20.1046 17.1046 21 16 21H4C2.89543 21 2 20.1046 2 19V13H0V11ZM4 19V13H16V19H4Z\" fill=\"#7B7B7B\" /> </svg>; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const getPotDonations = (potId, potDetail, accountId, potDonations, setPotDonations) => { return PotSDK.asyncGetDonationsForDonor(potId, accountId).then((donations) => { donations = donations.filter((donations) => donations.donor_id === accountId); const updatedDonations = donations.map((donation) => ({ ...donation, base_currency: potDetail.base_currency, pot_name: potDetail.pot_name, pot_id: potId, type: donation.project_id ? \"matched\" : \"sponsorship\" })); if (potDonations[potId]) return \"\"; setPotDonations((prevpotDonations) => { return { ...prevpotDonations, [potId]: updatedDonations }; }); }).catch(() => { if (potDonations[potId]) return \"\"; setPotDonations((prevpotDonations) => { return { ...prevpotDonations, [potId]: [] }; }); });};const A_130 = (searchTerm, totalDonations) => { const filteredApplications = totalDonations.filter((item) => { const searchIn = [item.pot_name || \"\", item.recipient_id || \"\", item.project_id || \"\", item.donor_id || \"\", item.pot_id || \"\"]; return searchIn.some((item) => item.toLowerCase().includes(searchTerm.toLowerCase())); }); return filteredApplications;};const filterDonations = (sortVal, sortList, search, totalDonations) => { const displayedDonations = A_130(search, totalDonations); let filtered; if (sortVal && sortVal !== \"all\") { filtered = displayedDonations.filter((donation) => { return sortList[donation.type].val === sortVal; }); return filtered; } else { return displayedDonations; }};const getName = (donation) => { switch (donation.type) { case \"direct\": return donation.recipient_id; case \"sponsorship\": return donation.pot_name; case \"payout\": return donation.pot_name; case \"matched\": return donation.project_id; default: return donation.recipient_id; }};const addTrailingZeros = (number) => { if (number < 100 && number >= 0.1) return number.toFixed(1); return number;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const NearOutline = __props__ => { return <svg {...__props__} viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <circle cx=\"9\" cy=\"9\" r=\"6.75\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.9509 5.25C11.6728 5.25 11.4153 5.39375 11.2697 5.63062L9.70187 7.95812C9.67925 7.99164 9.66959 8.03224 9.67469 8.07235C9.67979 8.11246 9.69932 8.14935 9.72961 8.17612C9.75991 8.2029 9.79892 8.21775 9.83935 8.21788C9.87979 8.21802 9.91889 8.20344 9.94937 8.17687L11.4925 6.83875C11.5015 6.8306 11.5127 6.82527 11.5247 6.82339C11.5367 6.82151 11.549 6.82318 11.5601 6.82819C11.5712 6.8332 11.5806 6.84133 11.5871 6.85159C11.5936 6.86184 11.597 6.87378 11.5969 6.88594V11.0766C11.5969 11.0894 11.5929 11.102 11.5856 11.1125C11.5782 11.1231 11.5677 11.1311 11.5556 11.1355C11.5435 11.1398 11.5304 11.1404 11.5179 11.137C11.5055 11.1336 11.4945 11.1265 11.4862 11.1166L6.82187 5.53281C6.74688 5.44415 6.65345 5.37291 6.5481 5.32407C6.44274 5.27522 6.328 5.24994 6.21187 5.25H6.04906C5.83714 5.25 5.63389 5.33419 5.48404 5.48404C5.33419 5.63389 5.25 5.83714 5.25 6.04906V11.9509C5.25 12.125 5.30683 12.2943 5.41184 12.4331C5.51686 12.5719 5.66432 12.6726 5.83182 12.7199C5.99931 12.7672 6.17767 12.7586 6.33979 12.6952C6.50191 12.6319 6.63893 12.5174 6.73 12.3691L8.29781 10.0416C8.32044 10.008 8.3301 9.96745 8.32499 9.92733C8.31989 9.88722 8.30037 9.85034 8.27007 9.82356C8.23977 9.79678 8.20077 9.78194 8.16033 9.7818C8.11989 9.78166 8.08079 9.79624 8.05031 9.82281L6.50719 11.1612C6.49813 11.1693 6.48693 11.1746 6.47494 11.1764C6.46295 11.1782 6.4507 11.1765 6.43967 11.1714C6.42864 11.1664 6.41931 11.1583 6.41282 11.148C6.40633 11.1378 6.40296 11.1259 6.40312 11.1137V6.9225C6.4031 6.90963 6.40705 6.89707 6.41443 6.88652C6.42181 6.87598 6.43227 6.86798 6.44437 6.8636C6.45647 6.85922 6.46963 6.85869 6.48205 6.86207C6.49447 6.86546 6.50554 6.87259 6.51375 6.8825L11.1775 12.4669C11.3294 12.6462 11.5525 12.7497 11.7875 12.75H11.9503C12.1622 12.7501 12.3655 12.666 12.5154 12.5163C12.6654 12.3666 12.7497 12.1635 12.75 11.9516V6.04906C12.7499 5.83716 12.6657 5.63397 12.5159 5.48413C12.366 5.3343 12.1628 5.25008 11.9509 5.25Z\" fill=\"#292929\" /> </svg>;}; const { SUPPORTED_FTS } = constants; const PER_PAGE = 30; const [ftMetadata, setFtMetadata] = useState({}); const [currentPage, setCurrentPage] = useState(1); const [filter, setFilter] = useState({ date: false, price: false }); const [currentFilter, setCurrentFilter] = useState(\"date\"); const [sort, setSort] = useState(\"all\"); const [search, setSearch] = useState(\"\"); const [potDonations, setPotDonations] = useState({}); const [directDonations, setDirectDonations] = useState(null); const [filteredDonations, setFilteredDonations] = useState(null); const { accountId } = props.alem.useParams(); const pots = PotFactorySDK.getPots(); let donationsForDonor = DonateSDK.getDonationsForDonor(accountId); if (donationsForDonor && !directDonations) { donationsForDonor = donationsForDonor.map((donation) => ({ ...donation, type: \"direct\" })); setDirectDonations(donationsForDonor); } if (pots && !potDonations[pots[pots.length - 1].id]) { pots.forEach((pot) => { PotSDK.asyncGetConfig(pot.id).then((potDetail) => { getPotDonations(pot.id, potDetail, accountId, potDonations, setPotDonations); }); }); } const [totalDonations, sponsorships, matchingRoundDonations] = useMemo(() => { const potDonationsValue = Object.values(potDonations).flat(); const sponsorships = potDonationsValue.filter((donation) => donation.type === \"sponsorship\"); const matchingRoundDonations = potDonationsValue.filter((donation) => donation.type === \"matched\"); const allDonations = [...(directDonations || []), ...potDonationsValue]; allDonations.sort((a, b) => (b.donated_at || b.donated_at_ms) - (a.donated_at || a.donated_at_ms)); setFilteredDonations(allDonations); return [allDonations, sponsorships, matchingRoundDonations]; }, [potDonations, directDonations]); const handleSortChange = ({ val }) => { const filtered = filterDonations(val, sortList, search, totalDonations); setFilteredDonations(filtered); setSort(val); }; const [totalDonationAmountNear] = useMemo(() => { let total = Big(0); totalDonations.forEach((donation) => { if (donation.ft_id === \"near\" || donation.base_currency === \"near\") { total = total.plus(Big(donation.total_amount || donation.amount)); } }); const totalDonationAmountNear = SUPPORTED_FTS.NEAR.fromIndivisible(total.toString()); return [totalDonationAmountNear]; }, [totalDonations]); useEffect(() => { const metadata = {}; const ftIds = totalDonations.reduce((acc, donation) => { if (donation.ft_id && donation.ft_id !== \"near\") { acc.add(donation.ft_id); } return acc; }, new Set()); ftIds.forEach((ftId) => { Near.asyncView(ftId, \"ft_metadata\", {}).then((ftMetadata) => { metadata[ftId] = ftMetadata; if (Object.keys(metadata).length === ftIds.size) { setFtMetadata(metadata); } }).catch((e) => { console.error(\"error getting ft metadata: \", e); }); }); }, [totalDonations]); const getDate = (donation) => donation.donated_at_ms || donation.donated_at; const sortDonation = (type) => { setCurrentFilter(type); const sort = !filter[type]; setFilter({ ...filter, [type]: sort }); if (type === \"price\") { const sortedDonations = filteredDonations.sort((a, b) => sort ? b.total_amount - a.total_amount : a.total_amount - b.total_amount); setFilteredDonations(sortedDonations); } else if (type === \"date\") { const sortedDonations = filteredDonations.sort((a, b) => { return sort ? getDate(a) - getDate(b) : getDate(b) - getDate(a); }); setFilteredDonations(sortedDonations); } }; const sortList = { all: { label: \"All donations\", val: \"all\", count: totalDonations?.length }, direct: { label: \"Direct donations\", val: \"direct\", count: directDonations?.length }, matched: { label: \"Matched donations\", val: \"matched\", count: matchingRoundDonations?.length }, sponsorship: { label: \"Sponsorships\", val: \"sponsorship\", count: sponsorships?.length } }; const DropdownLabel = () => { const digit = sortList[sort].count.toString().length; return <DropdownLabelWrapper> <div className=\"label\">{sortList[sort].label}</div> <div className=\"count\" style={{ width: \\`\\${24 + (digit - 1) * 6}px\\`, height: \\`\\${24 + (digit - 1) * 6}px\\` }}> {sortList[sort].count} </div> </DropdownLabelWrapper>; }; return <Container> <Stats> {totalDonationAmountNear && <div className=\"item\"> <div className=\"item-value\"> {\" \"} {totalDonationAmountNear}N{A_236 && <span>~\\${(totalDonationAmountNear * A_236).toFixed(2)}</span>} </div> <div className=\"item-label\">Donated</div> </div>} <div className=\"dropdown\"> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ handleSortChange: handleSortChange, sortList: Object.values(sortList), FilterMenuCustomClass: \"dropdown-menu-custom\", showCount: true, sortVal: <DropdownLabel />, ...props } }} /> </div> </Stats> <Sort> <div onClick={() => sortDonation(\"date\")} className={\\`\\${currentFilter === \"date\" ? \"active\" : \"\"}\\`}> Sort Date {currentFilter === \"date\" && <A_129 active={!filter.date} />} </div> <div onClick={() => sortDonation(\"price\")} className={\\`\\${currentFilter === \"price\" ? \"active\" : \"\"}\\`}> Sort Amount {currentFilter === \"price\" && <A_129 active={filter.price} />} </div> </Sort> <Funding> <div className=\"header\"> <div className=\"funding tab\">Project Name</div> <div className=\"tab sort\" onClick={() => sortDonation(\"price\")}> Amount {currentFilter === \"price\" && <A_129 active={filter.price} />} </div> <div className=\"tab sort date\" onClick={() => sortDonation(\"date\")}> Date {currentFilter === \"date\" && <A_129 active={!filter.date} />} </div> </div> <SearchBar> <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.7549 14.2549H14.9649L14.6849 13.9849C15.6649 12.8449 16.2549 11.3649 16.2549 9.75488C16.2549 6.16488 13.3449 3.25488 9.75488 3.25488C6.16488 3.25488 3.25488 6.16488 3.25488 9.75488C3.25488 13.3449 6.16488 16.2549 9.75488 16.2549C11.3649 16.2549 12.8449 15.6649 13.9849 14.6849L14.2549 14.9649V15.7549L19.2549 20.7449L20.7449 19.2549L15.7549 14.2549ZM9.75488 14.2549C7.26488 14.2549 5.25488 12.2449 5.25488 9.75488C5.25488 7.26488 7.26488 5.25488 9.75488 5.25488C12.2449 5.25488 14.2549 7.26488 14.2549 9.75488C14.2549 12.2449 12.2449 14.2549 9.75488 14.2549Z\" fill=\"#C7C7C7\" /> </svg> <input className=\"\" placeholder=\"Search funding\" onChange={(e) => { if (currentPage !== 1) setCurrentPage(1); setSearch(e.target.value); const filtered = A_130(e.target.value, totalDonations); setFilteredDonations(filtered); }} type=\"text\" /> </SearchBar> {filteredDonations?.length > 0 ? filteredDonations.slice((currentPage - 1) * PER_PAGE, currentPage * PER_PAGE).map((donation) => { const { total_amount, amount, pot_id, recipient_id, project_id, paid_at, base_currency, ft_id, type, donated_at, donated_at_ms } = donation; const ftId = ft_id || base_currency; const donationAmount = parseFloat(Big(total_amount || amount).div(Big(10).pow(ftId === \"near\" ? 24 : ftMetadata[ftId]?.decimals || 24)).toFixed(2)); const isPot = type === \"sponsorship\"; const url = isPot ? \\`?tab=pot&potId=\\${pot_id}\\` : \\`?tab=project&projectId=\\${project_id || recipient_id}\\`; const name = _address(getName(donation), 15); return <div className=\"funding-row\"> <FundingSrc> {isPot ? <PotIcon className=\"profile-image\" /> : <ProfileImage accountId={recipient_id || project_id} style={{}} />} <div className=\"funding-src\"> <a href={hrefWithParams(url)} target=\"_blank\"> {isPot && <span className=\"pot-name\"> Sponsor :</span>} {name} </a> <div className=\"type\">{sortList[type].label?.slice(0, -1)}</div> </div> </FundingSrc> <div className=\"price tab\"> <div className=\"near-icon\"> {ftId === \"near\" ? <NearOutline /> : <ImgIcon src={ftMetadata[ftId]?.icon} />} </div> {addTrailingZeros(donationAmount)} </div> <div className=\"tab date\">{getTimePassed(donated_at_ms || donated_at || paid_at, true)} ago</div> </div>; }) : <div className=\"funding-row\">No Donations</div>} </Funding> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ data: filteredDonations, currentPage: currentPage, perPage: PER_PAGE, onPageChange: (page) => {setCurrentPage(page);}, ...props } }} /> </Container>; `, Pots: ` const Outer = styled.div\\` display: flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; @keyframes beacon { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.3); } } animation: beacon 1.5s infinite;\\`;const Inner = styled.div\\` width: 10px; height: 10px; border-radius: 50%;\\`; const Indicator = ({ animate, colorInner, colorOuter}) => { return <Outer style={{ backgroundColor: colorOuter, animationPlayState: animate ? \"running\" : \"paused\" }}> <Inner style={{ backgroundColor: colorInner }} /> </Outer>;}; const TagContainer = styled.div\\` display: flex; justify-content: center; align-items: center; text-align: center; padding: 6px 8px; border-radius: 4px;\\`;const TagText = styled.span\\` font-size: 14px;\\`; const Tag = __props__ => { const { backgroundColor, borderColor, textColor, text } = __props__; const textStyle = __props__.textStyle || {}; return <TagContainer style={{ backgroundColor: backgroundColor || \"#ffffff\", border: \\`1px solid \\${borderColor || \"#000000\"}\\`, boxShadow: \\`0px -0.699999988079071px 0px \\${borderColor} inset\\` }}> {__props__.preElements} <TagText style={{ color: textColor || \"#000000\", ...textStyle }}> {text} </TagText> </TagContainer>;}; const nearToUsd = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then(res => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\");const yoctosToUsd = amount => { return nearToUsd ? \"~\\$\" + formatWithCommas(new Big(amount).mul(nearToUsd).div(1e24).toFixed(2)) : \"0\";}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; function daysUntil(timestamp) { const now = Date.now(); const differenceInTime = timestamp - now; const differenceInDays = Math.ceil(differenceInTime / (1000 * 3600 * 24)); return \\`\\${differenceInDays} \\${differenceInDays === 1 ? \"day\" : \"days\"}\\`;} const tagsList = potConfig => { const { application_start_ms, application_end_ms, public_round_start_ms, public_round_end_ms, cooldown_end_ms, all_paid_out } = potConfig; const now = Date.now(); const applicationOpen = now >= application_start_ms && now < application_end_ms; const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; const cooldownPending = public_round_end_ms && now >= public_round_end_ms && !cooldown_end_ms; const cooldownOpen = now >= public_round_end_ms && now < cooldown_end_ms; const payoutsPending = cooldown_end_ms && now >= cooldown_end_ms && !all_paid_out; const payoutsCompleted = all_paid_out; const tags = [{ backgroundColor: \"#EFFEFA\", borderColor: \"#33DDCB\", textColor: \"#023131\", text: \"Sponsorship Open\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#CAFDF3\", colorInner: \"#33DDCB\", animate: true }, visibility: now < application_start_ms }, { backgroundColor: \"#EFFEFA\", borderColor: \"#33DDCB\", textColor: \"#023131\", text: daysUntil(application_end_ms) + \" left to apply\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#CAFDF3\", colorInner: \"#33DDCB\", animate: true }, visibility: applicationOpen }, { backgroundColor: \"#F7FDE8\", borderColor: \"#9ADD33\", textColor: \"#192C07\", text: daysUntil(public_round_end_ms) + \" left to donate\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#D7F5A1\", colorInner: \"#9ADD33\", animate: true }, visibility: publicRoundOpen }, { backgroundColor: \"#F5F3FF\", borderColor: \"#A68AFB\", textColor: \"#2E0F66\", text: \"Cooldown pending\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#EDE9FE\", colorInner: \"#A68AFB\", animate: true }, visibility: cooldownPending }, { backgroundColor: \"#F5F3FF\", borderColor: \"#A68AFB\", textColor: \"#2E0F66\", text: \"Challenge period\", textStyle: { fontWeight: 500, marginLeft: \"8px\" }, preElementsProps: { colorOuter: \"#EDE9FE\", colorInner: \"#A68AFB\", animate: true }, visibility: cooldownOpen }, { backgroundColor: \"#F7FDE8\", borderColor: \"#9ADD33\", textColor: \"#192C07\", text: \"Payouts pending\", preElementsProps: { colorOuter: \"#D7F5A1\", colorInner: \"#9ADD33\", animate: true }, textStyle: { fontWeight: 500, marginLeft: \"8px\" }, visibility: payoutsPending }, { backgroundColor: \"#464646\", borderColor: \"#292929\", textColor: \"#FFF\", text: \"Payouts completed\", preElementsProps: { colorOuter: \"#656565\", colorInner: \"#A6A6A6\", animate: false }, textStyle: { fontWeight: 500, marginLeft: \"8px\" }, visibility: payoutsCompleted }]; return tags;}; const A_133 = styled.a\\` display: flex; flex-direction: column; min-width: 320px; min-height: 300px; border-radius: 8px; background: white; box-shadow: 0px -2px 0px 0px #464646 inset, 0px 0px 0px 1px #464646; padding-bottom: 5px; height: 100%; &:hover { text-decoration: none; cursor: pointer; }\\`;const CardSection = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 16px; padding: 32px; width: 100%; height: 100%;\\`;const A_134 = styled.div\\` color: #292929; font-size: 22px; font-weight: 600; line-height: 28px; word-wrap: break-word; > div { font-weight: inherit; display: flex; align-items: baseline; } .usd-amount { font-size: 14px; font-weight: 400; margin-left: 0.25rem; } .text { font-size: 14px; color: #7b7b7b; margin-left: 0.5rem; }\\`;const A_135 = styled.div\\` color: #525252; font-size: 16px; font-weight: 400; line-height: 28px; word-wrap: break-word; a { color: rgb(123, 123, 123); }\\`;const Subtitle = styled.span\\` color: #7b7b7b; font-size: 14px; font-weight: 600; line-height: 20px; word-wrap: break-word;\\`; const MAX_DESCRIPTION_LENGTH = 100;const MAX_TITLE_LENGTH = 36;const PotCard = ({ potId}) => { const potConfig = PotSDK.getConfig(potId); if (!potConfig) return <A_133 style={{ justifyContent: \"center\", alignItems: \"center\" }}> {potConfig == null ? <div className=\"spinner-border text-secondary\" role=\"status\" /> : <div>Pot {potId} not found.</div>} </A_133>; const { pot_name, pot_description, matching_pool_balance } = potConfig; const amountNear = yoctosToNear(matching_pool_balance, true); const amountUsd = yoctosToUsd(matching_pool_balance); const description = !pot_description ? \"No description\" : pot_description.length > MAX_DESCRIPTION_LENGTH ? \\`\\${pot_description.slice(0, MAX_DESCRIPTION_LENGTH)}...\\` : pot_description; const title = !pot_name ? \"No title\" : pot_name.length > MAX_TITLE_LENGTH ? \\`\\${pot_name.slice(0, MAX_TITLE_LENGTH)}...\\` : pot_name; const tags = tagsList(potConfig); return <A_133 href={hrefWithParams(\\`?tab=pot&potId=\\${potId}\\`)}> <CardSection> <A_134>{title}</A_134> <A_135> <Markdown text={description} /> </A_135> </CardSection> <CardSection style={{ background: \"#F6F5F3\", borderTop: \"1px #7B7B7B solid\", marginTop: \"auto\", height: \"fit-content\" }}> <A_134> <div> {amountNear} {amountUsd && <span className=\"usd-amount\">{amountUsd}</span>} <span className=\"text\">in pot</span> </div> </A_134> {tags.map((tag) => tag.visibility ? <Tag {...tag} preElements={<Indicator {...tag.preElementsProps || {}} />} key={tag.text} /> : \"\")} </CardSection> </A_133>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_136 = styled.div\\` display: grid; grid-template-columns: repeat(3, 1fr); > div { padding-top: 0rem; } @media screen and (max-width: 1400px) { grid-template-columns: repeat(2, 1fr); } @media screen and (max-width: 768px) { grid-template-columns: repeat(1, 1fr); }\\`;const NoResults = styled.div\\` display: flex; justify-content: space-between; align-items: center; padding: 68px 105px; border-radius: 12px; background: #f6f5f3; .text { font-family: \"Lora\"; max-width: 290px; font-size: 22px; font-style: italic; font-weight: 500; color: #292929; } img { width: 60%; } @media screen and (max-width: 768px) { flex-direction: column-reverse; padding: 24px 16px; .text { font-size: 16px; } img { width: 100%; } }\\`; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const pots = PotFactorySDK.getPots(); const { projectId } = props.alem.useParams(); const [potIds, setPotIds] = useState(null); const [loading, setLoading] = useState(true); const getApprovedApplications = potId => PotSDK.asyncGetApprovedApplications(potId).then(applications => { if (applications.some(app => app.project_id === projectId)) { setPotIds([...(potIds || []), potId]); } if (pots[pots.length - 1].id === potId) setLoading(false); }).catch(() => console.log(\\`Error fetching approved applications for \\${potId}\\`)); if (pots && loading) { pots.forEach(pot => { getApprovedApplications(pot.id); }); } return loading ? \"Loading...\" : potIds.length ? <A_136> {potIds.map(potId => <PotCard {...{ potId, tab: \"pots\" }} />)} </A_136> : <NoResults> <div className=\"text\">This project has not participated in any pots yet.</div> <img src=\"https://ipfs.near.social/ipfs/bafkreibcjfkv5v2e2n3iuaaaxearps2xgjpc6jmuam5tpouvi76tvfr2de\" alt=\"pots\" /> </NoResults>; `, Pagination: ` const A_148 = styled.div\\` display: flex; gap: 1rem; justify-content: center; list-style-type: none; li { display: flex; align-items: center; justify-content: center; &.disabled { pointer-events: none; .arrow::before { border-right: 0.12em solid rgba(0, 0, 0, 0.43); border-top: 0.12em solid rgba(0, 0, 0, 0.43); } &:hover { cursor: default; } } } .pagination-item { border: 1px solid transparent; background: #292929; border-radius: 2px; padding: 10px; font-size: 12px; color: white; cursor: pointer; transition: all 300ms; &.dots:hover { cursor: default; opacity: 1; } &:hover { opacity: 0.75; } &.selected { background: white; cursor: default; color: #292929; border-color: #292929; } } .arrow { cursor: pointer; &::before { position: relative; content: \"\"; display: inline-block; width: 0.4em; height: 0.4em; border-right: 0.12em solid rgba(0, 0, 0, 0.87); border-top: 0.12em solid rgba(0, 0, 0, 0.87); } &.left { transform: rotate(-135deg) translate(-50%); } &.right { transform: rotate(45deg); } }\\`; const props = props; const DOTS = \"...\"; const range = (start, end) => { let length = end - start + 1; return Array.from({ length }, (_, idx) => idx + start); }; const usePagination = ({ totalCount, perPage, siblingCount, currentPage }) => { const paginationRange = useMemo(() => { const totalPageCount = Math.ceil(totalCount / perPage); const totalPageNumbers = siblingCount + 3; if (totalPageNumbers >= totalPageCount || totalPageCount < 6) { return range(1, totalPageCount); } const leftSiblingIndex = Math.max(currentPage - siblingCount, 1); const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPageCount); const shouldShowLeftDots = leftSiblingIndex > 2; const shouldShowRightDots = rightSiblingIndex <= totalPageCount - 3; const firstPageIndex = 1; const lastPageIndex = totalPageCount; if (!shouldShowLeftDots && shouldShowRightDots) { let leftItemCount = 3 + siblingCount; let leftRange = range(1, leftItemCount); return [...leftRange, DOTS, totalPageCount]; } if (shouldShowLeftDots && !shouldShowRightDots) { let rightItemCount = 3 + siblingCount; let rightRange = range(totalPageCount - rightItemCount + 1, totalPageCount); return [firstPageIndex, DOTS, ...rightRange]; } if (shouldShowLeftDots && shouldShowRightDots) { let middleRange = range(leftSiblingIndex, rightSiblingIndex); return [firstPageIndex, DOTS, ...middleRange, DOTS, lastPageIndex]; } if (!shouldShowLeftDots && !shouldShowRightDots) { return range(1, totalPageCount); } }, [totalCount, perPage, siblingCount, currentPage]); return paginationRange; }; const { onPageChange, data, currentPage, perPage } = props; const siblingCount = props.siblingCount ?? 1; const showArrows = props.showArrows ?? false; const totalCount = data?.length; const paginationRange = usePagination({ currentPage, totalCount, siblingCount, perPage }) || []; if (currentPage === 0 || paginationRange.length < 2) { return \"\"; } const onNext = () => { onPageChange(currentPage + 1); }; const onPrevious = () => { onPageChange(currentPage - 1); }; let lastPage = paginationRange[paginationRange.length - 1]; return <A_148> {showArrows && <li className={\\`\\${currentPage === 1 ? \"disabled\" : \"\"}\\`} onClick={onPrevious}> <div className=\"arrow left\" /> </li>} {paginationRange?.length > 0 && paginationRange.map(pageNumber => { if (pageNumber === DOTS) { return <li className=\"pagination-item dots\">&#8230;</li>; } return <li key={pageNumber} className={\\`pagination-item \\${pageNumber === currentPage ? \"selected\" : \"\"}\\`} onClick={() => onPageChange(pageNumber)}> {pageNumber} </li>; })} {showArrows && <li className={\\`\\${currentPage === lastPage ? \"disabled\" : \"\"}\\`} onClick={onNext}> <div className=\"arrow right\" /> </li>} </A_148>; `, PotlockFunding: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_140 = styled.div\\` display: flex; flex-direction: column; gap: 1.5rem;\\`;const A_141 = styled.div\\` font-size: 24px; font-weight: 600;\\`;const PotlockFundingContainer = styled.div\\` display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; background: #fff; overflow: hidden; .header { border-bottom: 0.5px solid #7b7b7b; padding: 0.5rem 1rem; div { font-weight: 600; } @media screen and (max-width: 768px) { .tab { display: none; } .funding { display: block; } } } .funding-row { padding: 1rem; } .header, .funding-row { display: flex; justify-content: space-between; gap: 2rem; font-size: 14px; flex-wrap: wrap; @media screen and (max-width: 768px) { gap: 4px; } } .tab { display: flex; align-items: center; gap: 0.5rem; width: 156px; justify-content: left; &.sort { cursor: pointer; svg { transition: rotate 300ms; } } @media screen and (max-width: 768px) { white-space: nowrap; width: 60px; } } .funding { flex: 1; } .price { gap: 1rem; font-weight: 600; justify-content: left; svg { width: 1.5em; } } .date { justify-content: right; } @media screen and (max-width: 768px) { .price { gap: 0.5rem; } .date { width: 100%; justify-content: left; color: #7b7b7b; margin-left: 2.5rem; } }\\`;const A_142 = styled.div\\` display: flex; align-items: center; gap: 1rem; flex: 1; max-width: 100%; gap: 1rem; .profile-image { width: 24px; height: 24px; display: flex !important; } .funding-src { display: flex; flex-direction: column; .pot-name { color: inherit; font-weight: inherit; display: none; } a { color: #292929; transition: 300ms; font-weight: 600; :hover { text-decoration: none; color: #dd3345; } } .type { color: #7b7b7b; } } @media screen and (max-width: 768px) { .funding-src .type { display: none; } .funding-src .pot-name { display: inline-block; } }\\`;const A_143 = styled.div\\` display: flex; align-items: center; background: #f6f5f3; position: relative; svg { width: 18px; left: 1rem; top: 50%; transform: translateY(-50%); position: absolute; pointer-events: none; } input { width: 100%; height: 100%; padding: 1rem; padding-left: 50px; border: none; background: transparent; :focus { outline: none; } }\\`;const A_144 = styled.div\\` display: flex; flex-wrap: wrap; margin: 24px 0; align-items: center; .item { display: flex; height: fit-content; gap: 8px; padding-right: 1rem; align-items: center; :nth-child(2) { border-right: 1px solid #7b7b7b; border-left: 1px solid #7b7b7b; padding-left: 1rem; } :nth-child(3) { padding-left: 1rem; } .item-value { font-weight: 600; } @media screen and (max-width: 768px) { display: none; } } .dropdown { margin-left: auto; @media screen and (max-width: 480px) { margin-right: auto; margin-left: 0; } }\\`;const A_145 = styled.div\\` display: none; justify-content: space-between; width: 100%; div { display: flex; align-items: center; font-weight: 500; cursor: pointer; gap: 8px; color: #7b7b7b; svg { transition: rotate 300ms; } &.active { color: #292929; } } @media screen and (max-width: 768px) { display: flex; }\\`;const A_146 = styled.div\\` display: flex; gap: 10px; align-items: center; .label { font-weight: 500; } .count { display: flex; width: \\${({ digit}) => 24 + (digit - 1) * 6}px; height: \\${({ digit}) => 24 + (digit - 1) * 6}px; align-items: center; justify-content: center; border-radius: 50%; background: #ebebeb; }\\`;const A_147 = styled.img\\` width: 21px; height: 21px;\\`; const getTimePassed = (timestamp, abbreviate) => { const currentTimestamp = new Date().getTime(); const timePassed = currentTimestamp - timestamp; const secondsPassed = Math.floor(timePassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); let time = \"0\"; if (daysPassed > 0) { time = !abbreviate ? \\`\\${daysPassed} day\\${daysPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${daysPassed}d\\`; } else if (hoursPassed > 0) { time = !abbreviate ? \\`\\${hoursPassed} hour\\${hoursPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${hoursPassed}h\\`; } else if (minutesPassed > 0) { time = !abbreviate ? \\`\\${minutesPassed} minute\\${minutesPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${minutesPassed}m\\`; } else { time = !abbreviate ? \\`\\${secondsPassed} second\\${secondsPassed === 1 ? \"\" : \"s\"}\\` : \\`\\${secondsPassed}s\\`; } return time;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const { donations: donations, directDonations: directDonations, matchingRoundDonations: matchingRoundDonations, potPayouts: potPayouts, sponsorships: sponsorships, projectId: projectId, totalDonationAmountNear: totalDonationAmountNear, uniqueDonors: uniqueDonors, totalMatched: totalMatched } = props; const [filter, setFilter] = useState({ date: false, price: false }); const [currentFilter, setCurrentFilter] = useState(\"date\"); const [sort, setSort] = useState(\"all\"); const [currentPage, setCurrentPage] = useState(1); const [totalDonations, setTotalDonations] = useState(donations); const [filteredDonations, setFilteredDonations] = useState(donations); const [search, setSearch] = useState(\"\"); const perPage = 30; useEffect(() => { setTotalDonations(donations); setFilteredDonations(donations); }, [donations]); const sortList = { all: { label: \"All donations\", val: \"all\", count: donations?.length }, direct: { label: \"Direct donations\", val: \"direct\", count: directDonations?.length }, matched: { label: \"Matched donations\", val: \"matched\", count: matchingRoundDonations?.length }, ...(projectId ? { payout: { label: \"Matching pool allocations\", val: \"payout\", count: potPayouts?.length } } : { sponsorship: { label: \"Sponsorships\", val: \"sponsorship\", count: sponsorships?.length } }) }; const searchDonations = (searchTerm) => { const filteredApplications = totalDonations.filter((item) => { const searchIn = [item.pot_name || \"\", item.recipient_id || \"\", item.project_id || \"\", item.donor_id || \"\", item.pot_id || \"\"]; return searchIn.some((item) => item.toLowerCase().includes(searchTerm.toLowerCase())); }); return filteredApplications; }; const getDate = (donation) => donation.donated_at_ms || donation.donated_at; const sortDonation = (type) => { setCurrentFilter(type); const sort = !filter[type]; setFilter({ ...filter, [type]: sort }); if (type === \"price\") { const sortedDonations = filteredDonations.sort((a, b) => sort ? b.total_amount - a.total_amount : a.total_amount - b.total_amount); setFilteredDonations(sortedDonations); } else if (type === \"date\") { const sortedDonations = filteredDonations.sort((a, b) => { return sort ? getDate(a) - getDate(b) : getDate(b) - getDate(a); }); setFilteredDonations(sortedDonations); } }; const filterDonations = (sortVal) => { const displayedDonations = searchDonations(search); let filtered; if (sortVal && sortVal !== \"all\") { filtered = displayedDonations.filter((donation) => { return sortList[donation.type].val === sortVal; }); return filtered; } else { return displayedDonations; } }; const getName = (donation) => { switch (donation.type) { case \"direct\": return projectId ? donation.donor_id : donation.recipient_id; case \"sponsorship\": return donation.pot_name; case \"payout\": return donation.pot_name; case \"matched\": return projectId ? donation.donor_id : donation.project_id; default: return projectId ? donation.donor_id : donation.recipient_id; } }; const stats = { ...(totalDonationAmountNear ? { Donated: <> {totalDonationAmountNear}N{A_236 && <span>~\\${(totalDonationAmountNear * A_236).toFixed(2)}</span>} </> } : {}), ...(uniqueDonors ? { [\\`Unique donor\\${uniqueDonors === 1 ? \"\" : \"s\"}\\`]: uniqueDonors } : {}), ...(uniqueDonors ? { \"Total Matched\": totalMatched + \"N\" } : {}) }; const NearIcon = (props) => <svg {...props} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" id=\"near-logo\"> <rect width=\"24\" height=\"24\" rx=\"12\" fill=\"#CECECE\" /> <path d=\"M15.616 6.61333L13.1121 10.3333C12.939 10.5867 13.2719 10.8933 13.5117 10.68L15.9756 8.53333C16.0422 8.48 16.1354 8.52 16.1354 8.61333V15.32C16.1354 15.4133 16.0155 15.4533 15.9623 15.3867L8.50388 6.45333C8.26415 6.16 7.91787 6 7.53163 6H7.26526C6.5727 6 6 6.57333 6 7.28V16.72C6 17.4267 6.5727 18 7.27858 18C7.71809 18 8.13097 17.7733 8.3707 17.3867L10.8746 13.6667C11.0477 13.4133 10.7148 13.1067 10.475 13.32L8.0111 15.4533C7.94451 15.5067 7.85128 15.4667 7.85128 15.3733V8.68C7.85128 8.58667 7.97114 8.54667 8.02442 8.61333L15.4828 17.5467C15.7225 17.84 16.0821 18 16.4551 18H16.7214C17.4273 18 18 17.4267 18 16.72V7.28C18 6.57333 17.4273 6 16.7214 6C16.2686 6 15.8557 6.22667 15.616 6.61333Z\" fill=\"black\" /> </svg>; const PotIcon = (potIconProps) => <svg {...potIconProps} width=\"20\" height=\"21\" viewBox=\"0 0 20 21\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M10 3C10.5523 3 11 2.55228 11 2C11 1.44772 10.5523 1 10 1C9.44772 1 9 1.44772 9 2C9 2.55228 9.44772 3 10 3ZM12 2C12 2.37912 11.8945 2.7336 11.7113 3.03569C14.6721 3.33449 17.0882 5.47841 17.7921 8.3C17.9279 8.84425 18 9.41371 18 10H16.3H3.7H2C2 9.41371 2.07208 8.84425 2.20786 8.3C2.9118 5.47841 5.3279 3.33449 8.28871 3.03569C8.10549 2.7336 8 2.37912 8 2C8 0.895431 8.89543 0 10 0C11.1046 0 12 0.895431 12 2ZM9 4.7C6.66751 4.7 4.68694 6.20674 3.97852 8.3H16.0215C15.3131 6.20674 13.3325 4.7 11 4.7H9ZM0 11H2H4H16H18H20V13H18V19C18 20.1046 17.1046 21 16 21H4C2.89543 21 2 20.1046 2 19V13H0V11ZM4 19V13H16V19H4Z\" fill=\"#7B7B7B\" /> </svg>; const Arrow = (props) => <svg {...props} style={{ rotate: !props.active ? \"0deg\" : \"180deg\" }} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 6L1.0575 7.0575L5.25 2.8725V12H6.75V2.8725L10.935 7.065L12 6L6 0L0 6Z\" fill=\"#7B7B7B\" /> </svg>; const [ftMetadata, setFtMetadata] = useState({}); useEffect(() => { const metadata = {}; const ftIds = totalDonations.reduce((acc, donation) => { if (donation.ft_id && donation.ft_id !== \"near\") { acc.add(donation.ft_id); } return acc; }, new Set()); ftIds.forEach((ftId) => { Near.asyncView(ftId, \"ft_metadata\", {}).then((ftMetadata) => { metadata[ftId] = ftMetadata; if (Object.keys(metadata).length === ftIds.size) { setFtMetadata(metadata); } }).catch((e) => { console.error(\"error getting ft metadata: \", e); }); }); }, []); return <A_140> {projectId && <A_141>Potlock Funding</A_141>} <A_144> {Object.keys(stats).map((k) => <div className=\"item\"> <div className=\"item-value\">{stats[k]}</div> <div className=\"item-label\">{k}</div> </div>)} <div className=\"dropdown\"> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ ...{ sortVal: <A_146 digit={sortList[sort].count.toString().length}> <div className=\"label\">{sortList[sort].label}</div> <div className=\"count\">{sortList[sort].count}</div> </A_146>, showCount: true, sortList: Object.values(sortList), FilterMenuCustomStyle: \\`left:auto; right:0;\\`, handleSortChange: ({ val }) => {const filtered = filterDonations(val);setFilteredDonations(filtered);setSort(val);} }, ...props } }} /> </div> </A_144> <A_145> <div onClick={() => sortDonation(\"date\")} className={\\`\\${currentFilter === \"date\" ? \"active\" : \"\"}\\`}> Sort Date {currentFilter === \"date\" && <Arrow active={!filter.date} />} </div> <div onClick={() => sortDonation(\"price\")} className={\\`\\${currentFilter === \"price\" ? \"active\" : \"\"}\\`}> Sort Amount {currentFilter === \"price\" && <Arrow active={filter.price} />} </div> </A_145> <PotlockFundingContainer> <div className=\"header\"> <div className=\"funding tab\">{projectId ? \"Funding Source\" : \"Project Name\"}</div> <div className=\"tab sort\" onClick={() => sortDonation(\"price\")}> Amount {currentFilter === \"price\" && <Arrow active={filter.price} />} </div> <div className=\"tab sort date\" onClick={() => sortDonation(\"date\")}> Date {currentFilter === \"date\" && <Arrow active={!filter.date} />} </div> </div> <A_143> <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.7549 14.2549H14.9649L14.6849 13.9849C15.6649 12.8449 16.2549 11.3649 16.2549 9.75488C16.2549 6.16488 13.3449 3.25488 9.75488 3.25488C6.16488 3.25488 3.25488 6.16488 3.25488 9.75488C3.25488 13.3449 6.16488 16.2549 9.75488 16.2549C11.3649 16.2549 12.8449 15.6649 13.9849 14.6849L14.2549 14.9649V15.7549L19.2549 20.7449L20.7449 19.2549L15.7549 14.2549ZM9.75488 14.2549C7.26488 14.2549 5.25488 12.2449 5.25488 9.75488C5.25488 7.26488 7.26488 5.25488 9.75488 5.25488C12.2449 5.25488 14.2549 7.26488 14.2549 9.75488C14.2549 12.2449 12.2449 14.2549 9.75488 14.2549Z\" fill=\"#C7C7C7\" /> </svg> <input className=\"\" placeholder=\"Search funding\" onChange={(e) => { if (currentPage !== 1) setCurrentPage(1); setSearch(e.target.value); const filtered = searchDonations(e.target.value); setFilteredDonations(filtered); }} type=\"text\" /> </A_143> {filteredDonations.slice((currentPage - 1) * perPage, currentPage * perPage).map((donation) => { const { donor_id, total_amount, amount, pot_id, recipient_id, project_id, paid_at, base_currency, ft_id, type, donated_at, donated_at_ms } = donation; const ftId = ft_id || base_currency; const donationAmount = parseFloat(Big(total_amount || amount).div(Big(10).pow(ftId === \"near\" ? 24 : ftMetadata[ftId]?.decimals || 24)).toFixed(2)); const addTrailingZeros = (number) => { if (number < 100 && number >= 0.1) return number.toFixed(1); return number; }; const isPot = type === \"payout\" || type === \"sponsorship\"; const url = isPot ? \\`?tab=pot&potId=\\${pot_id}\\` : projectId ? \\`?tab=profile&accountId=\\${donor_id}\\` : \\`?tab=project&projectId=\\${project_id || recipient_id}\\`; const name = _address(getName(donation), 15); return <div className=\"funding-row\"> <A_142> {isPot ? <PotIcon className=\"profile-image\" /> : <ProfileImage accountId={projectId ? donor_id : recipient_id || project_id} fallbackUrl=\"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\" />} <div className=\"funding-src\"> <a href={hrefWithParams(url)} target=\"_blank\"> {isPot && <span className=\"pot-name\"> {projectId ? \"Matching Pool\" : \"Sponsor\"} :</span>} {name} </a> <div className=\"type\">{sortList[type].label?.slice(0, -1)}</div> </div> </A_142> <div className=\"price tab\"> <div className=\"near-icon\"> {ftId === \"near\" ? <NearIcon /> : <A_147 src={ftMetadata[ftId]?.icon} />} </div> {addTrailingZeros(donationAmount)} </div> <div className=\"tab date\">{getTimePassed(donated_at_ms || donated_at || paid_at, true)} ago</div> </div>; })} {filteredDonations.length === 0 && <div className=\"funding-row\">No Donations</div>} </PotlockFundingContainer> <Widget loading=\" \" code={props.alem.componentsCode.Pagination} props={{ ...{ ...{ onPageChange: (page) => {setCurrentPage(page);}, data: filteredDonations, currentPage, perPage: perPage, bgColor: \"#7B7B7B\" }, ...props } }} /> </A_140>; `, ExternalFunding: ` const A_149 = styled.div\\` display: flex; flex-direction: column; > .description { margin-top: 0.5rem; margin-bottom: 1.5rem; } .external-funding { display: flex; flex-direction: column; width: 100%; border-radius: 6px; border: 1px solid #7b7b7b; background: #fff; transition: all 300ms ease-in-out; &.hidden { visibility: hidden; height: 0; opacity: 0; transform: translateY(100px); } .header { border-bottom: 0.5px solid #7b7b7b; padding: 10px 20px; div { font-weight: 600; } @media screen and (max-width: 920px) { div { display: none; } .funding { display: block; } } } .funding-row { padding: 20px; flex-wrap: wrap; position: relative; } .header, .funding-row { display: flex; justify-content: space-between; gap: 2rem; font-size: 14px; div { text-transform: capitalize; width: 100%; max-width: 120px; text-align: left; &:last-of-type { justify-content: right; } } .investor { display: flex; flex-direction: column; div:first-of-type { font-weight: 600; } } .mobile-date { display: none; color: #7b7b7b; } .amount { font-weight: 600; display: flex; align-items: center; gap: 1rem; svg { display: none; rotate: 180deg; } } .description { flex: 1; max-width: 100%; } .toggle-check { position: absolute; width: 100%; left: 0; top: 0; height: 100%; opacity: 0; display: none; } .date { display: flex; align-items: center; } @media screen and (max-width: 920px) { gap: 8px; div { max-width: initial; } .date { display: none; } .mobile-date { display: block; } .description { flex-basis: 100%; order: 1; max-height: 0; overflow: hidden; transition: max-height 200ms ease-in-out; } div { width: fit-content; } .amount svg { display: block; } .toggle-check { display: block; } .toggle-check:checked + .description { max-height: 200px; } .toggle-check:checked ~ div svg { rotate: 0deg; } } } @media screen and (max-width: 920px) { .header div:not(:first-of-type) { display: none; } } }\\`;const A_150 = styled.div\\` font-size: 24px; font-weight: 600; display: flex; align-items: center; gap: 16px;\\`;const A_151 = styled.svg\\` width: 12px; transition: all 200ms;\\`; const {externalFunding: externalFunding} = props; const [showFundingTable, setShowFundingTable] = useState(true); const ArrowDown = arrowProps => <A_151 {...props} style={{ rotate: arrowProps.showFundingTable ? \"\" : \"180deg\" }} viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6 0.294983L0 6.29498L1.41 7.70498L6 3.12498L10.59 7.70498L12 6.29498L6 0.294983Z\" fill=\"#151A23\" /> </A_151>; const externalTableTabs = [\"funding Source\", \"description\", \"date\", \"amount\"]; return <A_149> <A_150 style={{ cursor: \"pointer\" }} onClick={() => setShowFundingTable(!showFundingTable)}> External Funding <ArrowDown showFundingTable={showFundingTable} /> </A_150> <div className=\"description\">This not related to the funding generated on this platform</div> <div className={\\` external-funding \\${showFundingTable ? \"\" : \"hidden\"} \\`}> <div className=\"header\"> {externalTableTabs.map(tab => <div className={tab} key={tab}> {tab} </div>)} </div> {externalFunding.map(({ investorName, description, date, amountReceived, denomination }) => <div className=\"funding-row\"> <div className=\"investor\"> <div>{investorName}</div> {date && <div className=\"date-mobile\">{date}</div>} </div> <input type=\"checkbox\" className=\"toggle-check\" /> <div className=\"description\">{description}</div> <div className=\"date\">{date ?? \"No specified date\"}</div> <div className=\"amount\"> {parseFloat(amountReceived).toLocaleString() + \" \" + denomination}{\" \"} <ArrowDown showFundingTable={showFundingTable} /> </div> </div>)} </div> </A_149>; `, FundingRaised: ` const A_137 = styled.div\\` display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 80px 1rem; border-radius: 12px; gap: 24px; background: #f6f5f3; .text { font-family: \"Lora\"; font-size: 22px; font-style: italic; font-weight: 500; color: #292929; } img { width: 100%; max-width: 604px; } @media screen and (max-width: 768px) { padding: 1.5rem 1rem; .text { font-size: 16px; } }\\`;const A_138 = styled.div\\` display: flex; flex-direction: column;\\`;const A_139 = styled.div\\` width: 100%; background: #c7c7c7; height: 1px; margin: 3rem 0;\\`; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const componentProps = props; const { donations, potPayouts, profile } = componentProps; const externalFunding = profile.plFundingSources ? JSON.parse(profile.plFundingSources) : []; const [totalDonationAmountNear, uniqueDonors, totalMatched] = useMemo(() => { if (donations) { let totalNear = Big(0); const uniqueDonors = [...new Set(donations.map((donation) => donation.donor_id))]; donations.forEach((donation) => { if (donation.ft_id === \"near\" || donation.base_currency === \"near\") { totalNear = totalNear.plus(Big(donation.total_amount || donation.amount)); } }); const totalDonationAmountNear = constants.SUPPORTED_FTS[\"NEAR\"].fromIndivisible(totalNear.toString()); let totalMatched = Big(0); if (potPayouts) { potPayouts.forEach((payout) => { totalMatched = totalMatched.plus(Big(payout.amount)); }); } totalMatched = constants.SUPPORTED_FTS[\"NEAR\"].fromIndivisible(totalMatched.toString()); return [totalDonationAmountNear, uniqueDonors?.length, totalMatched]; } return [\"\", 0, 0]; }, [donations, potPayouts]); return externalFunding.length === 0 && donations.length === 0 ? <A_137> <img src=\"https://ipfs.near.social/ipfs/bafkreif5awokaip363zk6zqrsgmpehs6rap3w67engc4lxdlk4x6iystru\" alt=\"pots\" /> <div className=\"text\">No funds have been raised for this project.</div> </A_137> : <A_138> {externalFunding.length > 0 && <Widget loading=\" \" code={props.alem.componentsCode.ExternalFunding} props={{ ...{ externalFunding: externalFunding, ...props } }} />} {externalFunding.length > 0 && donations.length > 0 && <A_139 />} {donations.length > 0 && <Widget loading=\" \" code={props.alem.componentsCode.PotlockFunding} props={{ ...{ ...{ totalDonationAmountNear, uniqueDonors, totalMatched }, ...props } }} />} </A_138>; `, Compose: ` const compProps = props; if (!context.accountId) { return \"\"; } const indexKey = compProps.indexKey ?? \"main\"; const draftKey = compProps.indexKey ?? \"draft\"; const draft = Storage.privateGet(draftKey) || compProps.initialText; const groupId = compProps.groupId; if (draft === null) { return \"\"; } const [initialText] = useState(draft); const composeData = () => { const data = { post: { main: JSON.stringify(Object.assign({ groupId }, state.content)) }, index: { post: JSON.stringify({ key: indexKey, value: { type: \"md\" } }) } }; const item = { type: \"social\", path: \\`\\${context.accountId}/post/main\\` }; const notifications = state.extractMentionNotifications(state.content.text, item); const hashtags = state.extractHashtags(state.content.text); return data; }; State.init({ onChange: ({ content }) => { State.update({ content }); Storage.privateSet(draftKey, content.text || \"\"); } }); return <> <div style={{ margin: \"0 -12px\" }}> <Widget src=\"mob.near/widget/MainPage.N.Common.Compose\" props={{ placeholder: \"What's happening?\", onChange: state.onChange, initialText, onHelper: ({ extractMentionNotifications, extractHashtags }) => { State.update({ extractMentionNotifications, extractHashtags }); }, composeButton: onCompose => <CommitButton disabled={!state.content} force className=\"post-btn\" data={composeData} onCommit={() => { onCompose(); }}> Post </CommitButton> }} /> </div> {state.content && <Widget key=\"post-preview\" src=\"mob.near/widget/MainPage.N.Post\" props={{ accountId: context.accountId, content: state.content, blockHeight: \"now\" }} />} </>; `, About: ` const getTeamMembersFromSocialProfileData = profileData => { if (!profileData) return []; const team = profileData.plTeam ? JSON.parse(profileData.plTeam) : profileData.team ? Object.entries(profileData.team).filter(([_, v]) => v !== null).map(([k, _]) => k) : []; return team;}; const A_153 = styled.div\\` display: flex; flex-direction: row; align-items: flex-start; justify-content: flex-start; @media screen and (max-width: 768px) { flex-direction: column; }\\`;const A_154 = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 600;\\`;const TeamMembersContainer = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 2rem; flex-wrap: wrap;\\`;const TeamMemberItem = styled.a\\` display: flex; flex-direction: column; justify-content: flex-start; gap: 8px; cursor: pointer; :hover { text-decoration: none; .profile-image img { filter: grayscale(0%); } } .profile-image { width: 180px; height: 180px; img { width: 100%; height: 100%; border-radius: 6px; filter: grayscale(100%); transition: 300ms ease-in-out; } } @media screen and (max-width: 768px) { .profile-image { width: 160px; height: 160px; } }\\`;const TeamMemberAccountId = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 400;\\`;const imageWidthPx = 129;const Col1 = styled.div\\` display: flex; width: 30%; margin-bottom: 1rem; @media screen and (max-width: 768px) { width: 100%; }\\`;const Col2 = styled.div\\` display: flex; width: 70%; @media screen and (max-width: 768px) { width: 100%; }\\`; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Team = (__props__) => { let { team } = __props__; team = team.filter((item) => item.length > 3); return <A_153> <Col1> <A_154>Team members</A_154> </Col1> <Col2> <TeamMembersContainer> {!team.length ? <div>No team members to display</div> : <TeamMembersContainer> {!team.length ? <div>No team members to display</div> : team.map((teamMember) => { const match = teamMember.match(/.near/i); if (match && match.length > 0) { return <TeamMemberItem href={hrefWithParams(\\`?tab=profile&accountId=\\${teamMember}\\`)} target=\"_blank\"> <ProfileImage {...{ accountId: teamMember, imageClassName: \"\", style: {}, thumbnail: false, tooltip: true }} /> <TeamMemberAccountId>@{teamMember}</TeamMemberAccountId> </TeamMemberItem>; } })} </TeamMembersContainer>} </TeamMembersContainer> </Col2> </A_153>;}; const AboutItem = ({ title, text}) => { const Container = styled.div\\` display: flex; flex-direction: row; align-items: flex-start; justify-content: flex-start; @media screen and (max-width: 768px) { flex-direction: column; align-items: flex-start; } \\`; const Col1 = styled.div\\` display: flex; width: 30%; margin-bottom: 1rem; @media screen and (max-width: 768px) { width: 100%; } \\`; const Col2 = styled.div\\` display: flex; flex-direction: column; width: 70%; p { margin: 0; } @media screen and (max-width: 768px) { width: 100%; } \\`; const Title = styled.div\\` color: #2e2e2e; font-size: 16px; font-weight: 600; \\`; return <Container> <Col1> <Title>{title}</Title> </Col1> <Col2>{text}</Col2> </Container>;}; const A_155 = styled.div\\` max-width: 920px; display: flex; flex-direction: column; gap: 48px;\\`;const A_156 = styled.div\\` color: #2e2e2e; font-size: 40px; font-weight: 500; font-family: \"Lora\"; @media screen and (max-width: 768px) { font-size: 32px; }\\`;const A_157 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 24px;\\`;const GithubWrapper = styled.div\\` display: flex; flex-direction: column; gap: 1rem; a { transition: all 300ms; display: flex; align-items: center; gap: 1rem; svg { transition: all 300ms; } .url { color: #292929; width: fit-content; } :hover { text-decoration: none; transform: translateX(4px); svg { rotate: 45deg; } } }\\`;const SmartContractWrapper = styled.div\\` display: flex; flex-direction: column; gap: 1rem; .contract { display: flex; align-items: center; gap: 1rem; .text { display: flex; flex-direction: column; .chain { font-size: 14px; color: #7b7b7b; } } }\\`; const { projectId: projectId, accountId: accountId } = props; const profile = Social.getr(\\`\\${projectId}/profile\\`); if (!profile) { return \"\"; } const { name, description, plPublicGoodReason } = profile; const smartContracts = profile.plSmartContracts ? Object.entries(JSON.parse(profile.plSmartContracts)).reduce((accumulator, [chain, contracts]) => { const contractsForChain = Object.keys(contracts).map((contractAddress) => { return [chain, contractAddress]; }); return accumulator.concat(contractsForChain); }, []) : []; const githubRepos = profile.plGithubRepos ? JSON.parse(profile.plGithubRepos) : []; const Github = () => githubRepos.length > 0 ? <GithubWrapper> {githubRepos.map((url) => <a href={url} target=\"_blank\"> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1.5 0.5V2.5H10.09L0.5 12.09L1.91 13.5L11.5 3.91V12.5H13.5V0.5H1.5Z\" fill=\"#7B7B7B\" /> </svg> <div className=\"url\">{url}</div> </a>)} </GithubWrapper> : \"None provided\"; const SmartContracts = () => smartContracts.length > 0 ? <SmartContractWrapper> {smartContracts.map(([chain, contract]) => { return <div className=\"contract\"> <Widget loading=\" \" code={props.alem.componentsCode.CopyIcon} props={{ ...{ textToCopy: contract, ...props } }} /> <div className=\"text\"> <div className=\"address\">{contract}</div> <div className=\"chain\">{chain}</div> </div> </div>; })} </SmartContractWrapper> : \"None provided\"; const markdown = <Markdown text={description} />; const github = <Github />; const smartContractsItems = <SmartContracts />; const team = useMemo(() => { return <Team team={getTeamMembersFromSocialProfileData(profile)} />; }, []); return <A_155> <A_157> <A_156>About {name}</A_156> </A_157> <AboutItem title=\"Overview\" text={markdown} /> <AboutItem title=\"Why we are a public good\" text={plPublicGoodReason || \"None provided\"} /> {team} <AboutItem title=\"Github repo(s)\" text={github} /> <AboutItem title=\"Smart contracts\" text={smartContractsItems} /> </A_155>; `, FollowersList: ` const FollowButton = __props__ => { if (!__props__.accountId || !context.accountId || context.accountId === __props__.accountId) { return \"\"; } const followEdge = Social.keys(\\`\\${context.accountId}/graph/follow/\\${__props__.accountId}\\`, undefined, { values_only: true }); const inverseEdge = Social.keys(\\`\\${__props__.accountId}/graph/follow/\\${context.accountId}\\`, undefined, { values_only: true }); const loading = followEdge === null || inverseEdge === null; const follow = followEdge && Object.keys(followEdge).length; const inverse = inverseEdge && Object.keys(inverseEdge).length; const type = follow ? \"unfollow\" : \"follow\"; const data = { graph: { follow: { [__props__.accountId]: follow ? null : \"\" } }, index: { graph: JSON.stringify({ key: \"follow\", value: { type, accountId: __props__.accountId } }), notify: JSON.stringify({ key: __props__.accountId, value: { type } }) } }; return <CommitButton disabled={loading} className={\\`btn \\${loading || follow ? \"btn-outline-dark\" : \"btn-primary\"} rounded-5\\`} data={data}> {loading ? \"Loading\" : follow ? \"Following\" : inverse ? \"Follow back\" : \"Follow\"} </CommitButton>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const Preview = __props__ => { const accountId = __props__.accountId ?? context.accountId; const profile = Social.getr(\\`\\${accountId}/profile\\`); const name = profile.name; return <div className=\"profile d-inline-block\"> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${accountId}\\`)} className=\"text-decoration-none link-dark\"> <ProfileImage {...{ profile, accountId, className: \"float-start d-inline-block me-2\" }} /> <div className=\"profile-info d-inline-block\" style={{ maxWidth: \"16em\" }}> <div className=\"profile-name text-truncate\">{name || \"No-name profile\"}</div> <div className=\"profile-links d-flex\"> <div className=\"d-inline-block profile-account text-secondary text-truncate\">@{accountId}</div> </div> </div> </a> </div>;}; const A_159 = styled.div\\` display: flex; flex-direction: column; gap: 1rem; .profile-row { display: flex; width: 100%; justify-content: space-between; align-items: center; } .btn-primary { background-color: #dd3345; border: none; transition: all 300ms; :hover { opacity: 0.8; } }\\`; const {accountId: accountId, nav: nav} = props; const [followers, setFollowers] = useState(null); if (!accountId) { return \"\"; } const url = nav === \"followers\" ? \\`*/graph/follow/\\${accountId}\\` : \\`\\${accountId}/graph/follow/*\\`; let followersKeys = Social.keys(url, \"final\", { return_type: \"BlockHeight\", values_only: true }); if (!followers && followersKeys) { let followers = []; if (nav === \"followers\") { followers = Object.entries(followersKeys || {}); followers.sort((a, b) => b.graph.follow[accountId][1] - a.graph.follow[accountId][1]); } else { followers = Object.entries(followersKeys[accountId].graph.follow || {}); followers.sort((a, b) => b[1] - a[1]); } setFollowers(followers); } return <A_159> {followers === null ? \"Loading...\" : followers ? followers.map(([accountId], i) => <div className=\"profile-row\" key={i}> <div className=\"me-4\"> <Preview {...{ ...props, accountId }} /> </div> <div> <FollowButton accountId={accountId} /> </div> </div>) : \\`No \\${nav}\\`} </A_159>; `, DonationsInfo: ` const A_162 = styled.div\\` display: flex; flex-direction: column; padding: 24px; gap: 24px; border-radius: 10px; border: 1px solid #f4b37d; border-bottom-width: 3px; background: #fef6ee; margin-left: auto; height: fit-content; .donations-info { display: flex; gap: 4px; flex-direction: column; .amount { font-weight: 500; font-size: 2.5rem; line-height: 1; font-family: \"Lora\"; } .donors { font-size: 14px; span { font-weight: 600; } } } .btn-wrapper { display: flex; gap: 1.5rem; justify-content: space-between; > div, button { padding: 10px 0; width: 160px; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; } } @media only screen and (max-width: 480px) { width: 100%; .donations-info .amount { font-size: 2rem; } .btn-wrapper { > div, button { width: 100%; } } }\\`; const FollowContainer = styled.div\\` position: relative; cursor: pointer; border-radius: 6px; border: 1px solid #dd3345; font-size: 14px; font-weight: 600; color: #dd3345; word-wrap: break-word; transition: all 300ms; &::before { background: #dd3345; position: absolute; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; content: \"Unfollow\"; color: white; opacity: 0; transition: all 300ms; } :hover { background: #dd3345; color: white; \\${__props__ => __props__.buttonText === \"Following\" ? \\` ::before { opacity: 1; } \\` : \"\"} }\\`; const A_161 = ({ accountId, classname}) => { if (!accountId || !context.accountId || context.accountId === accountId) { return \"\"; } const followEdge = Social.keys(\\`\\${context.accountId}/graph/follow/\\${accountId}\\`, undefined, { values_only: true }); const inverseEdge = Social.keys(\\`\\${accountId}/graph/follow/\\${context.accountId}\\`, undefined, { values_only: true }); const loading = followEdge === null || inverseEdge === null; const follow = followEdge && Object.keys(followEdge).length; const inverse = inverseEdge && Object.keys(inverseEdge).length; const type = follow ? \"unfollow\" : \"follow\"; const socialArgs = { data: { [context.accountId]: { graph: { follow: { [accountId]: follow ? null : \"\" } }, index: { graph: JSON.stringify({ key: \"follow\", value: { type, accountId: accountId } }), notify: JSON.stringify({ key: accountId, value: { type } }) } } } }; const buttonText = loading ? \"Loading\" : follow ? \"Following\" : inverse ? \"Follow back\" : \"Follow\"; return <FollowContainer className={classname || \"\"} onClick={() => { const transactions = [{ contractName: \"social.near\", methodName: \"set\", deposit: Big(JSON.stringify(socialArgs).length * 0.00003).mul(Big(10).pow(24)), args: socialArgs }]; Near.call(transactions); }}> {buttonText} </FollowContainer>;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsdWithFallback = (amountNear, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas((amountNear * A_236).toFixed(2)) : formatWithCommas(amountNear.toString()) + (abbreviate ? \"N\" : \" NEAR\");}; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useDonationModal = () => useContext(\"donation-modal\"); const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const {projectId: projectId, donations: donations} = props; const { potId } = props.alem.useParams(); const { setDonationModalProps } = useDonationModal(); const [totalDonationAmountNear, uniqueDonors] = useMemo(() => { let totalNear = Big(0); const uniqueDonors = [...new Set(donations.map(donation => donation.donor_id))]; donations.forEach(donation => { if (donation.ft_id === \"near\" || donation.base_currency === \"near\") { totalNear = totalNear.plus(Big(donation.total_amount || donation.amount)); } }); const totalDonationAmountNear = constants.SUPPORTED_FTS[\"NEAR\"].fromIndivisible(totalNear.toString()); return [totalDonationAmountNear, uniqueDonors?.length]; }, [donations]); return <A_162> <div className=\"donations-info\"> <div className=\"amount\">{nearToUsdWithFallback(Number(totalDonationAmountNear))}</div> <div className=\"donors\"> Raised from <span> {uniqueDonors}</span> {uniqueDonors === 1 ? \"donor\" : \"donors\"} </div> </div> <div className=\"btn-wrapper\"> <Button type=\"primary\" text=\"Donate\" onClick={() => setDonationModalProps({ projectId, potId })} /> <A_161 accountId={projectId} /> </div> </A_162>; `, CopyIcon: ` const {textToCopy: textToCopy, customStyle: customStyle} = props; const [copied, setCopid] = useState(false); const Icon = styled.svg\\` cursor: pointer; height: 20px; transition: scale 200ms ease-in-out; \\${customStyle || \"\"} :hover { scale: 1.1; } \\`; return copied ? <svg width=\"18\" height=\"14\" viewBox=\"0 0 18 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M5.8002 10.9L1.6002 6.69999L0.200195 8.09999L5.8002 13.7L17.8002 1.69999L16.4002 0.299988L5.8002 10.9Z\" fill=\"#151A23\" /> </svg> : <Icon onClick={() => { clipboard.writeText(textToCopy); setCopid(true); setTimeout(() => { setCopid(false); }, 2000); }} viewBox=\"0 0 18 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M15.5 0H6.5C5.4 0 4.5 0.9 4.5 2V14C4.5 15.1 5.4 16 6.5 16H15.5C16.6 16 17.5 15.1 17.5 14V2C17.5 0.9 16.6 0 15.5 0ZM15.5 14H6.5V2H15.5V14ZM0.5 13V11H2.5V13H0.5ZM0.5 7.5H2.5V9.5H0.5V7.5ZM7.5 18H9.5V20H7.5V18ZM0.5 16.5V14.5H2.5V16.5H0.5ZM2.5 20C1.4 20 0.5 19.1 0.5 18H2.5V20ZM6 20H4V18H6V20ZM11 20V18H13C13 19.1 12.1 20 11 20ZM2.5 4V6H0.5C0.5 4.9 1.4 4 2.5 4Z\" fill=\"#7B7B7B\" /> </Icon>; `, BodyHeader: ` const CheckIcon = __props__ => <svg {...__props__} viewBox=\"0 0 18 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M5.8002 10.9L1.6002 6.69999L0.200195 8.09999L5.8002 13.7L17.8002 1.69999L16.4002 0.299988L5.8002 10.9Z\" fill=\"#151A23\" /> </svg>; const ReferrerIcon = __props__ => <svg {...__props__} viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M12.375 9.5625C14.6925 7.455 16.875 5.4825 16.875 3.7875C16.875 2.4 15.7875 1.3125 14.4 1.3125C13.62 1.3125 12.8625 1.68 12.375 2.25C11.88 1.68 11.13 1.3125 10.35 1.3125C8.9625 1.3125 7.875 2.4 7.875 3.7875C7.875 5.4825 10.0575 7.455 12.375 9.5625ZM10.35 2.8125C10.68 2.8125 11.0175 2.97 11.235 3.225L12.375 4.5675L13.515 3.225C13.7325 2.97 14.07 2.8125 14.4 2.8125C14.955 2.8125 15.375 3.2325 15.375 3.7875C15.375 4.6275 13.845 6.165 12.375 7.53C10.905 6.165 9.375 4.62 9.375 3.7875C9.375 3.2325 9.795 2.8125 10.35 2.8125Z\" fill=\"#7B7B7B\" /> <path d=\"M14.625 11.8125H13.125C13.125 10.9125 12.5625 10.1025 11.7225 9.7875L7.1025 8.0625H1.125V16.3125H5.625V15.2325L10.875 16.6875L16.875 14.8125V14.0625C16.875 12.8175 15.87 11.8125 14.625 11.8125ZM2.625 14.8125V9.5625H4.125V14.8125H2.625ZM10.8525 15.12L5.625 13.6725V9.5625H6.8325L11.1975 11.19C11.4525 11.2875 11.625 11.535 11.625 11.8125C11.625 11.8125 10.1325 11.775 9.9 11.7L8.115 11.1075L7.6425 12.5325L9.4275 13.125C9.81 13.2525 10.2075 13.32 10.6125 13.32H14.625C14.9175 13.32 15.18 13.4925 15.3 13.74L10.8525 15.12Z\" fill=\"#7B7B7B\" /> </svg>; const FollowContainer = styled.div\\` position: relative; cursor: pointer; border-radius: 6px; border: 1px solid #dd3345; font-size: 14px; font-weight: 600; color: #dd3345; word-wrap: break-word; transition: all 300ms; &::before { background: #dd3345; position: absolute; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; content: \"Unfollow\"; color: white; opacity: 0; transition: all 300ms; } :hover { background: #dd3345; color: white; \\${__props__ => __props__.buttonText === \"Following\" ? \\` ::before { opacity: 1; } \\` : \"\"} }\\`; const A_161 = ({ accountId, classname}) => { if (!accountId || !context.accountId || context.accountId === accountId) { return \"\"; } const followEdge = Social.keys(\\`\\${context.accountId}/graph/follow/\\${accountId}\\`, undefined, { values_only: true }); const inverseEdge = Social.keys(\\`\\${accountId}/graph/follow/\\${context.accountId}\\`, undefined, { values_only: true }); const loading = followEdge === null || inverseEdge === null; const follow = followEdge && Object.keys(followEdge).length; const inverse = inverseEdge && Object.keys(inverseEdge).length; const type = follow ? \"unfollow\" : \"follow\"; const socialArgs = { data: { [context.accountId]: { graph: { follow: { [accountId]: follow ? null : \"\" } }, index: { graph: JSON.stringify({ key: \"follow\", value: { type, accountId: accountId } }), notify: JSON.stringify({ key: accountId, value: { type } }) } } } }; const buttonText = loading ? \"Loading\" : follow ? \"Following\" : inverse ? \"Follow back\" : \"Follow\"; return <FollowContainer className={classname || \"\"} onClick={() => { const transactions = [{ contractName: \"social.near\", methodName: \"set\", deposit: Big(JSON.stringify(socialArgs).length * 0.00003).mul(Big(10).pow(24)), args: socialArgs }]; Near.call(transactions); }}> {buttonText} </FollowContainer>;}; const LinktreeContainer = styled.div\\` display: flex; flex-wrap: wrap; -webkit-box-pack: start; justify-content: flex-start; gap: 1rem;\\`;const LinktreeItemContainer = styled.a\\` display: flex; svg { width: 24px; height: 24px; path, rect { transition: all 300ms ease-in-out; } &#near-logo:hover path { fill: white; } :hover path, :hover rect { fill: #292929; } }\\`;const LinkText = styled.a\\` font-size: 14px; color: gray; font-weight: 400; margin-left: 16px; cursor: \\${__props__ => __props__.disabled ? \"not-allowed\" : \"pointer\"}; &:hover { text-decoration: none; }\\`; const WebsiteSvg = () => <A_163 viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 20C8.63333 20 7.34167 19.7375 6.125 19.2125C4.90833 18.6875 3.84583 17.9708 2.9375 17.0625C2.02917 16.1542 1.3125 15.0917 0.7875 13.875C0.2625 12.6583 0 11.3667 0 10C0 8.61667 0.2625 7.32083 0.7875 6.1125C1.3125 4.90417 2.02917 3.84583 2.9375 2.9375C3.84583 2.02917 4.90833 1.3125 6.125 0.7875C7.34167 0.2625 8.63333 0 10 0C11.3833 0 12.6792 0.2625 13.8875 0.7875C15.0958 1.3125 16.1542 2.02917 17.0625 2.9375C17.9708 3.84583 18.6875 4.90417 19.2125 6.1125C19.7375 7.32083 20 8.61667 20 10C20 11.3667 19.7375 12.6583 19.2125 13.875C18.6875 15.0917 17.9708 16.1542 17.0625 17.0625C16.1542 17.9708 15.0958 18.6875 13.8875 19.2125C12.6792 19.7375 11.3833 20 10 20ZM10 17.95C10.4333 17.35 10.8083 16.725 11.125 16.075C11.4417 15.425 11.7 14.7333 11.9 14H8.1C8.3 14.7333 8.55833 15.425 8.875 16.075C9.19167 16.725 9.56667 17.35 10 17.95ZM7.4 17.55C7.1 17 6.8375 16.4292 6.6125 15.8375C6.3875 15.2458 6.2 14.6333 6.05 14H3.1C3.58333 14.8333 4.1875 15.5583 4.9125 16.175C5.6375 16.7917 6.46667 17.25 7.4 17.55ZM12.6 17.55C13.5333 17.25 14.3625 16.7917 15.0875 16.175C15.8125 15.5583 16.4167 14.8333 16.9 14H13.95C13.8 14.6333 13.6125 15.2458 13.3875 15.8375C13.1625 16.4292 12.9 17 12.6 17.55ZM2.25 12H5.65C5.6 11.6667 5.5625 11.3375 5.5375 11.0125C5.5125 10.6875 5.5 10.35 5.5 10C5.5 9.65 5.5125 9.3125 5.5375 8.9875C5.5625 8.6625 5.6 8.33333 5.65 8H2.25C2.16667 8.33333 2.10417 8.6625 2.0625 8.9875C2.02083 9.3125 2 9.65 2 10C2 10.35 2.02083 10.6875 2.0625 11.0125C2.10417 11.3375 2.16667 11.6667 2.25 12ZM7.65 12H12.35C12.4 11.6667 12.4375 11.3375 12.4625 11.0125C12.4875 10.6875 12.5 10.35 12.5 10C12.5 9.65 12.4875 9.3125 12.4625 8.9875C12.4375 8.6625 12.4 8.33333 12.35 8H7.65C7.6 8.33333 7.5625 8.6625 7.5375 8.9875C7.5125 9.3125 7.5 9.65 7.5 10C7.5 10.35 7.5125 10.6875 7.5375 11.0125C7.5625 11.3375 7.6 11.6667 7.65 12ZM14.35 12H17.75C17.8333 11.6667 17.8958 11.3375 17.9375 11.0125C17.9792 10.6875 18 10.35 18 10C18 9.65 17.9792 9.3125 17.9375 8.9875C17.8958 8.6625 17.8333 8.33333 17.75 8H14.35C14.4 8.33333 14.4375 8.6625 14.4625 8.9875C14.4875 9.3125 14.5 9.65 14.5 10C14.5 10.35 14.4875 10.6875 14.4625 11.0125C14.4375 11.3375 14.4 11.6667 14.35 12ZM13.95 6H16.9C16.4167 5.16667 15.8125 4.44167 15.0875 3.825C14.3625 3.20833 13.5333 2.75 12.6 2.45C12.9 3 13.1625 3.57083 13.3875 4.1625C13.6125 4.75417 13.8 5.36667 13.95 6ZM8.1 6H11.9C11.7 5.26667 11.4417 4.575 11.125 3.925C10.8083 3.275 10.4333 2.65 10 2.05C9.56667 2.65 9.19167 3.275 8.875 3.925C8.55833 4.575 8.3 5.26667 8.1 6ZM3.1 6H6.05C6.2 5.36667 6.3875 4.75417 6.6125 4.1625C6.8375 3.57083 7.1 3 7.4 2.45C6.46667 2.75 5.6375 3.20833 4.9125 3.825C4.1875 4.44167 3.58333 5.16667 3.1 6Z\" fill=\"#7B7B7B\" /> </A_163>; const TwitterSvg = () => <A_163 xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 21 17\" fill=\"none\"> <path d=\"M20.92 2C20.15 2.35 19.32 2.58 18.46 2.69C19.34 2.16 20.02 1.32 20.34 0.31C19.51 0.81 18.59 1.16 17.62 1.36C16.83 0.5 15.72 0 14.46 0C12.11 0 10.19 1.92 10.19 4.29C10.19 4.63 10.23 4.96 10.3 5.27C6.74 5.09 3.57 3.38 1.46 0.79C1.09 1.42 0.88 2.16 0.88 2.94C0.88 4.43 1.63 5.75 2.79 6.5C2.08 6.5 1.42 6.3 0.84 6V6.03C0.84 8.11 2.32 9.85 4.28 10.24C3.65073 10.4122 2.9901 10.4362 2.35 10.31C2.62161 11.1625 3.15354 11.9084 3.87102 12.4429C4.5885 12.9775 5.45545 13.2737 6.35 13.29C4.83363 14.4904 2.954 15.1393 1.02 15.13C0.68 15.13 0.34 15.11 0 15.07C1.9 16.29 4.16 17 6.58 17C14.46 17 18.79 10.46 18.79 4.79C18.79 4.6 18.79 4.42 18.78 4.23C19.62 3.63 20.34 2.87 20.92 2Z\" fill=\"#7B7B7B\" /> </A_163>; const NearSvg = __props__ => <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" id=\"near-logo\" {...__props__}> <rect width=\"24\" height=\"24\" rx=\"12\" fill=\"#7B7B7B\" /> <path d=\"M15.616 6.61333L13.1121 10.3333C12.939 10.5867 13.2719 10.8933 13.5117 10.68L15.9756 8.53333C16.0422 8.48 16.1354 8.52 16.1354 8.61333V15.32C16.1354 15.4133 16.0155 15.4533 15.9623 15.3867L8.50388 6.45333C8.26415 6.16 7.91787 6 7.53163 6H7.26526C6.5727 6 6 6.57333 6 7.28V16.72C6 17.4267 6.5727 18 7.27858 18C7.71809 18 8.13097 17.7733 8.3707 17.3867L10.8746 13.6667C11.0477 13.4133 10.7148 13.1067 10.475 13.32L8.0111 15.4533C7.94451 15.5067 7.85128 15.4667 7.85128 15.3733V8.68C7.85128 8.58667 7.97114 8.54667 8.02442 8.61333L15.4828 17.5467C15.7225 17.84 16.0821 18 16.4551 18H16.7214C17.4273 18 18 17.4267 18 16.72V7.28C18 6.57333 17.4273 6 16.7214 6C16.2686 6 15.8557 6.22667 15.616 6.61333Z\" fill=\"#fff\" /> </svg>; const A_163 = styled.svg\\` width: 24px; height: 24px; path, rect { transition: all 300ms ease-in-out; } &#near-logo:hover path { fill: white; } :hover path, :hover rect { fill: #292929; }\\`; const GithubSvg = () => <A_163 xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" fill=\"none\"> <path d=\"M10 0C8.68678 0 7.38642 0.258658 6.17317 0.761205C4.95991 1.26375 3.85752 2.00035 2.92893 2.92893C1.05357 4.8043 0 7.34784 0 10C0 14.42 2.87 18.17 6.84 19.5C7.34 19.58 7.5 19.27 7.5 19V17.31C4.73 17.91 4.14 15.97 4.14 15.97C3.68 14.81 3.03 14.5 3.03 14.5C2.12 13.88 3.1 13.9 3.1 13.9C4.1 13.97 4.63 14.93 4.63 14.93C5.5 16.45 6.97 16 7.54 15.76C7.63 15.11 7.89 14.67 8.17 14.42C5.95 14.17 3.62 13.31 3.62 9.5C3.62 8.39 4 7.5 4.65 6.79C4.55 6.54 4.2 5.5 4.75 4.15C4.75 4.15 5.59 3.88 7.5 5.17C8.29 4.95 9.15 4.84 10 4.84C10.85 4.84 11.71 4.95 12.5 5.17C14.41 3.88 15.25 4.15 15.25 4.15C15.8 5.5 15.45 6.54 15.35 6.79C16 7.5 16.38 8.39 16.38 9.5C16.38 13.32 14.04 14.16 11.81 14.41C12.17 14.72 12.5 15.33 12.5 16.26V19C12.5 19.27 12.66 19.59 13.17 19.5C17.14 18.16 20 14.42 20 10C20 8.68678 19.7413 7.38642 19.2388 6.17317C18.7362 4.95991 17.9997 3.85752 17.0711 2.92893C16.1425 2.00035 15.0401 1.26375 13.8268 0.761205C12.6136 0.258658 11.3132 0 10 0Z\" fill=\"#7B7B7B\" /> </A_163>; const Linktree = ({ projectId, accountId}) => { const id = projectId || accountId || \"\"; const profile = Social.getr(\\`\\${id}/profile\\`); const linktree = profile?.linktree; if (!linktree) return \"\"; const itemIconUrls = { github: <GithubSvg />, twitter: <TwitterSvg />, website: <WebsiteSvg />, NEAR: <NearSvg /> }; const fullUrls = { twitter: handle => \\`https://twitter.com/\\${handle.trim()}\\`, github: username => \\`https://github.com/\\${username.trim()}\\`, website: url => url.includes(\"http\") ? url : \\`https://\\${url.trim()}\\` }; return <LinktreeContainer> {Object.entries(linktree).map(([k, v]) => { return k in itemIconUrls && v ? <LinktreeItemContainer key={k} href={fullUrls[k](v)} onClick={e => { if (!v) { e.preventDefault(); } }} target=\"_blank\"> {itemIconUrls[k]} </LinktreeItemContainer> : null; })} <LinktreeItemContainer target=\"_blank\" href={\\`https://near.social/mob.near/widget/ProfilePage?accountId=\\${projectId || accountId}\\`}> {itemIconUrls.NEAR} </LinktreeItemContainer> </LinktreeContainer>;}; const getTagsFromSocialProfileData = profileData => { if (!profileData) return []; const DEPRECATED_CATEGORY_MAPPINGS = { \"social-impact\": \"Social Impact\", \"non-profit\": \"NonProfit\", climate: \"Climate\", \"public-good\": \"Public Good\", \"de-sci\": \"DeSci\", \"open-source\": \"Open Source\", community: \"Community\", education: \"Education\" }; const tags = profileData.plCategories ? JSON.parse(profileData.plCategories) : profileData.category ? [profileData.category.text ?? DEPRECATED_CATEGORY_MAPPINGS[profileData.category] ?? \"\"] : []; return tags;}; const ProfileTags = ({ projectId, accountId}) => { const TagsContainer = styled.div\\` display: flex; gap: 12px; flex-wrap: wrap; max-width: 600px; \\`; const Tag = styled.span\\` color: #292929; font-size: 14px; font-weight: 500; padding: 4px 6px; border-radius: 2px; background: #ebebeb; box-shadow: 0px -1px 0px 0px #dbdbdb inset, 0px 0px 0px 0.5px #dbdbdb; \\`; const projectProfile = Social.getr(\\`\\${projectId || accountId}/profile\\`); const tags = accountId ? Object.keys(projectProfile.tags || {}) : getTagsFromSocialProfileData(projectProfile); if (!tags.length) return \"No tags\"; return <TagsContainer> {projectId && projectId.endsWith(\".sputnik-dao.near\") && <Tag>DAO</Tag>} {tags.map((tag, tagIndex) => <Tag key={tagIndex}>{tag}</Tag>)} </TagsContainer>;}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_164 = styled.div\\` display: flex; flex-direction: column; padding: 0 4rem; margin-bottom: 66px; width: 100%; gap: 0.5rem; @media screen and (max-width: 768px) { padding: 0 1rem; }\\`;const A_165 = styled.div\\` display: flex; width: 100%; flex-wrap: wrap; gap: 2rem; .follow-btn { padding: 10px 0px; width: 160px; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; height: fit-content; } @media only screen and (max-width: 480px) { flex-direction: column; .follow-btn { margin-left: auto; } }\\`;const Info = styled.div\\` display: flex; flex-direction: column; gap: 24px; flex: 1;\\`;const NameContainer = styled.div\\` display: flex; width: 100%; flex-wrap: wrap; flex-direction: row; align-items: end; gap: 1rem;\\`;const LinksWrapper = styled.div\\` display: flex; gap: 2rem; flex-wrap: wrap;\\`;const ReferralButton = styled.div\\` display: flex; gap: 0.5rem; align-items: center; cursor: pointer; div { font-size: 14px; font-weight: 500; } svg { width: 18px; } svg path { transition: fill 300ms ease-in-out; } :hover svg path { fill: #292929; }\\`;const Name = styled.div\\` font-size: 40px; font-weight: 500; color: #2e2e2e; line-height: 1; font-family: \"Lora\"; @media screen and (max-width: 768px) { font-size: 32px; line-height: 40px; }\\`;const AccountInfoContainer = styled.div\\` display: flex; gap: 0.5rem; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const AccountId = styled.div\\` font-size: 16px; font-weight: 400; @media screen and (max-width: 768px) { font-size: 14px; }\\`; const { profile: profile, accountId: accountId, projectId: projectId } = props; const name = profile.name; const policy = projectId ? Near.view(projectId, \"get_policy\", {}) : false; const isDao = !!policy; const [copid, setCopid] = useState(false); const userHasPermissions = useMemo(() => { if (!policy) return false; const userRoles = policy.roles.filter((role) => { if (role.kind === \"Everyone\") return true; return role.kind.Group && role.kind.Group.includes(context.accountId); }); const kind = \"call\"; const action = \"AddProposal\"; const allowed = userRoles.some(({ permissions }) => { return permissions.includes(\\`\\${kind}:\\${action}\\`) || permissions.includes(\\`\\${kind}:*\\`) || permissions.includes(\\`*:\\${action}\\`) || permissions.includes(\"*:*\"); }); return allowed; }, [policy]); const isOwner = projectId === context.accountId; const isPermissionedMember = isDao && userHasPermissions; const canEdit = isOwner || isPermissionedMember; const id = projectId || accountId || \"\"; return <A_164> <A_165> <Info> <NameContainer> <Name>{name.length > 25 ? name.slice(0, 25).trim() + \"...\" : name}</Name> <AccountInfoContainer> <AccountId>@ {id.length > 15 ? id.slice(0, 15).trim() + \"...\" : id}</AccountId> <Widget loading=\" \" code={props.alem.componentsCode.CopyIcon} props={{ ...{ textToCopy: id, customStyle: \"height: 18px;\", ...props } }} /> </AccountInfoContainer> {canEdit && <Button type=\"secondary\" text=\"Edit profile\" style={{ marginLeft: \"auto\" }} href={hrefWithParams(\\`?tab=editproject&projectId=\\${projectId}\\`)} />} {accountId === context.accountId && !projectId && <Button type=\"secondary\" text=\"Edit profile\" style={{ marginLeft: \"auto\" }} href={hrefWithParams(\\`?tab=editprofile\\`)} />} </NameContainer> <ProfileTags projectId={projectId} accountId={accountId} /> <LinksWrapper> <Linktree projectId={projectId} accountId={accountId} /> {projectId && context.accountId && <ReferralButton onClick={() => { clipboard.writeText(\\`https://bos.potlock.org/staging.potlock.near/widget/IndexLoader?tab=project&projectId=\\${projectId}&referrerId=\\${context.accountId}\\`); setCopid(true); setTimeout(() => { setCopid(false); }, 2000); }}> {copid ? <CheckIcon /> : <ReferrerIcon />} <div>Earn referral fees</div> </ReferralButton>} </LinksWrapper> </Info> {projectId ? <Widget loading=\" \" code={props.alem.componentsCode.DonationsInfo} props={{ ...{ projectId: projectId, ...props } }} /> : accountId !== context.accountId ? <A_161 accountId={accountId || \"\"} classname=\"follow-btn\" /> : \"\"} </A_165> </A_164>; `, Body: ` const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const A_160 = ({ navOptions, nav}) => { const getSelectedNavOption = () => { const navOption = navOptions.find((option) => option.id == nav); return navOption ?? navOptions[0]; }; const NavOptionsContainer = styled.div\\` display: flex; justify-content: flex-start; gap: 2rem; width: 100%; border-bottom: 1px solid #c7c7c7; padding: 0 4rem; .nav-option { font-size: 14px; color: #7b7b7b; padding: 10px 16px; font-weight: 500; white-space: nowrap; border-bottom: 2px solid transparent; transition: 300ms ease; &.disabled { pointer-events: none; cursor: not-allowed; } &.selected { color: #292929; border-bottom-color: #292929; } :hover { border-bottom-color: #292929; text-decoration: none; } } @media screen and (max-width: 768px) { padding: 0px 1rem; overflow-x: scroll; .nav-option.selected { order: -1; } } \\`; return <NavOptionsContainer> {navOptions.map((option) => { const selected = option.id == getSelectedNavOption().id; return option.label ? <a className={\\`nav-option \\${selected && \"selected\"} \\${option.disabled && \"disabled\"}\\`} href={option.href}> {option.label} </a> : \"\"; })} </NavOptionsContainer>;}; const A_233 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_234 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_235 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 10px; background: #ffffff; border: 1px solid #d0d5dd; /* box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); */ box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset; border-radius: 4px; color: #101828; width: 100%;\\`;const Placeholder = styled.span\\` color: #a0a3a8;\\`;const scaleOut = styled.keyframes\\` from { transform: scaleY(0); } to { transform: scaleY(1); }\\`;const SelectContent = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; gap: 0.5em; width: 100%; border: 1px solid #d0d5dd; border-radius: 4px; background: #ffffff; z-index: 3 !important;\\`;const Viewport = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; width: 100%;\\`;const Item = styled.button\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 0.5em; width: 100%; cursor: pointer; background: transparent; border: none; transition: background 0.2s ease-in-out; &:nth-child(n + 1) { border-top: 1px solid #d0d5dd; } &:hover { background: #d0d5dd; boder: none; } &:focus { outline: none; }\\`; const Select = (componentProps) => { const label = componentProps.label ?? \"Label\"; const noLabel = componentProps.noLabel ?? false; const placeholder = componentProps.placeholder ?? \"Select an option\"; const value = componentProps.value ?? \"\"; const options = componentProps.options ?? []; const onChange = componentProps.onChange ?? (() => {}); const validate = componentProps.validate ?? (() => {}); const error = componentProps.error ?? \"\"; return <A_233 style={componentProps.containerStyles || {}}> {noLabel ? <></> : <A_234>{label}</A_234>} <Select.Root value={value?.value} onValueChange={(value) => onChange(options.find((option) => option.value === value))}> <Select.Trigger asChild={true}> <A_235 style={componentProps.inputStyles || {}}> {componentProps.iconLeft && componentProps.iconLeft} <Select.Value aria-label={value.value} placeholder={<Placeholder>{placeholder}</Placeholder>} /> {} <Select.Icon> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1 1.5L6 6.5L11 1.5\" stroke=\"currentColor\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg> </Select.Icon> {} </A_235> </Select.Trigger> <Select.Content asChild={true}> <SelectContent> <Select.Viewport asChild={true}> <Viewport> {options.map(({ text, value }) => <Select.Item value={value} asChild={true}> <Item> <Select.ItemText>{text}</Select.ItemText> <Select.ItemIndicator> <svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" /> </svg> </Select.ItemIndicator> </Item> </Select.Item>)} </Viewport> </Select.Viewport> </SelectContent> </Select.Content> </Select.Root> </A_233>;}; const A_166 = styled.div\\` padding-top: 0; position: relative;\\`;const ProfileWraper = styled.div\\` display: flex; padding-left: 4rem; align-items: end; transform: translateY(-50%); position: relative; z-index: 6; @media screen and (max-width: 768px) { padding-left: 8px; }\\`;const ProfileStats = styled.div\\` position: relative; z-index: 1; display: flex; align-items: center; gap: 1.5rem; transform: translate(-25px, -20px); @media screen and (max-width: 768px) { transform: translate(-25px, 0px); gap: 10px; }\\`;const Verified = styled.div\\` opacity: 1; display: flex; align-items: center; font-size: 11px; letter-spacing: 0.88px; gap: 4px; padding: 3px; border-radius: 20px; background: #fff; text-transform: uppercase; overflow: hidden; &.not-verified { width: 10px; opacity: 0; } div { font-weight: 600; } svg { background: white; border-radius: 50%; } @media screen and (max-width: 768px) { div { display: none; } }\\`;const ProfileImageContainer = styled.div\\` background: white; border-radius: 50%; padding: 6px; position: relative; width: 120px; height: 120px; &.editable { &:after { content: \"\"; position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%; border-radius: 50%; background-color: rgba(45.9, 45.9, 45.9, 0); transition: background-color 0.3s; pointer-events: none; @media screen and (max-width: 768px) { height: 64px; } } &:hover { cursor: pointer; &:after { background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; } } } .profile-image { height: 100%; width: 100%; display: flex; border-radius: 50%; img { object-fit: cover; width: 100%; height: 100%; } } > svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0; /* Start with the image invisible */ transition: opacity 0.3s; z-index: 2; /* Ensure the image is on top */ pointer-events: none; } @media screen and (max-width: 768px) { width: 100px; height: 100px; }\\`;const BackgroundImageContainer = styled.div\\` position: relative; width: 100%; height: 318px; display: flex; &.editable { &:after { content: \"\"; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(45.9, 45.9, 45.9, 0); transition: background-color 0.3s; pointer-events: none; } &:hover { cursor: pointer; &:after { background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; } } } img { object-fit: cover; height: 100%; width: 100%; } svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0; transition: opacity 0.3s; z-index: 2; pointer-events: none; } @media screen and (max-width: 768px) { height: 264px; }\\`; const statuses = { Approved: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M10.7835 0.943599C10.3834 0.454917 9.6361 0.454917 9.236 0.943599L8.44604 1.90847C8.17411 2.24062 7.72052 2.36216 7.31895 2.21048L6.15238 1.76985C5.56155 1.54669 4.91436 1.92035 4.8122 2.5436L4.61051 3.77419C4.54108 4.19781 4.20904 4.52985 3.78542 4.59928L2.55484 4.80097C1.93158 4.90313 1.55792 5.55032 1.78108 6.14115L2.22171 7.30772C2.37339 7.70929 2.25185 8.16288 1.9197 8.43481L0.95483 9.22477C0.466148 9.62487 0.466147 10.3722 0.954829 10.7723L1.9197 11.5622C2.25185 11.8342 2.37339 12.2878 2.22171 12.6893L1.78108 13.8559C1.55792 14.4467 1.93158 15.0939 2.55484 15.1961L3.78542 15.3978C4.20904 15.4672 4.54108 15.7992 4.61051 16.2229L4.8122 17.4534C4.91436 18.0767 5.56155 18.4504 6.15238 18.2272L7.31895 17.7866C7.72052 17.6349 8.17411 17.7564 8.44604 18.0886L9.236 19.0535C9.6361 19.5421 10.3834 19.5421 10.7835 19.0535L11.5735 18.0886C11.8454 17.7564 12.299 17.6349 12.7006 17.7866L13.8671 18.2272C14.458 18.4504 15.1052 18.0767 15.2073 17.4534L15.409 16.2229C15.4784 15.7992 15.8105 15.4672 16.2341 15.3978L17.4647 15.1961C18.0879 15.0939 18.4616 14.4467 18.2384 13.8559L17.7978 12.6893C17.6461 12.2878 17.7677 11.8342 18.0998 11.5622L19.0647 10.7723C19.5534 10.3722 19.5534 9.62487 19.0647 9.22478L18.0998 8.43481C17.7677 8.16288 17.6461 7.70929 17.7978 7.30772L18.2384 6.14115C18.4616 5.55032 18.0879 4.90313 17.4647 4.80097L16.2341 4.59928C15.8105 4.52985 15.4784 4.19781 15.409 3.77419L15.2073 2.54361C15.1052 1.92035 14.458 1.54669 13.8671 1.76985L12.7006 2.21048C12.299 2.36216 11.8454 2.24062 11.5735 1.90847L10.7835 0.943599ZM13.5029 7.49591C13.3065 7.30179 12.9899 7.3036 12.7957 7.49996L8.92649 11.4119L7.25946 9.53742C7.07595 9.33107 6.75991 9.31256 6.55356 9.49607C6.34722 9.67958 6.3287 9.99562 6.51221 10.202L8.8858 12.8709L9.90713 11.8496L13.5073 8.20316C13.7013 8.00663 13.6994 7.69003 13.5029 7.49591Z\" fill=\"#0DBFAF\" /> </svg>, color: \"#0E615E\" }, Rejected: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM13.59 5L10 8.59L6.41 5L5 6.41L8.59 10L5 13.59L6.41 15L10 11.41L13.59 15L15 13.59L11.41 10L15 6.41L13.59 5Z\" fill=\"#F6767A\" /> </svg>, color: \"#ED464F\" }, Pending: { icon: <svg width=\"21\" height=\"20\" viewBox=\"0 0 21 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.1523 0C4.63234 0 0.152344 4.48 0.152344 10C0.152344 15.52 4.63234 20 10.1523 20C15.6723 20 20.1523 15.52 20.1523 10C20.1523 4.48 15.6723 0 10.1523 0ZM10.1523 18C5.73234 18 2.15234 14.42 2.15234 10C2.15234 5.58 5.73234 2 10.1523 2C14.5723 2 18.1523 5.58 18.1523 10C18.1523 14.42 14.5723 18 10.1523 18Z\" fill=\"#F4B37D\" /> <path d=\"M5.15234 11.5C5.98077 11.5 6.65234 10.8284 6.65234 10C6.65234 9.17157 5.98077 8.5 5.15234 8.5C4.32392 8.5 3.65234 9.17157 3.65234 10C3.65234 10.8284 4.32392 11.5 5.15234 11.5Z\" fill=\"#F4B37D\" /> <path d=\"M10.1523 11.5C10.9808 11.5 11.6523 10.8284 11.6523 10C11.6523 9.17157 10.9808 8.5 10.1523 8.5C9.32392 8.5 8.65234 9.17157 8.65234 10C8.65234 10.8284 9.32392 11.5 10.1523 11.5Z\" fill=\"#F4B37D\" /> <path d=\"M15.1523 11.5C15.9808 11.5 16.6523 10.8284 16.6523 10C16.6523 9.17157 15.9808 8.5 15.1523 8.5C14.3239 8.5 13.6523 9.17157 13.6523 10C13.6523 10.8284 14.3239 11.5 15.1523 11.5Z\" fill=\"#F4B37D\" /> </svg>, color: \"#EA6A25\" }, Graylisted: { icon: <svg width=\"21\" height=\"20\" viewBox=\"0 0 21 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M9.15234 16H11.1523V14H9.15234V16ZM10.1523 0C4.63234 0 0.152344 4.48 0.152344 10C0.152344 15.52 4.63234 20 10.1523 20C15.6723 20 20.1523 15.52 20.1523 10C20.1523 4.48 15.6723 0 10.1523 0ZM10.1523 18C5.74234 18 2.15234 14.41 2.15234 10C2.15234 5.59 5.74234 2 10.1523 2C14.5623 2 18.1523 5.59 18.1523 10C18.1523 14.41 14.5623 18 10.1523 18ZM10.1523 4C7.94234 4 6.15234 5.79 6.15234 8H8.15234C8.15234 6.9 9.05234 6 10.1523 6C11.2523 6 12.1523 6.9 12.1523 8C12.1523 10 9.15234 9.75 9.15234 13H11.1523C11.1523 10.75 14.1523 10.5 14.1523 8C14.1523 5.79 12.3623 4 10.1523 4Z\" fill=\"#7B7B7B\" /> </svg>, color: \"#7B7B7B\" }, Blacklisted: { icon: <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM2 10C2 5.58 5.58 2 10 2C11.85 2 13.55 2.63 14.9 3.69L3.69 14.9C2.63 13.55 2 11.85 2 10ZM10 18C8.15 18 6.45 17.37 5.1 16.31L16.31 5.1C17.37 6.45 18 8.15 18 10C18 14.42 14.42 18 10 18Z\" fill=\"#292929\" /> </svg>, color: \"#292929\" }}; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const FollowStats = ({ projectId: _projectId, accountId: _accountId}) => { const projectId = _projectId; const accountId = projectId || _accountId; if (!accountId) { return \"\"; } const following = Social.keys(\\`\\${accountId}/graph/follow/*\\`, \"final\", { return_type: \"BlockHeight\", values_only: true }); const followers = Social.keys(\\`*/graph/follow/\\${accountId}\\`, \"final\", { return_type: \"BlockHeight\", values_only: true }); const numFollowing = following ? Object.keys(following[accountId].graph.follow || {}).length : 0; const numFollowers = followers ? Object.keys(followers || {}).length : 0; const profileLink = hrefWithParams(\\`?tab=\\${projectId ? \"project\" : \"profile\"}&\\${projectId ? \"projectId\" : \"accountId\"}=\\${projectId || accountId}\\`); const Container = styled.div\\` display: flex; align-items: center; font-size: 14px; gap: 2rem; a { gap: 8px; display: flex; } @media screen and (max-width: 768px) { gap: 1rem; } \\`; return <Container> <div> <a href={\\`\\${profileLink}&nav=followers\\`} className=\"text-dark\"> {numFollowers !== null ? <span style={{ fontWeight: 600 }}>{numFollowers}</span> : \"-\"} <span>Follower{numFollowers !== 1 && \"s\"}</span> </a> </div> <div> <a href={\\`\\${profileLink}&nav=following\\`} className=\"text-dark\"> {numFollowing !== null ? <span style={{ fontWeight: 600 }}>{numFollowing}</span> : \"-\"} <span>Following</span> </a> </div> </Container>;}; const CameraSvg = ({ height}) => <svg width={height || 48} height={height || 48} viewBox={\\`0 0 48 48\\`} fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <mask id=\"mask0_3178_2528\" style={{ maskType: \"alpha\" }} maskUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width={height || 48} height={height || 48}> <rect width={height || 48} height={height || 48} fill=\"#D9D9D9\" /> </mask> <g mask=\"url(#mask0_3178_2528)\"> <path d=\"M5 40.35C4.2 40.35 3.5 40.05 2.9 39.45C2.3 38.85 2 38.15 2 37.35V11.7C2 10.9333 2.3 10.2417 2.9 9.625C3.5 9.00833 4.2 8.7 5 8.7H12.35L16 4.35H29.7V7.35H17.4L13.75 11.7H5V37.35H39V16.65H42V37.35C42 38.15 41.6917 38.85 41.075 39.45C40.4583 40.05 39.7667 40.35 39 40.35H5ZM39 11.65V7.35H34.7V4.35H39V0H42V4.35H46.35V7.35H42V11.65H39ZM21.975 33C24.3917 33 26.4167 32.1833 28.05 30.55C29.6833 28.9167 30.5 26.8917 30.5 24.475C30.5 22.0583 29.6833 20.0417 28.05 18.425C26.4167 16.8083 24.3917 16 21.975 16C19.5583 16 17.5417 16.8083 15.925 18.425C14.3083 20.0417 13.5 22.0583 13.5 24.475C13.5 26.8917 14.3083 28.9167 15.925 30.55C17.5417 32.1833 19.5583 33 21.975 33ZM21.975 30C20.3917 30 19.0833 29.475 18.05 28.425C17.0167 27.375 16.5 26.0583 16.5 24.475C16.5 22.8917 17.0167 21.5833 18.05 20.55C19.0833 19.5167 20.3917 19 21.975 19C23.5583 19 24.875 19.5167 25.925 20.55C26.975 21.5833 27.5 22.8917 27.5 24.475C27.5 26.0583 26.975 27.375 25.925 28.425C24.875 29.475 23.5583 30 21.975 30Z\" fill=\"white\" /> </g> </svg>; const BannerHeader = (__props__) => { const { showFollowers, registration, projectId, profileImageOnChange, bgImageOnChange } = __props__; const accountId = __props__.accountId || projectId || context.accountId; if (!accountId) { return \"No account ID\"; } const editable = bgImageOnChange && profileImageOnChange; const profile = __props__.profile ?? Social.getr(\\`\\${accountId}/profile\\`); const image = profile.image; const backgroundImage = __props__.backgroundImage || profile.backgroundImage; const profileImage = __props__.profileImage || image; const imageStyle = __props__.imageStyle ?? {}; const backgroundStyle = __props__.backgroundStyle ?? {}; const containerStyle = __props__.containerStyle ?? {}; const nadaBotVerified = Near.view(\"v1.nadabot.near\", \"is_human\", { account_id: accountId }); return <A_166 style={{ ...containerStyle }}> <BackgroundImageContainer className={editable ? \"editable\" : \"\"} style={{ height: backgroundStyle.height ? backgroundStyle.height : \"\" }}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: backgroundImage, alt: \"profile background\", style: { ...backgroundStyle, pointerEvents: \"none\" }, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\" }, ...props } }} /> <CameraSvg height={48} /> {editable && <Files multiple={false} accepts={[\"image/*\"]} minFileSize={1} style={{ zIndex: 4, top: 0, width: \"100%\", height: backgroundStyle.height ?? \"100%\", position: \"absolute\" }} clickable onChange={bgImageOnChange} />} </BackgroundImageContainer> <ProfileWraper> <ProfileImageContainer className={editable ? \"editable\" : \"\"}> <CameraSvg height={24} /> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ style: { ...imageStyle }, className: \"profile-image\", image: profileImage, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm\", ...props } }} /> {editable && <Files multiple={false} accepts={[\"image/*\"]} minFileSize={1} style={{ position: \"absolute\", width: \"100%\", height: \"100%\", left: 0, top: 0 }} clickable onChange={profileImageOnChange}> </Files>} </ProfileImageContainer> {showFollowers && <ProfileStats> {registration ? <Verified> {statuses[registration.status].icon} <div style={{ color: statuses[registration.status].color }}> {registration.status}</div> </Verified> : nadaBotVerified ? <Verified> {statuses.Approved.icon} <div style={{ color: statuses.Approved.color }}> Verified</div> </Verified> : <div style={{ width: \"10px\" }} />} <FollowStats accountId={projectId || accountId} projectId={projectId} /> </ProfileStats>} </ProfileWraper> {__props__.children && __props__.children} </A_166>; }; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const A_167 = styled.div\\` display: flex; flex-direction: column;\\`;const A_168 = styled.div\\` display: flex; flex-direction: column; justify-content: flex-start; overflow: hidden;\\`;const Details = styled.div\\` display: flex; flex-direction: column; gap: 2rem; width: 100%; padding: 3rem 4rem 24px; @media screen and (max-width: 768px) { padding: 24px 1rem; }\\`;const SidebarContainer = styled.div\\` width: 15%; padding-left: 1rem; @media screen and (max-width: 768px) { width: fit-content; > div:first-of-type { display: none; } }\\`;const A_169 = styled.div\\` color: #525252; font-size: 16px; font-weight: 600; line-height: 20px; word-wrap: break-word; margin-bottom: 8px;\\`;const A_170 = styled.div\\` display: flex; flex-direction: row; align-items: center;\\`; const props = props; const { projectId } = props; const { accountId: _accountId } = props.alem.useParams(); const accountId = _accountId ?? context.accountId; const { PROJECT_STATUSES, SUPPORTED_FTS: { NEAR } } = constants; const [statusReview, setStatusReview] = useState({ modalOpen: false, notes: \"\", newStatus: \"\" }); const listsContractId = ListsSDK.getContractId(); const userIsRegistryAdmin = ListsSDK.isRegistryAdmin(context.accountId); const registration = ListsSDK.getRegistration(null, projectId); const handleUpdateStatus = () => { Near.call([{ contractName: listsContractId, methodName: \"update_registration\", args: { registration_id: registration.id, status: statusReview.newStatus, notes: statusReview.notes }, deposit: NEAR.toIndivisible(0.01).toString() }]); }; const SelectedNavComponent = useMemo(() => { return props.navOptions.find((option) => option.id == props.nav).source; }, []); return <A_167> <BannerHeader showFollowers accountId={projectId || accountId} projectId={projectId} registration={registration} /> <A_168> <Widget loading=\" \" code={props.alem.componentsCode.BodyHeader} props={{ ...{ accountId: accountId, projectId: projectId, profile: props.profile, ...props } }} /> {userIsRegistryAdmin && projectId && <Select {...{ noLabel: true, options: PROJECT_STATUSES.map((status) => ({ value: status, text: status })), value: { text: props.registration.status, value: props.registration.status }, onChange: (status) => { if (status.value != registration.status) { setStatusReview({ ...statusReview, newStatus: status.value, modalOpen: true }); } }, containerStyles: { padding: \"16px 24px\" } }} />} <A_160 navOptions={props.navOptions} nav={props.nav} /> <Details> {SelectedNavComponent && <SelectedNavComponent accountId={accountId} projectId={projectId} accounts={[projectId || accountId]} donations={props.donations} {...props} />} </Details> </A_168> {statusReview.modalOpen && <ModalOverlay onOverlayClick={() => setStatusReview({ ...statusReview, modalOpen: false })}> <> <A_169>Enter Notes for changing status to {statusReview.newStatus}</A_169> <TextArea {...{ noLabel: true, inputRows: 5, inputStyle: { background: \"#FAFAFA\" }, placeholder: \"Your notes here...\", value: statusReview.notes, onChange: (notes) => setStatusReview({ ...statusReview, notes }), validate: () => {} }} /> <A_170 style={{ justifyContent: \"flex-end\", marginTop: \"12px\" }}> <Button type=\"primary\" text=\"Submit\" onClick={handleUpdateStatus} /> </A_170> </> </ModalOverlay>} </A_167>; `, ProjectBanner: ` const A_171 = { Graylisted: { background: \"#C7C7C7\", text: \"GRAYLISTED: needs further review, unsure if project qualifies on public goods\", textColor: \"#525252\", toggleColor: \"#FFFFFF\" }, Rejected: { background: \"#DD3345\", text: \"REJECTED: this project was denied on public goods registry\", textColor: \"#FEF6EE\", toggleColor: \"#C7C7C7\" }, Pending: { background: \"#F0CF1F\", text: \"PENDING: this project has yet to be approved\", textColor: \"#292929\", toggleColor: \"#7B7B7B\" }, Blacklisted: { background: \"#292929\", text: \"BLACKLISTED: this project has been removed for violating terms\", textColor: \"#F6F5F3\", toggleColor: \"#C7C7C7\" }, Unregistered: { background: \"#DD3345\", text: \"UNREGISTERED: This account has not registered as a public good\", textColor: \"#F6F5F3\", toggleColor: \"#C7C7C7\" }}; const A_172 = styled.div\\` width: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 12px; backdrop-filter: blur(150px);\\`;const A_173 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const BannerText = styled.div\\` text-align: center; font-size: 22px; font-weight: 600; letter-spacing: 0.015em; text-transform: uppercase; @media screen and (max-width: 768px) { font-size: 12px; }\\`;const Toggle = styled.span\\` align-items: center; gap: 0.5rem; font-weight: 600; font-size: 22px; white-space: nowrap; margin-left: 0.5rem; svg { width: 12px; transition: all 300ms ease-in-out; } &.active svg { rotate: 180deg; } @media screen and (max-width: 768px) { font-size: 12px; svg { width: 8px; } }\\`;const Notes = styled.div\\` overflow: hidden; transition: all 300ms ease-in-out; font-size: 12px; font-style: italic; max-height: 0; text-transform: uppercase; max-width: 1270px; &.active { max-height: 80px; margin-top: 12px; }\\`; const {registration: registration} = props; const [toggle, setToggle] = useState(false); const registrationStatus = registration ? A_171[registration.status] : A_171.Unregistered; return <A_172 style={{ background: registrationStatus.background }}> <A_173> <BannerText onClick={() => registration.admin_notes ? setToggle(!toggle) : \"\"} style={{ color: registrationStatus.textColor, cursor: registration.admin_notes ? \"pointer\" : \"default\" }}> {registrationStatus.text} {registration.admin_notes && <Toggle className={\\`\\${toggle ? \"active\" : \"\"}\\`} style={{ color: registrationStatus.toggleColor }}> (See {toggle ? \"Less\" : \"Why\"}) <svg className={\\`\\${toggle ? \"active\" : \"\"}\\`} viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M10.59 0.294922L6 4.87492L1.41 0.294922L0 1.70492L6 7.70492L12 1.70492L10.59 0.294922Z\" fill=\"#C7C7C7\" style={{ fill: registrationStatus.toggleColor, stroke: registrationStatus.toggleColor }} /> </svg> </Toggle>} </BannerText> </A_173> {registration.admin_notes && <Notes className={\\`\\${toggle ? \"active\" : \"\"}\\`} style={{ color: registrationStatus.toggleColor }}> Admin notes: {registration.admin_notes} </Notes>} </A_172>; `, ProjectPage: ` const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const A_152 = styled.div\\` display: flex; justify-content: space-between; align-items: center; padding: 68px 105px; border-radius: 12px; background: #f6f5f3; .text { font-family: \"Lora\"; max-width: 290px; font-size: 22px; font-style: italic; font-weight: 500; color: #292929; } img { width: 60%; } @media screen and (max-width: 768px) { flex-direction: column-reverse; padding: 24px 16px; .text { font-size: 16px; } img { width: 100%; } }\\`; const MergedIndexFeed = (compProps) => { if (!compProps.index) { return \"props.index is not defined\"; } const indices = JSON.parse(JSON.stringify(Array.isArray(compProps.index) ? compProps.index : [compProps.index])); const filter = compProps.filter; const renderItem = compProps.renderItem ?? ((item) => <div key={JSON.stringify(item)}> #{item.blockHeight}: {JSON.stringify(item)} </div>); const cachedRenderItem = (item, i) => { const key = JSON.stringify(item); if (!(key in state.cachedItems)) { state.cachedItems[key] = renderItem(item, i); } return state.cachedItems[key]; }; const initialRenderLimit = compProps.initialRenderLimit ?? 10; const addDisplayCount = compProps.nextLimit ?? initialRenderLimit; const reverse = !!compProps.reverse; const computeFetchFrom = (items, limit, desc) => { if (!items || items.length < limit) { return false; } const blockHeight = items[items.length - 1].blockHeight; return desc ? blockHeight - 1 : blockHeight + 1; }; const mergeItems = (iIndex, oldItems, newItems, desc) => { const index = indices[iIndex]; const items = [...new Set([...newItems.map((item) => ({ ...item, action: index.action, key: index.key, index: iIndex })), ...oldItems].map((i) => JSON.stringify(i)))].map((i) => JSON.parse(i)); items.sort((a, b) => a.blockHeight - b.blockHeight); if (desc) { items.reverse(); } return items; }; const jIndices = JSON.stringify(indices); if (jIndices !== state.jIndices) { State.update({ jIndices, feeds: indices.map(() => ({})), items: [], displayCount: initialRenderLimit, cachedItems: {} }); } let stateChanged = false; for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; let feedChanged = false; index.options = index.options || {}; index.options.limit = Math.min(Math.max(initialRenderLimit + addDisplayCount * 2, index.options.limit), 100); const desc = index.options.order === \"desc\"; const initialItems = Social.index(index.action, index.key, index.options, index.cacheOptions); if (initialItems === null) { continue; } const jInitialItems = JSON.stringify(initialItems); const nextFetchFrom = computeFetchFrom(initialItems, index.options.limit, desc); if (feed.jInitialItems !== jInitialItems) { feed.jInitialItems = jInitialItems; feedChanged = true; if (nextFetchFrom !== feed.initialNextFetchFrom) { feed.fetchFrom = false; feed.items = mergeItems(iIndex, [], initialItems, desc); feed.initialNextFetchFrom = nextFetchFrom; feed.nextFetchFrom = nextFetchFrom; } else { feed.items = mergeItems(iIndex, feed.items, initialItems, desc); } } feed.usedCount = 0; if (feedChanged) { state.feeds[iIndex] = feed; stateChanged = true; } } const filteredItems = []; while (filteredItems.length < state.displayCount) { let bestItem = null; for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; const desc = index.options.order === \"desc\"; if (!feed.items) { continue; } const item = feed.items[feed.usedCount]; if (!item) { continue; } if (bestItem === null || (desc ? item.blockHeight > bestItem.blockHeight : item.blockHeight < bestItem.blockHeight)) { bestItem = item; } } if (!bestItem) { break; } state.feeds[bestItem.index].usedCount++; if (filter) { if (filter.ignore) { if (bestItem.accountId in filter.ignore) { continue; } } if (filter.require) { if (!(bestItem.accountId in filter.require)) { continue; } } } filteredItems.push(bestItem); } for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; const desc = index.options.order === \"desc\"; let feedChanged = false; if ((feed.items.length || 0) - feed.usedCount < addDisplayCount * 2 && !feed.fetchFrom && feed.nextFetchFrom && feed.nextFetchFrom !== feed.fetchFrom) { feed.fetchFrom = feed.nextFetchFrom; feedChanged = true; } if (feed.fetchFrom) { const limit = addDisplayCount; const newItems = Social.index(index.action, index.key, Object.assign({}, index.options, { from: feed.fetchFrom, subscribe: undefined, limit })); if (newItems !== null) { feed.items = mergeItems(iIndex, feed.items, newItems, desc); feed.fetchFrom = false; feed.nextFetchFrom = computeFetchFrom(newItems, limit, desc); feedChanged = true; } } if (feedChanged) { state.feeds[iIndex] = feed; stateChanged = true; } } if (stateChanged) { State.update(); } const makeMoreItems = () => { State.update({ displayCount: state.displayCount + addDisplayCount }); }; const loader = <div className=\"loader\" key={\"loader\"}> <span className=\"spinner-grow spinner-grow-sm me-1\" role=\"status\" aria-hidden=\"true\" /> Loading ... </div>; const fetchMore = compProps.manual && (state.feeds.some((f) => !!f.fetchFrom) && filteredItems.length < state.displayCount ? loader : state.displayCount < filteredItems.length && <div key={\"loader more\"}> <a href=\"javascript:void\" onClick={(e) => makeMoreItems()}> {compProps.loadMoreText ?? \"Load more...\"} </a> </div>); const items = filteredItems ? filteredItems.slice(0, state.displayCount) : []; if (reverse) { items.reverse(); } const renderedItems = items.length === 0 ? <A_152> <div className=\"text\">This {compProps.tab === \"profile\" ? \"user\" : \"project\"} has not posted yet.</div> <img src=\"https://ipfs.near.social/ipfs/bafkreicwz5cuku3kxxp3wzldslpfzmfqgukpm2wwy7t7zuevkdp4gbl2uq\" alt=\"pots\" /> </A_152> : items.map(cachedRenderItem); return compProps.manual ? <> {reverse && fetchMore} {renderedItems} {!reverse && fetchMore} </> : <InfiniteScroll pageStart={0} loadMore={makeMoreItems} threshold={compProps.threshold ?? 250} hasMore={state.displayCount <= filteredItems.length} loader={loader}> {renderedItems} </InfiniteScroll>;}; const Post = postProps => { return <Widget loading={<div className=\"w-100\" style={{ height: \"200px\" }} />} src=\"mob.near/widget/MainPage.N.Post\" props={postProps} />;}; const Feed = (compProps) => { const post = compProps.post === undefined ?? true; const hashtags = compProps.hashtags || []; const indexKey = compProps.indexKey ?? \"main\"; const index = [{ action: \"post\", key: indexKey, options: { limit: 10, order: \"desc\", accountId: compProps.accounts }, cacheOptions: { ignoreCache: true } }, { action: \"repost\", key: indexKey, options: { limit: 10, order: \"desc\", accountId: compProps.accounts }, cacheOptions: { ignoreCache: true } }]; const isPremiumFeed = compProps.isPremiumFeed; const commentAccounts = compProps.commentAccounts; const renderedPosts = {}; const makePostItem = (a) => ({ type: \"social\", path: \\`\\${a.accountId}/post/main\\`, blockHeight: a.blockHeight }); const renderPost = (a) => { if (a.value.type !== \"md\") { return false; } const item = JSON.stringify(makePostItem(a)); if (item in renderedPosts) { return false; } renderedPosts[item] = true; const postProps = { accountId: a.accountId, blockHeight: a.blockHeight, isPremiumFeed, commentAccounts, indexKey, groupId: compProps.groupId, permissions: compProps.permissions }; return <div key={JSON.stringify(a)}> <Post {...postProps} /> </div>; }; const repostSvg = <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 2 24 24\" stroke=\"currentColor\" strokeWidth=\"1\"> <path fill-rule=\"evenodd\" d=\"M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z\" transform=\"rotate(180, 12, 12), translate(0, 4)\" /> <path fill-rule=\"evenodd\" d=\"M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z\" transform=\"translate(0, 4)\" /> </svg>; const extractParentPost = (item) => { if (!item || item.type !== \"social\" || !item.path || !item.blockHeight) { return undefined; } const accountId = item.path.split(\"/\")[0]; const parentPost = { accountId, blockHeight: item.blockHeight }; return \\`\\${accountId}/post/main\\` === item.path ? parentPost : undefined; }; const renderRepost = (a) => { if (a.value.type !== \"repost\") { return false; } const post = extractParentPost(a.value.item); if (!post) { return false; } const item = JSON.stringify(makePostItem(post)); if (item in renderedPosts) { return false; } renderedPosts[item] = true; const profileLineProps = { accountId: a.accountId, hideImage: true, hideAccountId: true, tooltip: true }; const postProps = { accountId: post.accountId, blockHeight: post.blockHeight, reposted: true, isPremiumFeed, commentAccounts, indexKey, groupId: compProps.groupId, permissions: compProps.permissions }; const postLoading = <div className=\"w-100\" style={{ height: \"200px\" }} />; return <div key={JSON.stringify(a)}> <div className=\"text-muted\" style={{ fontSize: \"13px\", fontWeight: 700, marginLeft: \"24px\", marginBottom: \"-24px\", paddingTop: \"4px\", position: \"relative\", zIndex: 1 }}> {repostSvg}{\" \"} <span style={{ marginLeft: \"8px\" }}> Reposted by <Widget loading={a.accountId} src=\"mob.near/widget/N.ProfileLine\" /> </span> </div> <Widget loading={postLoading} src=\"mob.near/widget/MainPage.N.Post\" props={postProps} /> </div>; }; const renderItem = (item) => { return item.action === \"post\" ? renderPost(item) : renderRepost(item); }; const Container = styled.div\\` display: flex; flex-direction: column; .post-btn { background: rgb(46, 46, 46); border-radius: 6px; padding: 12px 16px; border: none; color: white; } \\`; return <Container> {post && <Widget loading=\" \" code={props.alem.componentsCode.Compose} props={{ ...{ ...props } }} />} <MergedIndexFeed index={index} renderItem={renderItem} filter={compProps.filter} threshold={800} /> </Container>; }; const A_158 = styled.div\\` display: flex; flex-direction: column; .tab-content { padding: 1rem 0; }\\`;const Nav = styled.div\\` .nav-pills { background: #fbfbfb; font-weight: 500; --bs-nav-pills-border-radius: 0; --bs-nav-link-color: #000; --bs-nav-pills-link-active-color: #000; --bs-nav-pills-link-active-bg: #fbfbfb; --bs-nav-link-padding-y: 0.75rem; border-bottom: 1px solid #eee; padding-top: 3px; } .nav-link.active { border-bottom: 3px solid #dd3345; } .nav-item:not(:has(> .disabled)):hover { background: #dd334456; .nav-link { color: #dd3345; } } margin: 0 -12px;\\`; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const FollowTabs = (__props__) => { const { accountId, projectId, nav } = __props__; const profileLink = hrefWithParams(\\`?tab=profile&accountId=\\${accountId}\\`); return <A_158> <Nav> <ul className=\"nav nav-pills nav-fill\" role=\"tablist\"> <li className=\"nav-item\" role=\"presentation\"> <a href={\\`\\${profileLink}&nav=followers\\`} className={\\`btn nav-link \\${nav === \"followers\" ? \"active\" : \"\"}\\`} role=\"tab\"> Followers </a> </li> <li className=\"nav-item\" role=\"presentation\"> <a href={\\`\\${profileLink}&nav=following\\`} className={\\`btn nav-link \\${nav === \"following\" ? \"active\" : \"\"}\\`} role=\"tab\"> Following </a> </li> </ul> </Nav> <div className=\"tab-content\"> <div className=\"tab-pane fade in show active\" role=\"tabpanel\"> <Widget loading=\" \" code={props.alem.componentsCode.FollowersList} props={{ ...{ accountId: projectId || accountId, nav: nav, ...props } }} /> </div> </div> </A_158>; }; const ProjectOptions = (projectId) => [{ label: \"Home\", id: \"home\", disabled: false, source: (componentProps) => <Widget loading=\" \" code={props.alem.componentsCode.About} props={{ ...{ ...componentProps, ...props } }} />, href: hrefWithParams(\\`?tab=project&projectId=\\${projectId}&nav=home\\`) }, { label: \"Social Feed\", id: \"feed\", disabled: false, source: (componentProps) => <Feed {...componentProps} />, href: hrefWithParams(\\`?tab=project&projectId=\\${projectId}&nav=feed\\`) }, { label: \"Pots\", id: \"pots\", disabled: false, source: (componentProps) => <Widget loading=\" \" code={props.alem.componentsCode.Pots} props={{ ...{ ...componentProps, ...props } }} />, href: hrefWithParams(\\`?tab=project&projectId=\\${projectId}&nav=pots\\`) }, { label: \"Funding Raised\", id: \"funding\", disabled: false, source: (componentProps) => <Widget loading=\" \" code={props.alem.componentsCode.FundingRaised} props={{ ...{ ...componentProps, ...props } }} />, href: hrefWithParams(\\`?tab=project&projectId=\\${projectId}&nav=funding\\`) }, { label: \"\", id: \"followers\", disabled: false, source: FollowTabs }, { label: \"\", id: \"following\", disabled: false, source: FollowTabs }]; const loadingSkeleton = styled.keyframes\\` 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; }\\`;const SkeletonContainer = styled.div\\` display: flex; flex-direction: column; position: relative; width: 100%; animation-name: \\${loadingSkeleton}; animation-duration: 1s; animation-iteration-count: infinite;\\`;const LoadingBackground = styled.div\\` position: relative; background: #eee; width: 100%; height: 318px; @media screen and (max-width: 768px) { height: 264px; }\\`;const LoadingProfileImg = styled.div\\` width: \\${__props__ => __props__.imageStyle?.width ?? \"128px\"}; height: \\${__props__ => __props__.imageStyle?.height ?? \"128px\"}; z-index: 1; padding: 6px; transform: translateY(-50%); position: relative; margin-left: 4rem; background: white; border-radius: 50%; @media screen and (max-width: 768px) { margin-left: 1rem; } div { background: #eee; width: 100%; height: 100%; border-radius: 50%; }\\`;const BannerSkeleton = () => <SkeletonContainer> <LoadingBackground /> <LoadingProfileImg> <div /> </LoadingProfileImg> </SkeletonContainer>; const A_174 = styled.div\\` /* margin-top: calc(-1 * var(--body-top-padding, 0)); */\\`; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const getProjectByProjectId = projectId => { return ListsSDK.getRegistration(null, projectId);}; const { projectId } = props.alem.useParams(); const accountId = context.accountId; const project = getProjectByProjectId(projectId); const pots = PotFactorySDK.getPots(); const registration = ListsSDK.getRegistration(null, projectId); const account = fetch(\"https://api3.nearblocks.io/v1/account/\" + projectId); if (registration === null || account === null) return <BannerSkeleton />; const isObjectNotEmpty = (obj) => Object.keys(obj).length > 0; const addressExist = account?.body?.account[0]; if (!isObjectNotEmpty(addressExist || {}) && !registration) return <div style={{ marginTop: \"1rem\", fontSize: \"1.5rem\" }}>Account does not exist.</div>; const [directDonations, setDirectDonations] = useState(null); const [matchingRoundDonations, setMatchingRoundDonations] = useState({}); const [potPayouts, setPotPayouts] = useState({}); const getProjectRoundDonations = (potId, potDetail) => { return PotSDK.asyncGetDonationsForProject(potId, projectId).then((donations) => { const updatedDonations = donations.map((donation) => ({ ...donation, base_currency: potDetail.base_currency, pot_name: potDetail.pot_name, pot_id: potId, type: \"matched\" })); setMatchingRoundDonations((prevmMatchingRoundDonations) => { return { ...prevmMatchingRoundDonations, [potId]: updatedDonations }; }); }).catch(() => { setMatchingRoundDonations((prevmMatchingRoundDonations) => { return { ...prevmMatchingRoundDonations, [potId]: [] }; }); }); }; let donationsForRecipient = DonateSDK.getDonationsForRecipient(projectId); if (donationsForRecipient && !directDonations) { donationsForRecipient = donationsForRecipient.map((donation) => ({ ...donation, type: \"direct\" })); setDirectDonations(donationsForRecipient); } if (pots && !matchingRoundDonations[pots[pots.length - 1].id]) { pots.forEach((pot) => { PotSDK.asyncGetConfig(pot.id).then((potDetail) => { const payout = potDetail.payouts.filter((pay) => projectId === pay.project_id)[0]; if (payout.paid_at) setPotPayouts((prevPayout) => ({ ...prevPayout, [pot.id]: { ...payout, pot_id: pot.id, pot_name: potDetail.pot_name, base_currency: potDetail.base_currency, type: \"payout\" } })); getProjectRoundDonations(pot.id, potDetail); }); }); } const allDonations = useMemo(() => { const RoundDonationsValue = Object.values(matchingRoundDonations).flat(); let payouts = Object.values(potPayouts); const allDonations = [...(directDonations || []), ...RoundDonationsValue, ...payouts]; allDonations.sort((a, b) => { const b_donated_at = b.donated_at_ms || b.donated_at || b.paid_at; const a_donated_at = a.donated_at_ms || a.donated_at || a.paid_at; return b_donated_at - a_donated_at; }); return allDonations; }, [matchingRoundDonations, directDonations, potPayouts]); const profile = Social.getr(\\`\\${projectId}/profile\\`); if (project === null) return <BannerSkeleton />; if (project == undefined) { return <div style={{ marginTop: \"1rem\", fontSize: \"1.5rem\" }}>Project not found</div>; } const { nav } = props.alem.useParams(); return <A_174> {registration.status !== \"Approved\" && <Widget loading=\" \" code={props.alem.componentsCode.ProjectBanner} props={{ ...{ registration: registration, ...props } }} />} <Widget loading=\" \" code={props.alem.componentsCode.Body} props={{ ...{ ...{ project, profile, registration, projectId, post: projectId === accountId, nav: nav ?? props.nav ?? \"home\", donations: allDonations, directDonations: directDonations, matchingRoundDonations: Object.values(matchingRoundDonations).flat(), potPayouts: Object.values(potPayouts), navOptions: ProjectOptions(projectId) }, ...props } }} /> </A_174>; `, DonationStats: ` const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const A_175 = styled.div\\` display: flex; flex-direction: row; align-items: center; gap: 1.5rem; margin-top: 1rem; padding: 0 40px; @media screen and (max-width: 768px) { gap: 1rem; padding: 0 8px; }\\`;const StatsTitle = styled.div\\` display: flex; flex-direction: row; align-items: baseline; gap: 8px; font-weight: 600; font-size: 20px; color: #dd3345;\\`;const StatsSubTitle = styled.div\\` color: #656565; font-size: 14px; font-weight: 400;\\`; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const [donated, setDonated] = useState(null); const [donations, setDonations] = useState(null); const data = DonateSDK.getConfig(); useEffect(() => { if (!donated) { const lastDonationAmount = data.net_donations_amount ? yoctosToUsdWithFallback(data.net_donations_amount, true) : null; const totalDonations = data.total_donations_count; setDonated(lastDonationAmount); setDonations(totalDonations); } }, [data, donated]); return <A_175> <StatsTitle> {donated || \"-\"} <StatsSubTitle>Donated</StatsSubTitle> </StatsTitle> <StatsTitle> {donations || \"-\"} <StatsSubTitle>Donations</StatsSubTitle> </StatsTitle> </A_175>; `, NewHero: ` const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useDonationModal = () => useContext(\"donation-modal\"); const svgContent = \\`<svg width=\"1320\" height=\"332\" viewBox=\"0 0 1320 332\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"1320\" height=\"1\" fill=\"#FEF6EE\"/><mask id=\"mask0_11854_22101\" style=\"mask-type:alpha\" maskUnits=\"userSpaceOnUse\" x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\"><rect x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\" fill=\"url(#paint0_radial_11854_22101)\"/></mask><g mask=\"url(#mask0_11854_22101)\"><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" fill=\"#FEF6EE\"/><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" fill=\"#FEF6EE\"/><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" fill=\"#F8D3B0\"/><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" fill=\"#F8D3B0\"/><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" fill=\"#F8D3B0\"/><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" fill=\"#F8D3B0\"/><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" fill=\"#F8D3B0\"/><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" fill=\"#F8D3B0\"/><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" fill=\"#F8D3B0\"/><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" fill=\"#F8D3B0\"/><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" fill=\"#FEF6EE\"/><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" fill=\"#FEF6EE\"/><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" fill=\"#FEF6EE\"/><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" fill=\"#F8D3B0\"/><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" fill=\"#F8D3B0\"/><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" fill=\"#F8D3B0\"/><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" fill=\"#F8D3B0\"/><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" fill=\"#F8D3B0\"/><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" fill=\"#F8D3B0\"/><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" fill=\"#F8D3B0\"/><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" fill=\"#F8D3B0\"/><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" stroke=\"#F4B37D\"/></g></g><defs><radialGradient id=\"paint0_radial_11854_22101\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(660 -158.5) rotate(90) scale(724.5 1018.83)\"><stop stop-color=\"#FCE9D5\"/><stop offset=\"0.855072\" stop-color=\"#FEF6EE\" stop-opacity=\"0\"/></radialGradient></defs></svg>\\`;const HomeBannerStyle = { backgroundImage: \\`url(\"data:image/svg+xml;charset=utf-8,\\${encodeURIComponent(svgContent)}\")\\`, backgroundSize: \"cover\", backgroundRepeat: \"no-repeat\", backgroundColor: \"#FEF6EE\"}; const A_176 = styled.div\\` display: flex; flex-direction: column;\\`;const HeroContainer = styled.div\\` display: flex; flex-direction: column; position: relative; width: 100%; justify-content: center; border: 1px solid #f8d3b0; border-radius: 12px; overflow: hidden; .background { position: absolute; pointer-events: none; left: 0px; width: 100%; top: 0px; min-height: 600px; } .content { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: center; padding: 64px 40px; } .sub-title { color: #dd3345; font-weight: 600; font-size: 16px; margin-top: 0; margin-bottom: 12px; } .title { letter-spacing: -0.4px; font-weight: 500; font-size: 40px; font-family: \"Lora\"; margin: 0; } .btns { display: flex; align-items: center; gap: 2rem; margin-top: 40px; font-size: 14px; a, button { padding: 12px 16px; display: flex; align-items: center; justify-content: center; font-weight: 500; border-radius: 6px; box-shadow: 0px 0px 0px 1px #292929, 0px -2px 0px 0px #292929 inset; border: none; text-decoration: none; color: #292929; transition: all 300ms; &:hover { background: #292929; color: white; } } button { color: white; background: #dd3345; &:hover { } } } @media only screen and (max-width: 768px) { .content { padding: 48px 20px; } .title { font-size: 36px; } .btns { flex-direction: column; gap: 1rem; margin-top: 24px; } .line-break { display: none; } } @media only screen and (max-width: 480px) { .btns a, button { width: 100%; padding: 12px 0; } }\\`;const A_177 = styled.div\\` width: 100%; height: 1px; background: #ebebeb; margin-top: 1rem;\\`; const { projectsData: projectsData } = props; const { allProjects, approvedProjects } = projectsData; const { setDonationModalProps } = useDonationModal(); const getRandomProject = () => { if (approvedProjects) { const randomIndex = Math.floor(Math.random() * approvedProjects.length); return approvedProjects[randomIndex]?.registrant_id; } }; const openDonateRandomlyModal = () => { setDonationModalProps({ projectId: getRandomProject() }); }; const accountId = context.accountId; const isRegisteredProject = useMemo(() => { if (allProjects) { return allProjects.find((registration) => registration.registrant_id === accountId); } }, [allProjects]); return <A_176> <HeroContainer style={{ ...HomeBannerStyle }}> <div className=\"content\"> <h3 className=\"sub-title\">Transforming Funding for Public Goods</h3> <h1 className=\"title\"> Discover impact projects, donate directly, & <br className=\"line-break\" /> participate in funding rounds. </h1> <div className=\"btns\"> <button onClick={openDonateRandomlyModal} className=\"donate-btn\"> Donate Randomly </button> <a href={isRegisteredProject ? \\`?tab=project&projectId=\\${accountId}\\` : \"?tab=createproject\"}> {isRegisteredProject ? \"View Your Project\" : \"Register Your Project\"} </a> </div> </div> </HeroContainer> <Widget loading=\" \" code={props.alem.componentsCode.DonationStats} props={{ ...{ ...props } }} /> <A_177 /> </A_176>; `, FilterDropdown: ` const DropdownRight = __props__ => <svg {...__props__} viewBox=\"0 0 18 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 12H6V10H0V12ZM0 0V2H18V0H0ZM0 7H12V5H0V7Z\" fill=\"#7B7B7B\" /> </svg>; const A_179 = styled.div\\` display: flex; position: relative; flex-direction: column; align-items: flex-end; font-size: 14px;\\`;const A_180 = styled.div\\` font-weight: 500; display: flex; align-items: center; gap: 0.5rem; cursor: pointer; width: fit-content; padding: 0.5rem 1rem; border-radius: 6px; border: 1px solid #7b7b7b; svg { width: 18px; } &.active { color: #fff; background: #292929; }\\`;const Menu = styled.div\\` position: absolute; top: calc(100% + 0.5rem); right: 0; transition: all 300ms ease-in-out; display: flex; flex-wrap: wrap; gap: 8px; border-radius: 8px; padding: 1rem; background: #fff; width: 500px; box-shadow: 0px 0px 0px 1px rgba(123, 123, 123, 0.09), 0px 3px 3px -1px rgba(123, 123, 123, 0.16), 0px 9px 9px -3px rgba(123, 123, 123, 0.1), 0px 17px 14px -5px rgba(123, 123, 123, 0.08); opacity: 0; visibility: hidden; transform: translateY(100px); z-index: 1; &.active { opacity: 1; visibility: visible; transform: translateY(0); } .title { width: 100%; &:not(:first-of-type) { margin-top: 2rem; } } .option { display: flex; align-items: center; gap: 10px; border-radius: 8px; border: 1px solid #dbdbdb; padding: 8px 12px; cursor: pointer; transition: all 300ms ease-in-out; svg { display: none; width: 14px; } :hover { border: 1px solid #f4b37d; background: #fef6ee; color: #ea6a25; } &.selected { border: 1px solid #f4b37d; background: #fef6ee; color: #ea6a25; svg { display: block; } } } @media only screen and (max-width: 768px) { width: 200px !important; left: 0; right: auto; }\\`;const A_181 = styled.div\\` font-weight: 600; font-size: 12px; display: flex; line-height: 1; width: 18px; height: 18px; align-items: center; justify-content: center; border-radius: 50%; background: #ebebeb; &.active { background: #464646; color: #f6f5f3; }\\`;const Screen = styled.div\\` position: fixed; width: 100%; height: 100%; left: 0; top: 0;\\`; const DropdownCenter = __props__ => <svg {...__props__} viewBox=\"0 0 18 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7 12H11V10H7V12ZM0 0V2H18V0H0ZM3 7H15V5H3V7Z\" fill=\"#7B7B7B\" /> </svg>; const props = props; const { onClick, menuClass, label, multipleOptions, defaultSelected } = props; const labelIcon = props.labelIcon ?? \"center\"; const options = props.options ?? {}; const [toggleMenu, setToggleMenu] = useState(false); const [selected, setSelected] = useState(defaultSelected || {}); const icons = { center: <DropdownCenter />, right: <DropdownRight /> }; function findIndexWithAll(listOfLists, target) { for (let i = 0; i < listOfLists.length; i++) { const indexInList = listOfLists[i].indexOf(target); if (indexInList !== -1) { return { listIndex: i, itemIndex: indexInList }; } } return { listIndex: -1, itemIndex: -1 }; } const handleSelect = ({ val, type, label }) => { let selectedUpdated = { ...selected }; const selectedList = selected[type] || []; if (!multipleOptions) { selectedUpdated = { val, label }; } else if (selectedList.includes(val)) { selectedUpdated[type] = selectedList.filter(item => item !== val); } else { selectedUpdated[type] = [...selectedList, val]; } const { listIndex, itemIndex } = findIndexWithAll(Object.values(selectedUpdated), \"all\"); const types = Object.keys(selectedUpdated); if (val === \"all\") { selectedUpdated = { [type]: [val] }; } else if (listIndex !== -1) { selectedUpdated[types[listIndex]].splice(itemIndex, 1); } setSelected(selectedUpdated); onClick(selectedUpdated); setToggleMenu(false); }; const count = Object.values(selected).reduce((total, list) => total + list.length, 0); return <A_179> {toggleMenu && <Screen onClick={() => setToggleMenu(false)} />} <A_180 className={toggleMenu ? \"active\" : \"\"} onClick={() => setToggleMenu(!toggleMenu)}> {label || \"Filter\"} {multipleOptions && <A_181 className={toggleMenu ? \"active\" : \"\"}>{count}</A_181>} <div>{icons[labelIcon]}</div> </A_180> <Menu className={\\`\\${toggleMenu ? \"active\" : \"\"} \\${menuClass ?? \"\"}\\`}> {Object.keys(options)?.map(menuLabel => <> <div className=\"title\">Filter by {menuLabel.includes(\"no label\") ? \"\" : menuLabel}</div> {(options[menuLabel] || [])?.map(({ label, val }) => <div className={\\`option \\${multipleOptions && (selected[menuLabel] || [])?.includes(val) && \"selected\"}\\`} key={val} onClick={() => handleSelect({ label, val, type: menuLabel })}> <svg viewBox=\"0 0 14 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M4.59625 8.90631L1.46875 5.77881L0.403748 6.83631L4.59625 11.0288L13.5962 2.02881L12.5387 0.971313L4.59625 8.90631Z\" fill=\"#F4B37D\" /> </svg> {label} </div>)} </>)} </Menu> </A_179>; `, A_184: ` const A_182 = styled.div\\` display: flex; position: relative; flex-direction: row; align-items: center; flex: 1; border-radius: 6px; border: 1px solid #7b7b7b; padding: 0.5rem; padding-left: 2.5rem; font-size: 14px;\\`;const A_183 = styled.div\\` display: flex; position: absolute; left: 14px; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; pointer-events: none; svg { height: 100%; }\\`;const SearchBarInput = styled.input\\` background: none; width: 100%; outline: none; border: none; &:focus { outline: none; border: none; }\\`;const FilterButton = styled.div\\` font-weight: 500; font-size: 14px; display: flex; align-items: center; gap: 0.5rem; cursor: pointer; width: fit-content; padding: 0.54rem 1rem; border-radius: 6px; border: 1px solid #7b7b7b; transition: all 200ms ease-in-out; &.active { color: #fff; background: #292929; }\\`;const FilterIcon = styled.div\\` display: flex; width: 16px; height: 16px; align-items: center; justify-content: center;\\`;const FilterMenu = styled.div\\` position: absolute; background: #fff; font-size: 14px; top: 110%; right: 0; padding: 8px; display: flex; flex-direction: column; gap: 8px; border-radius: 6px; border: 1px solid rgba(41, 41, 41, 0.36); box-shadow: 0px 12px 20px -4px rgba(123, 123, 123, 0.32), 0px 4px 8px -3px rgba(123, 123, 123, 0.2), 0px 0px 2px 0px rgba(123, 123, 123, 0.36); z-index: 2; opacity: 0; visibility: hidden; transform: translateY(100px); transition: all 200ms ease-in-out; &.active { opacity: 1; visibility: visible; transform: translateY(0); } @media screen and (max-width: 768px) { left: 0; background: #fff; }\\`;const FilterItem = styled.div\\` cursor: pointer; padding: 8px; display: flex; gap: 12px; white-space: nowrap; &:hover { background: #292929; color: #fff; border-radius: 6px; }\\`; const props = props; const { title, numItems, itemName, sortList, sortVal, handleSortChange, setSearchTerm, FilterMenuClass } = props; const onSearchChange = event => { setSearchTerm(event.target.value); }; const [openFilter, setOpenFilter] = useState(false); return <> <A_182> <A_183> <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M12.7549 11.2559H11.9649L11.6849 10.9859C12.6649 9.8459 13.2549 8.3659 13.2549 6.75586C13.2549 3.16586 10.3449 0.255859 6.75488 0.255859C3.16488 0.255859 0.254883 3.16586 0.254883 6.75586C0.254883 10.3459 3.16488 13.2559 6.75488 13.2559C8.3649 13.2559 9.8449 12.6659 10.9849 11.6859L11.2549 11.9659V12.7559L16.2549 17.7459L17.7449 16.2559L12.7549 11.2559ZM6.75488 11.2559C4.26488 11.2559 2.25488 9.2459 2.25488 6.75586C2.25488 4.26586 4.26488 2.25586 6.75488 2.25586C9.2449 2.25586 11.2549 4.26586 11.2549 6.75586C11.2549 9.2459 9.2449 11.2559 6.75488 11.2559Z\" fill=\"#7B7B7B\" /> </svg> </A_183> <SearchBarInput placeholder={\\`Search (\\${numItems}) \\${numItems === 1 ? itemName : itemName + \"s\"}\\`} onChange={onSearchChange} type=\"text\" /> </A_182> <div style={{ position: \"relative\" }} onClick={() => setOpenFilter(!openFilter)}> <FilterButton className={openFilter ? \"active\" : \"\"}> {sortVal || title} <FilterIcon> <svg width=\"18\" height=\"12\" viewBox=\"0 0 18 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0 12H6V10H0V12ZM0 0V2H18V0H0ZM0 7H12V5H0V7Z\" fill=\"#7B7B7B\" /> </svg> </FilterIcon> </FilterButton> <FilterMenu onClick={e => e.stopPropagation()} className={\\`\\${FilterMenuClass || \"\"} \\${openFilter ? \"active\" : \"\"} \\`}> {(sortList || []).map(filter => <FilterItem key={filter} onClick={() => { setOpenFilter(false); handleSortChange(filter); }}> {filter} </FilterItem>)} </FilterMenu> </div> </>; `, ListSection: ` const {shouldShuffle: shouldShuffle, items: items, renderItem: renderItem} = props; const responsive = props.responsive || []; const { Feed } = VM.require(\"devs.near/widget/Feed\"); if (!Feed) { return <p>Loading...</p>; } const _items = useMemo(() => { if (shouldShuffle) { return [...items].sort(() => Math.random() - 0.5); } return items; }, [items, shouldShuffle]); const PAGE_SIZE = 9; const Grid = styled.div\\` display: grid; width: 100%; padding-top: 20px; padding-bottom: 32px; gap: 31px; /* For mobile devices (1 column) */ @media screen and (max-width: 739px) { grid-template-columns: repeat(1, 1fr); \\${props.tab !== \"pot\" && \"padding-top: 40px;\"} } /* For tablet devices (2 columns) */ @media screen and (min-width: 740px) and (max-width: 1023px) { grid-template-columns: repeat(2, 1fr); } /* For desktop devices (3 columns) */ @media screen and (min-width: 1024px) { grid-template-columns: repeat(\\${!props.maxCols || props.maxCols > 2 ? \"3\" : \"2\"}, 1fr); } \\${responsive.map(view => \\` @media screen and (max-width: \\${view.breakpoint}px) { grid-template-columns: repeat(\\${view._items}, 1fr); } \\`)} \\`; return <Feed items={_items} Item={renderItem} Layout={Grid} perPage={PAGE_SIZE} />; `, AllProjects: ` const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const A_178 = { Category: [{ label: \"Desci\", val: \"Desci\" }, { label: \"Open Source\", val: \"Open Source\" }, { label: \"Non Profit\", val: \"Non Profit\" }, { label: \"Social Impact\", val: \"Social Impact\" }, { label: \"Climate\", val: \"Climate\" }, { label: \"Public Good\", val: \"Public Good\" }, { label: \"Community\", val: \"Community\" }, { label: \"Education\", val: \"Education\" }], Status: [{ label: \"All\", val: \"all\" }, { label: \"Approved\", val: \"Approved\" }, { label: \"Pending\", val: \"Pending\" }, { label: \"Rejected\", val: \"Rejected\" }, { label: \"Graylisted\", val: \"Graylisted\" }, { label: \"Blacklisted\", val: \"Blacklisted\" }]}; const getTagsFromSocialProfileData = profileData => { if (!profileData) return []; const DEPRECATED_CATEGORY_MAPPINGS = { \"social-impact\": \"Social Impact\", \"non-profit\": \"NonProfit\", climate: \"Climate\", \"public-good\": \"Public Good\", \"de-sci\": \"DeSci\", \"open-source\": \"Open Source\", community: \"Community\", education: \"Education\" }; const tags = profileData.plCategories ? JSON.parse(profileData.plCategories) : profileData.category ? [profileData.category.text ?? DEPRECATED_CATEGORY_MAPPINGS[profileData.category] ?? \"\"] : []; return tags;}; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsd = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then(res => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\");const yoctosToUsd = amount => { return nearToUsd ? \"~\\$\" + formatWithCommas(new Big(amount).mul(nearToUsd).div(1e24).toFixed(2)) : \"0\";}; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const getTeamMembersFromSocialProfileData = profileData => { if (!profileData) return []; const team = profileData.plTeam ? JSON.parse(profileData.plTeam) : profileData.team ? Object.entries(profileData.team).filter(([_, v]) => v !== null).map(([k, _]) => k) : []; return team;}; const A_185 = styled.div\\` display: flex; flex-direction: column; padding: 48px 40px 0; @media screen and (max-width: 768px) { padding: 40px 20px 0; }\\`;const A_186 = styled.div\\` display: flex; flex-direction: column; gap: 20px; width: 100%;\\`;const ProjectsContainer = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; overflow-y: hidden; padding-top: 5px; > div { width: 100%; }\\`;const FilterWrapper = styled.div\\` display: flex; gap: 1rem; align-items: center; width: 100%; .filter-menu { left: 0; right: auto; } .left-side-menu { left: auto; right: 0; } @media screen and (max-width: 768px) { flex-wrap: wrap; > div:nth-of-type(2) { width: 100%; order: -1; flex: auto; } .filter-menu { width: 250px !important; } .left-side-menu { right: auto; left: 0; } }\\`;const A_187 = styled.div\\` color: #292929; font-size: 14px; font-style: normal; font-weight: 500; line-height: 24px; letter-spacing: 1.12px; text-transform: uppercase;\\`; const createDebounce = (cb, timeout) => { let timer; const _timeout = timeout || 1000; return args => { clearTimeout(timer); timer = setTimeout(() => { cb(args); }, _timeout); };}; const { projectsData: projectsData } = props; const isRegistryAdmin = ListsSDK.isRegistryAdmin(context.accountId); const [totalDonation, setTotalDonation] = useState(0); const [totalDonated, setTotalDonated] = useState(\"0\"); const [projects, setProjects] = useState([]); const [filteredProjects, setFilteredProjects] = useState([]); const [sort, setSort] = useState(\"Sort\"); useEffect(() => { if (projects.length === 0 && projectsData) { const { allProjects, approvedProjects } = projectsData; setProjects(allProjects); setFilteredProjects(approvedProjects); } }, [projectsData]); if (!projects) { return \"\"; } const donateConfig = DonateSDK.getConfig(); if (donateConfig && !totalDonated && !totalDonation) { const lastDonationAmount = yoctosToUsd(donateConfig.net_donations_amount); setTotalDonated(lastDonationAmount); setTotalDonation(donateConfig.total_donations_count); } const handleSortChange = (sortType) => { setSort(sortType); const projects = [...filteredProjects]; switch (sortType) { case \"All\": break; case \"Newest to Oldest\": projects.sort((a, b) => b.submitted_ms - a.submitted_ms); setFilteredProjects(projects); break; case \"Oldest to Newest\": projects.sort((a, b) => a.submitted_ms - b.submitted_ms); setFilteredProjects(projects); break; default: break; } }; const searchByWords = (searchTerm) => { searchTerm = searchTerm.toLowerCase().trim(); let results = []; projects.forEach((project) => { const { registrant_id, status } = project; const data = Social.getr(\\`\\${registrant_id}/profile\\`); if (registrant_id.includes(searchTerm) || status.toLowerCase().includes(searchTerm)) { results.push(project); } else if (data) { if (data.description.toLowerCase().includes(searchTerm) || data.name.toLowerCase().includes(searchTerm) || getTagsFromSocialProfileData(data).join(\"\").toLowerCase().includes(searchTerm) || getTeamMembersFromSocialProfileData(data).join(\"\").toLowerCase().includes(searchTerm)) { results.push(project); } } }); setFilteredProjects(results); }; const checkAllTrue = (arr) => arr.every((item) => item === true); const filterProjects = (filters) => projects.filter((item) => { const filterVals = Object.keys(filters).map((type) => { if (filters[type].length === 0) return true; if (type === \"Category\") { const data = Social.getr(\\`\\${item.registrant_id}/profile\\`); const tagsForProfile = getTagsFromSocialProfileData(data); return filters[type].some((tag) => tagsForProfile.includes(tag)); } if (type === \"Status\") { if (filters[type].includes(\"all\")) return true; return filters[type].includes(item.status); } return true; }); return checkAllTrue(filterVals); }); const handleTag = (selectedFilters) => { const projectFilterBySearch = filterProjects(selectedFilters); setFilteredProjects(projectFilterBySearch); }; const onSearchChange = createDebounce((searchTerm) => searchByWords(searchTerm), 1000); const SORT_FILTERS = { NEW_TO_OLD: \"Newest to Oldest\", OLD_TO_NEW: \"Oldest to Newest\" }; return <A_185> <A_186> <A_187> All projects <span style={{ color: \"#DD3345\", marginLeft: \"8px\", fontWeight: 600 }}>{filteredProjects.length}</span> </A_187> <FilterWrapper> <Widget loading=\" \" code={props.alem.componentsCode.FilterDropdown} props={{ ...{ ...{ onClick: handleTag, multipleOptions: true, options: A_178, defaultSelected: { Status: [\"Approved\"] }, menuClass: \"filter-menu\" }, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.A_184} props={{ ...{ ...{ title: sort, numItems: filteredProjects.length, itemName: \"project\", sortList: Object.values(SORT_FILTERS), FilterMenuClass: \\`left-side-menu\\`, setSearchTerm: onSearchChange, handleSortChange: (filter) => {handleSortChange(filter);} }, ...props } }} /> </FilterWrapper> </A_186> <ProjectsContainer> {filteredProjects.length ? <Widget loading=\" \" code={props.alem.componentsCode.ListSection} props={{ ...{ items: filteredProjects, renderItem: (project) => <Widget loading=\" \" code={props.alem.componentsCode.A_197} props={{ ...{ projectId: project.registrant_id, ...props } }} />, ...props } }} /> : <div style={{ alignSelf: \"flex-start\", margin: \"24px 0px\" }}>No results</div>} </ProjectsContainer> </A_185>; `, A_197: ` const useDonationModal = () => useContext(\"donation-modal\"); const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const yoctosToNear = (amountYoctos, abbreviate) => { return formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const yoctosToUsdWithFallback = (amountYoctos, abbreviate) => { return A_236 ? \"~\\$\" + formatWithCommas(new Big(amountYoctos).mul(A_236).div(1e24).toFixed(2)) : formatWithCommas(new Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? \"N\" : \" NEAR\");}; const routesPath = { CREATE_PROJECT_TAB: \"createproject\", EDIT_PROJECT_TAB: \"editproject\", PROJECTS_LIST_TAB: \"projects\", PROJECT_DETAIL_TAB: \"project\", CART_TAB: \"cart\", FEED_TAB: \"feed\", POTS_TAB: \"pots\", DEPLOY_POT_TAB: \"deploypot\", POT_DETAIL_TAB: \"pot\", DONORS_TAB: \"donors\", PROFILE_TAB: \"profile\", EDIT_PROFILE_TAB: \"editprofile\"}; const getTagsFromSocialProfileData = profileData => { if (!profileData) return []; const DEPRECATED_CATEGORY_MAPPINGS = { \"social-impact\": \"Social Impact\", \"non-profit\": \"NonProfit\", climate: \"Climate\", \"public-good\": \"Public Good\", \"de-sci\": \"DeSci\", \"open-source\": \"Open Source\", community: \"Community\", education: \"Education\" }; const tags = profileData.plCategories ? JSON.parse(profileData.plCategories) : profileData.category ? [profileData.category.text ?? DEPRECATED_CATEGORY_MAPPINGS[profileData.category] ?? \"\"] : []; return tags;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const ipfsUrlFromCid = cid => { const { IPFS_BASE_URL } = constants; return \\`\\${IPFS_BASE_URL}\\${cid}\\`;}; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const CardContainer = styled.div\\` display: flex; flex-direction: column; max-width: 420px; min-height: 405px; width: 100%; height: 100%; overflow: hidden; border-radius: 12px; background: white; box-shadow: 0px -2px 0px #dbdbdb inset; border: 1px solid #dbdbdb; margin-left: auto; margin-right: auto; transition: all 300ms; &:hover { transform: translateY(-1rem); }\\`;const A_190 = styled.div\\` padding-left: 16px; &:hover { text-decoration: none; cursor: pointer; } @media screen and (max-width: 768px) { border-radius: 0; }\\`;const backgroundStyleHeightPx = 168;const A_191 = styled.div\\` svg { position: absolute; top: \\${backgroundStyleHeightPx / 2}px; left: 50%; transform: translate(-50%, -50%); opacity: 0; transition: opacity 0.3s; z-index: 2; pointer-events: none; }\\`;const A_192 = styled.div\\` transform: translateY(138px); width: 40px; height: 40px; position: absolute; img { width: 40px; height: 40px; } &:hover { cursor: pointer; &:after { /* Dark overlay with 40% opacity on hover */ background-color: rgba(45.9, 45.9, 45.9, 0.4); } svg { opacity: 1; /* Make the image visible on hover */ } }\\`;const A_193 = styled.div\\` display: flex; flex-direction: column; margin-top: 176px; padding: 16px 24px; gap: 16px; flex: 1;\\`;const A_194 = styled.div\\` font-size: 16px; font-weight: 600; color: #2e2e2e; width: 100%;\\`;const A_195 = styled.div\\` font-size: 16px; font-weight: 400; color: #2e2e2e; word-wrap: break-word;\\`;const DonationsInfoContainer = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 16px 24px; width: 100%; border-top: 1px #f0f0f0 solid;\\`;const DonationsInfoItem = styled.div\\` display: flex; flex-direction: row; gap: 8px; align-items: center;\\`;const DonationButton = styled.button\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: #fef6ee; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: #2e2e2e; font-size: 14px; line-height: 16px; font-weight: 600; &:hover { text-decoration: none; cursor: pointer; }\\`;const Amount = styled.div\\` font-size: 17px; font-weight: 600; color: #292929; line-height: 24px;\\`;const AmountDescriptor = styled.div\\` font-size: 11px; font-weight: 400; color: #525252; word-wrap: break-word; text-transform: uppercase; line-height: 16px; letter-spacing: 1.1px;\\`;const Tags = styled.div\\` display: flex; gap: 8px; flex-wrap: wrap;\\`;const A_196 = styled.span\\` box-shadow: 0px -0.699999988079071px 0px rgba(123, 123, 123, 0.36) inset; padding: 4px 8px; border-radius: 4px; border: 1px solid rgba(123, 123, 123, 0.36); color: #2e2e2e;\\`;const MatchingSection = styled.div\\` display: flex; padding: 8px 24px; align-items: center; justify-content: space-between; background: #ebebeb; border-radius: 0px 0px 12px 12px;\\`;const MatchingTitle = styled.div\\` color: #292929; font-size: 11px; font-weight: 400; line-height: 18px; letter-spacing: 1.1px; text-transform: uppercase; word-wrap: break-word;\\`;const MatchingAmount = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`; const loadingSkeleton = styled.keyframes\\` 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; }\\`;const CardSkeletonContainer = styled.div\\` display: flex; flex-direction: column; height: 447px; width: 100%; max-width: 400px; border-radius: 12px; background: white; box-shadow: 0px -2px 0px #dbdbdb inset; border: 1px solid #dbdbdb; margin-left: auto; margin-right: auto; animation-name: \\${loadingSkeleton}; animation-duration: 1s; animation-iteration-count: infinite;\\`;const HeaderSkeleton = styled.div\\` display: block; width: 100%; height: 168px; background: #eee;\\`;const ProfileImageSkeleton = styled.div\\` background: #e0e0e0; margin-left: 32px; transform: translateY(148px); width: 40px; height: 40px; position: absolute; border-radius: 999px;\\`;const TitleSkeleton = styled.div\\` width: 120px; height: 24px; background: #eee; margin-left: 24px; margin-top: 24px;\\`;const DescriptionSkeleton = styled.div\\` width: 83%; height: 48px; background: #eee; margin-left: 24px; margin-top: 24px;\\`;const TagSkeleton = styled.div\\` background: #eee; border-radius: 4px; height: 34px; width: 110px; margin: 24px;\\`;const FooterItemSkeleton = styled.div\\` width: 150px; height: 40px; background: #eee; @media screen and (max-width: 390px) { width: 100px; }\\`;const DonationsInfoContainerSkeleton = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 16px 24px; width: 100%; border-top: 1px #f0f0f0 solid;\\`;const DonationsInfoItemSkeleton = styled.div\\` display: flex; flex-direction: row; gap: 8px; align-items: center;\\`;const CardSkeleton = () => <CardSkeletonContainer> <HeaderSkeleton /> <ProfileImageSkeleton /> <TitleSkeleton /> <DescriptionSkeleton /> <TagSkeleton /> <DonationsInfoContainerSkeleton> <DonationsInfoItemSkeleton> <FooterItemSkeleton /> </DonationsInfoItemSkeleton> <DonationsInfoItemSkeleton> <FooterItemSkeleton /> </DonationsInfoItemSkeleton> </DonationsInfoContainerSkeleton> </CardSkeletonContainer>; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const navigate = { to: (route, params) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } if (routeContext.routes.includes(route)) { routeContext.updateRouteParameters({ ...routeContext, activeRoute: route, routeParams: params || {} }); } }, back: () => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } const updatedHistory = routeContext.history; if (updatedHistory) { updatedHistory.pop(); const routeProps = updatedHistory.at(-1); if (routeProps.route) { routeContext.updateRouteParameters({ ...routeContext, history: updatedHistory, activeRoute: routeProps.route, routeParams: routeProps.routeParams }); } } }}; const RouteLink = ({ to, href, target, params, label, className, style, onClick, children}) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"RouteLink component is being used without Router on top of it.\"); } const onClickHandler = () => { if (onClick) { onClick(); } if (routeContext.routeType === \"ContentBased\") { navigate.to(to, params); } }; if (routeContext.routeType === \"URLBased\") { let strParams = \"\"; if (params) { Object.keys(params).forEach(paramKey => { strParams += \\`&\\${paramKey}=\\${params[paramKey]}\\`; }); } const Link = styled(\"Link\")\\`\\`; return <Link onClick={onClickHandler} className={className} style={{ cursor: \"pointer\", textDecoration: \"none\", ...style }} target={target} href={href ? href : \\`?\\${routeContext.routeParameterName || \"path\"}=\\${to}\\${strParams}\\`}> {label || children} </Link>; } return <a style={{ cursor: \"pointer\", textDecoration: \"none\", ...style }} className={className} onClick={onClickHandler}> {label || children} </a>;}; const props = props; const { payoutDetails, allowDonate: _allowDonate } = props; const { potId } = props.alem.useParams(); const { setDonationModalProps } = useDonationModal(); const [ready, isReady] = useState(false); const projectId = props.project.registrant_id || props.projectId; const profile = Social.getr(\\`\\${projectId}/profile\\`); const allowDonate = _allowDonate ?? true; const MAX_DESCRIPTION_LENGTH = 80; const { name, description } = profile; const donationsForProject = potId && !payoutDetails ? PotSDK.getDonationsForProject(potId, projectId) : !potId ? DonateSDK.getDonationsForRecipient(projectId) : []; useEffect(() => { if (profile !== null && !ready) { isReady(true); } }, [profile, donationsForProject]); const totalAmountNear = useMemo(() => { if (payoutDetails) return payoutDetails.totalAmount; if (!donationsForProject) return \"0\"; let totalDonationAmountNear = new Big(0); for (const donation of donationsForProject) { if (donation.ft_id === \"near\" || donation.base_currency === \"near\" || potId) { totalDonationAmountNear = totalDonationAmountNear.plus(new Big(donation.total_amount)); } } return totalDonationAmountNear.toString(); }, [donationsForProject, payoutDetails]); const getImageSrc = (image) => { const defaultImageUrl = \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\"; if (!image) return defaultImageUrl; const { url, ipfs_cid } = image; if (ipfs_cid) { return ipfsUrlFromCid(ipfs_cid); } else if (url) { return url; } return defaultImageUrl; }; const backgroundImageStyle = { objectFit: \"cover\", left: 0, top: 0, height: \"168px\", borderRadius: \"6px 6px 0px 0px\", pointerEvents: \"none\" }; const profileImageStyle = { width: \"40px\", height: \"40px\", position: \"absolute\", bottom: \"-10px\", left: \"14px\", pointerEvents: \"none\" }; const tags = getTagsFromSocialProfileData(profile); if (!ready) return <CardSkeleton />; return <> <RouteLink to={routesPath.PROJECT_DETAIL_TAB} params={{ projectId, ...(potId ? { potId } : {}) }}> <CardContainer> <A_190 className=\"pt-0 position-relative\"> <A_191> {profile.backgroundImage?.nft ? <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: profile.backgroundImage, alt: \"background\", className: \"position-absolute w-100\", style: backgroundImageStyle, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\" }, ...props } }} /> : <img className=\"position-absolute w-100\" style={backgroundImageStyle} src={getImageSrc(profile.backgroundImage)} alt=\"background\" />} </A_191> <A_192 className=\"profile-picture d-inline-block\"> {profile.image?.nft ? <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...{ image: profile.image, alt: \"avatar\", className: \"rounded-circle w-100 img-thumbnail d-block\", style: profileImageStyle, fallbackUrl: \"https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci\" }, ...props } }} /> : <img className=\"rounded-circle w-100 img-thumbnail d-block\" style={profileImageStyle} src={getImageSrc(profile.image)} alt=\"avatar\" />} </A_192> </A_190> <A_193> <A_194>{_address(name, 30) || _address(projectId, 30)}</A_194> <A_195> {description.length > MAX_DESCRIPTION_LENGTH ? description.slice(0, MAX_DESCRIPTION_LENGTH) + \"...\" : description} </A_195> {!tags.length ? \"No tags\" : <Tags> {tags.map((tag, tagIndex) => <A_196 key={tagIndex}>{tag}</A_196>)} </Tags>} </A_193> <DonationsInfoContainer> <DonationsInfoItem> <Amount>{totalAmountNear ? yoctosToUsdWithFallback(totalAmountNear) : \"-\"}</Amount> <AmountDescriptor>Raised</AmountDescriptor> </DonationsInfoItem> {payoutDetails && <DonationsInfoItem> <Amount>{payoutDetails.donorCount}</Amount> <AmountDescriptor>{payoutDetails.donorCount === 1 ? \"Donor\" : \"Donors\"}</AmountDescriptor> </DonationsInfoItem>} {allowDonate && context.accountId && <DonationButton onClick={(e) => { e.preventDefault(); setDonationModalProps({ projectId }); }} disabled={!context.accountId}> Donate </DonationButton>} </DonationsInfoContainer> {payoutDetails && <MatchingSection> <MatchingTitle>Estimated matched amount</MatchingTitle> <MatchingAmount>{yoctosToNear(payoutDetails.matchingAmount) || \"- N\"}</MatchingAmount> </MatchingSection>} </CardContainer> </RouteLink> </>; `, FeaturedProjects: ` const loadingSkeleton = styled.keyframes\\` 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; }\\`;const CardSkeletonContainer = styled.div\\` display: flex; flex-direction: column; height: 447px; width: 100%; max-width: 400px; border-radius: 12px; background: white; box-shadow: 0px -2px 0px #dbdbdb inset; border: 1px solid #dbdbdb; margin-left: auto; margin-right: auto; animation-name: \\${loadingSkeleton}; animation-duration: 1s; animation-iteration-count: infinite;\\`;const HeaderSkeleton = styled.div\\` display: block; width: 100%; height: 168px; background: #eee;\\`;const ProfileImageSkeleton = styled.div\\` background: #e0e0e0; margin-left: 32px; transform: translateY(148px); width: 40px; height: 40px; position: absolute; border-radius: 999px;\\`;const TitleSkeleton = styled.div\\` width: 120px; height: 24px; background: #eee; margin-left: 24px; margin-top: 24px;\\`;const DescriptionSkeleton = styled.div\\` width: 83%; height: 48px; background: #eee; margin-left: 24px; margin-top: 24px;\\`;const TagSkeleton = styled.div\\` background: #eee; border-radius: 4px; height: 34px; width: 110px; margin: 24px;\\`;const FooterItemSkeleton = styled.div\\` width: 150px; height: 40px; background: #eee; @media screen and (max-width: 390px) { width: 100px; }\\`;const DonationsInfoContainerSkeleton = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 16px 24px; width: 100%; border-top: 1px #f0f0f0 solid;\\`;const DonationsInfoItemSkeleton = styled.div\\` display: flex; flex-direction: row; gap: 8px; align-items: center;\\`;const CardSkeleton = () => <CardSkeletonContainer> <HeaderSkeleton /> <ProfileImageSkeleton /> <TitleSkeleton /> <DescriptionSkeleton /> <TagSkeleton /> <DonationsInfoContainerSkeleton> <DonationsInfoItemSkeleton> <FooterItemSkeleton /> </DonationsInfoItemSkeleton> <DonationsInfoItemSkeleton> <FooterItemSkeleton /> </DonationsInfoItemSkeleton> </DonationsInfoContainerSkeleton> </CardSkeletonContainer>; const ContainerHeader = styled.div\\` display: flex; flex-direction: column; width: 100%; gap: 48px; padding: 48px 40px 0; @media screen and (max-width: 768px) { padding: 40px 8px 0; }\\`;const A_188 = styled.div\\` display: flex; flex-direction: column; gap: 20px; width: 100%;\\`;const A_189 = styled.div\\` color: #292929; font-size: 14px; font-style: normal; font-weight: 500; line-height: 24px; letter-spacing: 1.12px; text-transform: uppercase;\\`;const ProjectList = styled.div\\` display: grid; gap: 31px; /* For mobile devices (1 column) */ @media screen and (max-width: 739px) { grid-template-columns: repeat(1, 1fr); } /* For tablet devices (2 columns) */ @media screen and (min-width: 740px) and (max-width: 1023px) { grid-template-columns: repeat(2, 1fr); } /* For desktop devices (3 columns) */ @media screen and (min-width: 1024px) { grid-template-columns: repeat(3, 1fr); }\\`;const OnBottom = styled.div\\` width: 100%; display: flex; align-items: center; justify-content: center; padding: 20px 0;\\`; const { projectsData: projectsData } = props; const [projects, setProjects] = useState([]); useEffect(() => { if (projectsData) { const { featuredProjects } = projectsData; setProjects(featuredProjects); } }, [projectsData]); const LoadingCards = () => <> <CardSkeleton /> <CardSkeleton /> <CardSkeleton /> </>; const projectCards = useMemo(() => <> {projects.map((project) => { return <Widget loading=\" \" code={props.alem.componentsCode.A_197} props={{ ...{ projectId: project.registrant_id, ...props } }} />; })} </>, [projects]); return <ContainerHeader> <A_188> <A_189>Featured projects</A_189> </A_188> <ProjectList>{projects.length === 0 ? <LoadingCards /> : <>{projectCards}</>}</ProjectList> <OnBottom></OnBottom> </ContainerHeader>; `, ProjectsPage: ` const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const [projectsData, setProjectsDate] = useState(null); if (!projectsData) { ListsSDK.asyncGetRegistrations().then((allProjects) => { if (!allProjects) { return { allProjects: [], featuredProjects: [] }; } allProjects.sort((a, b) => b.submitted_ms - a.submitted_ms); const featuredProjectIds = [\"v1.foodbank.near\", \"potlock.near\", \"yearofchef.near\"]; const featuredProjects = allProjects.filter((project) => featuredProjectIds.includes(project.registrant_id)); const approvedProjects = allProjects.filter((project) => project.status === \"Approved\"); setProjectsDate({ allProjects, approvedProjects, featuredProjects }); }); } return <> <Widget loading=\" \" code={props.alem.componentsCode.NewHero} props={{ ...{ projectsData: projectsData, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.FeaturedProjects} props={{ ...{ projectsData: projectsData, ...props } }} /> <Widget loading=\" \" code={props.alem.componentsCode.AllProjects} props={{ ...{ projectsData: projectsData, ...props } }} /> </>; `, Router: ` const props = props; const { routes, type, parameterName, alem, alemRoutes, initialRoute } = props; useEffect(() => { routes.forEach(route => { if (!route.component) { console.error(\\`Routes: Invalid component for route \"\\${route.path}\"\\`); } if (!route.path) { console.error(\"Routes: Invalid path:\", route.path); } }); }, [routes]); const { routeParameterName, routeType, activeRoute } = alemRoutes; const routeParamName = parameterName || routeParameterName; const checkIfPathIsIncludedToRoutes = routePath => { let pathFound = false; if (routes) { routes.forEach(routeItem => { if (pathFound) return; if (!pathFound) { pathFound = routeItem.path === routePath; } }); } return pathFound; }; useEffect(() => { const bosProps = alem.rootProps; if (routes) { let currentUrlPath = bosProps[routeParamName] && checkIfPathIsIncludedToRoutes(bosProps[routeParamName]) ? bosProps[routeParamName] : routes[0].path; const _routes = routes.map(route => route.path); const _type = type || \"URLBased\"; let _activeRoute = initialRoute || alemRoutes.activeRoute || currentUrlPath; let _activeRouteParams = null; let _history = null; if (alem.keepRoute && type === \"ContentBased\") { const storedRouteProps = Storage.privateGet(\"alem::keep-route\"); if (storedRouteProps) { _history = storedRouteProps; const lastHistory = storedRouteProps[storedRouteProps.length - 1]; _activeRoute = lastHistory.route || null; _activeRouteParams = lastHistory.routeParams || null; } } if (!_activeRoute) { _activeRoute = routes[0].path; } if (!alemRoutes.routesInitialized) { alemRoutes.updateRouteParameters({ routes: _routes, routeType: _type, activeRoute: _activeRoute, routeParams: _activeRouteParams, history: _history, routeParameterName: routeParamName }); } } }, [routeType]); const Component = routes.find(route => route.path === activeRoute)?.component || routes[0].component || <></>; return <Component />; `, ModalSuccess: ` const useDonationModal = () => useContext(\"donation-modal\"); const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const AlertBanner = styled.div\\` display: flex; padding: 0.75rem 1rem; color: #ed464f; gap: 1rem; align-items: center; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; margin-top: 1.5rem; div { font-weight: 500; } .icon { width: 22px; }\\`;const NadabotBanner = styled.div\\` display: flex; align-items: center; padding: 0.75rem 1rem; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; flex-wrap: wrap; margin-top: 1.5rem; .label { display: flex; align-items: center; font-weight: 500; gap: 1rem; img { width: 24px; height: 24px; } } .verify { color: #dd3345; font-weight: 500; margin-left: auto; &:hover { text-decoration: none; } } @media only screen and (max-width: 480px) { flex-direction: column; align-items: flex-start; gap: 0px; .labe { align-items: flex-start; } .verify { margin-left: 40px; } }\\`;const VerifyInfoWrapper = styled.div\\` display: flex; align-items: center; gap: 14px; padding: 1rem; border-radius: 6px; border: 1px solid #ecc113; background: #fbf9c6; box-shadow: 0px 2px 1px 1px rgba(255, 255, 255, 0.8) inset, 0px -2px 4px 0px rgba(15, 15, 15, 0.15) inset; font-size: 14px; color: #3f2209; margin-top: 1.5rem; .icon { width: 17px; display: flex; height: fit-content; svg { width: 100%; } } .text { flex: 1; line-height: 150%; } a { font-weight: 500; color: #dd3345; :hover { text-decoration: none; } } @media only screen and (max-width: 480px) { flex-wrap: wrap; a { width: 100%; text-align: center; } }\\`; const VerifyInfo = () => <VerifyInfoWrapper> <div className=\"icon\"> <svg width=\"18\" height=\"16\" viewBox=\"0 0 18 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0.75 15.125H17.25L9 0.875L0.75 15.125ZM9.75 12.875H8.25V11.375H9.75V12.875ZM9.75 9.875H8.25V6.875H9.75V9.875Z\" fill=\"#ECC113\" /> </svg> </div> <div className=\"text\"> Your contribution won't be matched unless verified as human before the matching round ends. </div> <a href=\"https://app.nada.bot/\" target=\"_blank\"> Verify you’re human </a> </VerifyInfoWrapper>; const formatWithCommas = amount => { return Number(amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });}; const nearToUsd = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then(res => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\");const yoctosToUsd = amount => { return nearToUsd ? \"~\\$\" + formatWithCommas(new Big(amount).mul(nearToUsd).div(1e24).toFixed(2)) : \"0\";}; const Column = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start;\\`;const A_198 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: flex-start;\\`;const ModalMain = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; gap: 2rem; padding: 40px 32px;\\`;const A_199 = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; background: #f6f5f3; gap: 12px; padding: 28px 36px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px;\\`;const A_200 = styled.div\\` display: flex; flex-direction: column; align-items: center; gap: 1rem;\\`;const HeaderIcon = styled.div\\` padding: 12px; width: 48px; height: 48px; border-radius: 50%; background: #dd3345; box-shadow: 0px 0px 0px 6px #fee6e5; svg { width: 100%; height: 100%; }\\`;const TwitterShare = styled.a\\` display: flex; gap: 8px; color: white; border-radius: 4px; padding: 6px 1rem; background: rgb(41, 41, 41); align-items: center; font-size: 14px; cursor: pointer; :hover { text-decoration: none; }\\`;const ModalMiddel = styled.div\\` display: flex; flex-direction: column; gap: 4px; .amount-wrapper { display: flex; align-items: center; gap: 8px; justify-content: center; img, svg { width: 20px; height: 20px; } img { border-radius: 50%; } }\\`;const A_201 = styled.div\\` font-size: 22px; font-weight: 500; letter-spacing: -0.33px; text-transform: uppercase;\\`;const AmountUsd = styled.div\\` color: #7b7b7b; font-size: 22px;\\`;const ProjectName = styled.div\\` display: flex; align-items: center; gap: 3px; font-size: 14px; div { color: #7b7b7b; } a { color: #525252; &:hover { text-decoration: none; } }\\`;const H1 = styled.h1\\` color: #292929; font-size: 24px; font-weight: 600; line-height: 32px; word-wrap: break-word;\\`;const A_202 = styled.div\\` color: #292929; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const UserChip = styled.div\\` display: flex; flex-direction: row; padding: 2px 12px; gap: 4px; border-radius: 32px; background: #ebebeb;\\`;const A_203 = styled.a\\` display: flex; flex-direction: row; padding: 2px 12px; gap: 4px; border-radius: 32px; background: #ebebeb; &:hover { text-decoration: none; }\\`;const ShareText = styled.div\\` color: #7b7b7b; font-size: 14px; font-weight: 600; line-height: 24px; word-wrap: break-word;\\`;const SocialIcon = styled.svg\\` width: 24px; height: 24px; cursor: pointer; path { transition: 300ms; } :hover path { fill: #dd3345; }\\`; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const useRoutes = () => { const contextData = useContext(\"alemRoutes\"); if (!contextData) { console.error(\"useRoutes: You need to call \\`RouterProvider()\\` first.\"); } const data = { routesInitialized: contextData.routesInitialized, activeRoute: contextData.activeRoute, routeParameterName: contextData.routeParameterName, routes: contextData.routes, routeType: contextData.routeType, routeParams: contextData.routeParams, history: contextData.history }; return data;}; const getLocation = () => { const routes = useRoutes(); return { pathname: routes.activeRoute, routes: routes.routes, isRoutesReady: routes.routes && routes.routes.length > 0 };}; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const navigate = { to: (route, params) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } if (routeContext.routes.includes(route)) { routeContext.updateRouteParameters({ ...routeContext, activeRoute: route, routeParams: params || {} }); } }, back: () => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } const updatedHistory = routeContext.history; if (updatedHistory) { updatedHistory.pop(); const routeProps = updatedHistory.at(-1); if (routeProps.route) { routeContext.updateRouteParameters({ ...routeContext, history: updatedHistory, activeRoute: routeProps.route, routeParams: routeProps.routeParams }); } } }}; const DEFAULT_GATEWAY = \"https://bos.potlock.org/\"; const POTLOCK_TWITTER_ACCOUNT_ID = \"PotLock_\"; const DEFAULT_SHARE_HASHTAGS = [\"BOS\", \"PublicGoods\", \"Donations\"]; const params = props.alem.useParams(); const { transactionHashes } = params; const { setSuccessfulDonation: _setSuccessfulDonation, successfulDonation: _successfulDonation } = useDonationModal(); const { NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, ownerId } = constants; State.init({ showBreakdown: false }); const onClose = () => { _setSuccessfulDonation(null); const location = getLocation(); delete params.transactionHashes; navigate.to(location.pathname, params); }; const [successfulDonation, setSuccessfulDonation] = useState(_successfulDonation); const [ftMetadata, setFtMetadata] = useState(null); const { recipientProfile, successfulApplication } = state; const successfulDonationVals = successfulDonation ? Object.values(successfulDonation) : []; const recipientProfileVals = recipientProfile ? Object.values(recipientProfile) : []; const getProfileDataForSuccessfulDonation = (donationsObj) => { const donations = Object.values(donationsObj); donations.forEach((donation) => { const { donor_id, recipient_id, project_id } = donation; Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${recipient_id || project_id}/profile/**\\`] }).then((recipientData) => { Near.asyncView(\"social.near\", \"get\", { keys: [\\`\\${donor_id}/profile/**\\`] }).then((donorData) => { State.update({ recipientProfile: { ...recipientProfile, [recipient_id || project_id]: recipientData[recipient_id || project_id]?.profile || {} }, donorProfile: donorData[donor_id]?.profile || {} }); }); }); }); }; if (successfulDonation && !recipientProfile) { getProfileDataForSuccessfulDonation(successfulDonation); } const getTotalAmount = () => { let totalAmount = Big(0); if (successfulDonation) { successfulDonationVals.forEach((donation) => totalAmount = totalAmount.plus(Big(donation.total_amount))); } return totalAmount.toString(); }; const totalAmount = getTotalAmount(); if (transactionHashes && !successfulDonation) { const transactionHashesList = transactionHashes.split(\",\"); for (let i = 0; i < transactionHashesList.length; i++) { const txHash = transactionHashesList[i]; const body = JSON.stringify({ jsonrpc: \"2.0\", id: \"dontcare\", method: \"tx\", params: [txHash, context.accountId] }); asyncFetch(\"https://rpc.mainnet.near.org\", { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body }).then((res) => { const methodName = res.body.result.transaction.actions[0].FunctionCall.method_name; const successVal = res.body.result.status?.SuccessValue; const receiver_id = res.body.result.transaction.receiver_id; const result = JSON.parse(Buffer.from(successVal, \"base64\").toString(\"utf-8\")); const args = JSON.parse(Buffer.from(res.body.result.transaction.actions[0].FunctionCall.args, \"base64\").toString(\"utf-8\")); const recipientId = methodName === \"donate\" ? result.project_id ? result.project_id : result.recipient_id : methodName === \"ft_transfer_call\" ? JSON.parse(args.msg).recipient_id : \"\"; if (recipientId) { if (methodName === \"donate\") { setSuccessfulDonation((prev) => ({ ...prev, [recipientId]: { ...result, potId: receiver_id } })); } else if (methodName === \"apply\") { State.update({ successfulApplication: result }); } else if (methodName === \"ft_transfer_call\" && recipientId) { asyncFetch(\\`https://near-mainnet.api.pagoda.co/eapi/v1/nep141/metadata/\\${receiver_id}\\`, { headers: { \"Content-Type\": \"application/json\", \"x-api-key\": \"dce81322-81b0-491d-8880-9cfef4c2b3c2\" } }).then((data) => { const metadata = data.body.metadata; setFtMetadata(metadata); setSuccessfulDonation((prev) => ({ ...prev, [recipientId]: { project_id: recipientId, total_amount: result } })); }).catch((err) => console.log(err)); } } }).catch((err) => console.log(err)); } } useEffect(() => { if (successfulDonation && !ftMetadata) { Near.asyncView(successfulDonationVals[0]?.ft_id, \"ft_metadata\", {}).then((metaDate) => setFtMetadata(metaDate)); } }, [successfulDonation]); const twitterIntent = useMemo(() => { if (!recipientProfile) return; const twitterIntentBase = \"https://twitter.com/intent/tweet?text=\"; const recipient_id = successfulDonationVals[0].recipient_id || successfulDonationVals[0].project_id; const profile = recipientProfileVals[0]; const singlePorject = profile ? profile.linktree?.twitter ? \\`@\\${profile.linktree.twitter}\\` : profile.name : recipient_id; const tag = \\`\\${singlePorject}\\` + (successfulDonationVals.length > 1 ? \\` and \\${successfulDonationVals?.length - 1} other\\${successfulDonationVals?.length === 2 ? \"\" : \"s\"}\\` : \"\"); let url = DEFAULT_GATEWAY + (successfulDonationVals[0].potId ? \\`\\${ownerId}/widget/Index?tab=pot&potId=\\${successfulDonationVals[0].potId}&referrerId=\\${context.accountId}\\` : \\`\\${ownerId}/widget/Index?tab=project&projectId=\\${recipient_id}&referrerId=\\${context.accountId}\\`); let text = \\`I just donated to \\${tag} on @\\${POTLOCK_TWITTER_ACCOUNT_ID}! Support public goods at \\`; text = encodeURIComponent(text); url = encodeURIComponent(url); return twitterIntentBase + text + \\`&url=\\${url}\\` + \\`&hashtags=\\${DEFAULT_SHARE_HASHTAGS.join(\",\")}\\`; }, [successfulDonation, recipientProfile]); const isUserHumanVerified = Near.view(NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, { account_id: context.accountId }); const needsToVerify = isUserHumanVerified === false; return <ModalOverlay onOverlayClick={onClose} contentStyle={{ padding: \"0px\" }}> <> {successfulApplication ? <> <ModalMain> <H1>Thank you for applying!</H1> <A_202>Your application status: {successfulApplication.status}</A_202> </ModalMain> </> : successfulDonation ? <ModalMain> <A_200> <HeaderIcon> <svg width=\"18\" height=\"14\" viewBox=\"0 0 18 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M5.79499 10.875L1.62499 6.70498L0.204987 8.11498L5.79499 13.705L17.795 1.70498L16.385 0.294983L5.79499 10.875Z\" fill=\"white\" /> </svg> </HeaderIcon> <div>Donation Successful</div> <TwitterShare href={twitterIntent} target=\"_blank\"> <div>Share to</div> <div className=\"icon\"> <svg width=\"15\" height=\"14\" viewBox=\"0 0 15 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M14.25 13.4035L8.97064 5.70858L8.97965 5.71579L13.7398 0.200562H12.1491L8.27135 4.68956L5.19196 0.200562H1.02012L5.9489 7.38477L5.94831 7.38416L0.75 13.4035H2.34071L6.65183 8.40919L10.0782 13.4035H14.25ZM4.56169 1.40082L11.969 12.2032H10.7084L3.29515 1.40082H4.56169Z\" fill=\"#DBDBDB\" /> </svg> </div> </TwitterShare> </A_200> <ModalMiddel> <div className=\"amount-wrapper\"> {ftMetadata?.icon ? <img src={ftMetadata.icon} /> : <svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg>} <A_201> {totalAmount ? parseFloat(Big(totalAmount).div(Big(10).pow(ftMetadata?.decimals || 24)).toFixed(2)) : \"-\"} {ftMetadata?.symbol || \"NEAR\"} </A_201> {totalAmount && yoctosToUsd(totalAmount) && !ftMetadata && <AmountUsd>{yoctosToUsd(totalAmount)} </AmountUsd>} </div> <ProjectName> <div>Has been donated to</div> <a href={hrefWithParams(\\`?tab=project&projectId=\\${successfulDonationVals[0]?.recipient_id || successfulDonationVals[0]?.project_id}\\`)} target=\"_blank\"> {recipientProfileVals[0]?.name || successfulDonationVals[0]?.recipient_id || successfulDonationVals[0]?.project_id || \"-\"} </a> {successfulDonationVals.length > 1 && <div>and {successfulDonationVals.length - 1} others</div>} </ProjectName> </ModalMiddel> <Widget loading=\" \" code={props.alem.componentsCode.BreakdownSummary} props={{ ...{ ...{ referrerId: successfulDonationVals[0]?.referrer_id, totalAmount: Big(totalAmount).div(Big(10).pow(ftMetadata?.decimals || 24)).toFixed(2), bypassProtocolFee: !successfulDonationVals[0]?.protocol_fee || successfulDonationVals[0]?.protocol_fee === \"0\", ftIcon: ftMetadata?.icon }, ...props } }} /> {needsToVerify && !successfulDonationVals[0]?.recipient_id && <VerifyInfo />} </ModalMain> : null} </> </ModalOverlay>; `, BreakdownSummary: ` const A_237 = (__props__) => <svg {...__props__} style={{ width: 16}} viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg>; const A_211 = styled.svg\\` width: 16px; height: 16px;\\`;const BreakdownSummaryContent = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; .breakdown-details { display: flex; flex-direction: column; width: 100%; margin-top: 8px; gap: 12px; border: 1px #dbdbdb solid; border-radius: 8px; transition: all 300ms ease-in-out; &.hidden { visibility: hidden; height: 0; opacity: 0; transform: translateY(100px); } }\\`;const A_212 = styled.div\\` display: flex; flex-direction: row; justify-content: flex-start; align-items: center; width: 100%;\\`;const BreakdownTitle = styled.div\\` color: #2e2e2e; font-size: 14px; font-weight: 500; word-wrap: break-word;\\`;const ChevronIcon = styled.svg\\` width: 1rem; height: 1rem; margin-left: 8px; transition: all 300ms ease-in-out;\\`;const BreakdownDetails = styled.div\\` display: flex; flex-direction: column; width: 100%; margin-top: 8px; gap: 12px; padding: 16px; border-radius: 8px; border: 1px #dbdbdb solid; background: #fafafa; transition: all 300ms ease-in-out;\\`;const BreakdownItem = styled.div\\` display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 0 16px; gap: 16px; :first-of-type { padding-top: 1rem; } :last-of-type { padding-bottom: 1rem; }\\`;const A_213 = styled.div\\` color: #7b7b7b; font-size: 14px; font-weight: 400; word-wrap: break-word;\\`;const A_214 = styled.div\\` display: flex; flex-direction: row; align-items: center; font-weight: 500; gap: 8px;\\`;const BreakdownAmount = styled.div\\` color: #2e2e2e; font-size: 14px; line-height: 20px; font-weight: 500; word-wrap: break-word;\\`;const A_215 = styled.img\\` width: 16px; height: 16px;\\`; const basisPointsToPercent = basisPoints => { return basisPoints / 100;}; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const {referrerId: referrerId, totalAmount: totalAmount, bypassProtocolFee: bypassProtocolFee, recipientId: recipientId, potRferralFeeBasisPoints: potRferralFeeBasisPoints, ftIcon: ftIcon, bypassChefFee: bypassChefFee, chef: chef, chefFeeBasisPoints: chefFeeBasisPoints} = props; const donationContractConfig = DonateSDK.getConfig(); State.init({ showBreakdown: true }); if (!donationContractConfig) return \"\"; const { protocol_fee_basis_points } = donationContractConfig; const protocolFeeBasisPoints = props.protocolFeeBasisPoints ?? protocol_fee_basis_points; const referralFeeBasisPoints = potRferralFeeBasisPoints || props.referralFeeBasisPoints; const TOTAL_BASIS_POINTS = 10_000; let projectAllocationBasisPoints = TOTAL_BASIS_POINTS - (bypassProtocolFee || !protocolFeeBasisPoints ? 0 : protocolFeeBasisPoints) - (bypassChefFee || !chefFeeBasisPoints ? 0 : chefFeeBasisPoints); if (referrerId) { projectAllocationBasisPoints -= referralFeeBasisPoints; } const projectAllocationPercent = basisPointsToPercent(projectAllocationBasisPoints); const projectAllocationAmount = parseFloat(totalAmount) * projectAllocationBasisPoints / TOTAL_BASIS_POINTS; const protocolFeePercent = basisPointsToPercent(protocolFeeBasisPoints); const protocolFeeAmount = parseFloat(totalAmount) * protocolFeeBasisPoints / TOTAL_BASIS_POINTS; const referrerFeePercent = basisPointsToPercent(referralFeeBasisPoints); const referrerFeeAmount = parseFloat(totalAmount) * referralFeeBasisPoints / TOTAL_BASIS_POINTS; const chefFeePercent = basisPointsToPercent(chefFeeBasisPoints); const chefFeeAmount = parseFloat(totalAmount) * chefFeeBasisPoints / TOTAL_BASIS_POINTS; const fees = [{ label: \"Protocol fee\", percentage: protocolFeePercent, amount: protocolFeeAmount, show: !bypassProtocolFee }, { label: \"Referrer fee\", percentage: referrerFeePercent, amount: referrerFeeAmount, show: referrerId }, { label: \"Chef fee\", percentage: chefFeePercent, amount: chefFeeAmount, show: !bypassChefFee && chefFeeBasisPoints }, { label: \"On-Chain Storage\", percentage: \"\", amount: \"<0.01\", show: true }, { label: \"Project allocation\", percentage: projectAllocationPercent, amount: projectAllocationAmount, show: true }]; return <BreakdownSummaryContent style={props.containerStyle || {}}> <A_212 style={props.headerStyle || {}}> <BreakdownTitle> Breakdown</BreakdownTitle> </A_212> <div className={\\`breakdown-details \\${!state.showBreakdown ? \"hidden\" : \"\"}\\`} active={state.showBreakdown}> {fees.map(({ show, amount, label, percentage }) => { return show ? <BreakdownItem key={label}> <A_213> {label} {percentage ? \\`(\\${percentage}%)\\` : \"\"}{\" \"} </A_213> <A_214> <BreakdownAmount>{typeof amount === \"string\" ? amount : amount}</BreakdownAmount> {ftIcon ? <A_215 src={ftIcon} alt=\"ft-icon\" /> : <A_237 />} </A_214> </BreakdownItem> : \"\"; })} </div> </BreakdownSummaryContent>; `, NftImage: ` const props = props; const nft = props.nft ?? { contractId: props.contractId, tokenId: props.tokenId }; const contractId = nft.contractId; const tokenId = nft.tokenId; const className = props.className ?? \"img-fluid\"; const style = props.style; const alt = props.alt; const thumbnail = props.thumbnail; const fallbackUrl = props.fallbackUrl; const loadingUrl = props.loadingUrl ?? \"https://ipfs.near.social/ipfs/bafkreidoxgv2w7kmzurdnmflegkthgzaclgwpiccgztpkfdkfzb4265zuu\"; State.init({ contractId, tokenId, imageUrl: null }); if (contractId !== state.contractId || tokenId !== tokenId) { State.update({ contractId, tokenId, imageUrl: null }); } const nftMetadata = nft.contractMetadata ?? Near.view(contractId, \"nft_metadata\"); const tokenMetadata = nft.tokenMetadata ?? Near.view(contractId, \"nft_token\", { token_id: tokenId }).metadata; let imageUrl = null; if (nftMetadata && tokenMetadata) { let tokenMedia = tokenMetadata.media || \"\"; imageUrl = tokenMedia.startsWith(\"https://\") || tokenMedia.startsWith(\"http://\") || tokenMedia.startsWith(\"data:image\") ? tokenMedia : nftMetadata.base_uri ? \\`\\${nftMetadata.base_uri}/\\${tokenMedia}\\` : tokenMedia.startsWith(\"Qm\") || tokenMedia.startsWith(\"ba\") ? \\`https://ipfs.near.social/ipfs/\\${tokenMedia}\\` : tokenMedia; if (!tokenMedia && tokenMetadata.reference) { if (nftMetadata.base_uri === \"https://arweave.net\" && !tokenMetadata.reference.startsWith(\"https://\")) { const res = fetch(\\`\\${nftMetadata.base_uri}/\\${tokenMetadata.reference}\\`); imageUrl = res.body.media; } else if (tokenMetadata.reference.startsWith(\"https://\") || tokenMetadata.reference.startsWith(\"http://\")) { const res = fetch(tokenMetadata.reference); imageUrl = JSON.parse(res.body).media; } else if (tokenMetadata.reference.startsWith(\"ar://\")) { const res = fetch(\\`\\${\"https://arweave.net\"}/\\${tokenMetadata.reference.split(\"//\")[1]}\\`); imageUrl = JSON.parse(res.body).media; } } if (!imageUrl) { imageUrl = false; } } const rex = /^(?:https?:\\\\/\\\\/)(?:[^\\\\/]+\\\\/ipfs\\\\/)?(Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,})(?:\\\\.[^\\\\/]+)?(\\\\/.*)?$/g; rex.lastIndex = 0; const replaceIpfs = imageUrl => { if (state.oldUrl !== imageUrl && imageUrl) { const match = rex.exec(imageUrl); if (match) { const newImageUrl = \\`https://ipfs.near.social/ipfs/\\${match[1]}\\${match[2] || \"\"}\\`; if (newImageUrl !== imageUrl) { State.update({ oldUrl: imageUrl, imageUrl: newImageUrl }); return; } } } if (state.imageUrl !== false) { State.update({ imageUrl: false }); } }; const thumb = imageUrl => thumbnail && imageUrl && !imageUrl.startsWith(\"data:image/\") ? \\`https://i.near.social/\\${thumbnail}/\\${imageUrl}\\` : imageUrl; const img = state.imageUrl !== null ? state.imageUrl : imageUrl; const src = img !== false ? img : fallbackUrl; return <img className={className} style={style} src={src !== null ? thumb(src) : loadingUrl} alt={alt} onError={() => replaceIpfs(img)} />; `, Image: ` const props = props; const image = props.image; const className = props.className; const style = props.style; const alt = props.alt; const fallbackUrl = props.fallbackUrl; const thumbnail = props.thumbnail; State.init({ image }); if (JSON.stringify(image) !== JSON.stringify(state.image)) { State.update({ image, imageUrl: null }); } function toUrl(image) { return (image.ipfs_cid ? \\`https://ipfs.near.social/ipfs/\\${image.ipfs_cid}\\` : image.url) || fallbackUrl; } const thumb = (imageUrl) => thumbnail && imageUrl && !imageUrl.startsWith(\"data:image/\") ? \\`https://i.near.social/\\${thumbnail}/\\${imageUrl}\\` : imageUrl; return image.nft.contractId && image.nft.tokenId ? <Widget loading=\" \" code={props.alem.componentsCode.NftImage} props={{ ...{ ...{ className, style, alt, nft: image.nft, thumbnail, fallbackUrl }, ...props } }} /> : <img className={className} style={style} src={state.imageUrl ? thumb(state.imageUrl) : thumb(toUrl(image))} alt={alt} onError={() => { if (state.imageUrl !== fallbackUrl) { State.update({ imageUrl: fallbackUrl }); } }} />;`, OverlayTrigger: ` const props = props; const showTimer = 250; const hideTimer = 300; const handleOnMouseEnter = () => { clearTimeout(state.debounce); State.update({ debounce: setTimeout(() => State.update({ show: true }), showTimer) }); }; const handleOnMouseLeave = () => { clearTimeout(state.debounce); State.update({ debounce: setTimeout(() => State.update({ show: false }), hideTimer) }); }; State.init({ show: false }); const overlayClassName = props.overlayClassName ?? \"border m-3 p-3 rounded-4 bg-white shadow\"; const overlayStyle = props.overlayStyle ?? { maxWidth: \"24em\", zIndex: 1070 }; const overlay = <div className={overlayClassName} style={overlayStyle} onMouseEnter={handleOnMouseEnter} onMouseLeave={handleOnMouseLeave}> {props.popup} </div>; return <OverlayTrigger show={state.show} trigger={[\"hover\", \"focus\"]} delay={{ show: showTimer, hide: hideTimer }} placement=\"auto\" overlay={overlay}> <span className=\"d-inline-flex\" onMouseEnter={handleOnMouseEnter} onMouseLeave={handleOnMouseLeave}> {props.children} </span> </OverlayTrigger>; `, A_238: ` const FilterButton = styled.div\\` white-space: nowrap; display: flex; cursor: pointer; gap: 12px; align-items: center; font-size: 14px; font-weight: 500; line-height: 20px; border: 1px solid #292929; padding: 0.5rem 1rem; border-radius: 6px; color: #292929; * { font-weight: 500; }\\`;const FilterIcon = styled.div\\` display: flex; width: 16px; height: 16px; align-items: center; justify-content: center;\\`;const FilterMenu = styled.div\\` position: absolute; background: #fff; top: 140%; right: 0; padding: 8px; display: flex; flex-direction: column; gap: 8px; border-radius: 6px; border: 1px solid rgba(41, 41, 41, 0.36); box-shadow: 0px 12px 20px -4px rgba(123, 123, 123, 0.32), 0px 4px 8px -3px rgba(123, 123, 123, 0.2), 0px 0px 2px 0px rgba(123, 123, 123, 0.36); z-index: 3;\\`;const FilterItem = styled.div\\` cursor: pointer; padding: 8px; display: flex; justify-content: space-between; align-items: center; gap: 12px; white-space: nowrap; transition: all 300ms ease-in-out; &:hover { color: #fff; background: #292929; border-radius: 6px; .count { color: #fff; } } .count { color: #7b7b7b; }\\`;const Screen = styled.div\\` position: fixed; width: 100%; height: 100%; left: 0; top: 0;\\`; const componentProps = props; const [openFilter, setOpenFilter] = useState(false); const { sortList, sortVal, title, handleSortChange, FilterMenuCustomClass, showCount } = componentProps; const menuStyle = componentProps.menuStyle || {}; const buttonStyle = componentProps.buttonStyle || {}; return <> {openFilter && <Screen onClick={() => setOpenFilter(false)} />} <div style={{ position: \"relative\" }} onClick={() => setOpenFilter(!openFilter)}> <FilterButton style={buttonStyle || {}}> {sortVal || title || \"\"} <FilterIcon> <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M8 3.88667L10.1133 6L11.0533 5.06L8 2L4.94 5.06L5.88667 6L8 3.88667ZM8 12.1133L5.88667 10L4.94667 10.94L8 14L11.06 10.94L10.1133 10L8 12.1133Z\" fill=\"#7B7B7B\" /> </svg> </FilterIcon> </FilterButton> {openFilter && <FilterMenu onClick={e => e.stopPropagation()} className={FilterMenuCustomClass || \"\"} style={menuStyle}> {sortList.map(option => <FilterItem key={option.val} onClick={() => { setOpenFilter(false); handleSortChange(option); }}> {option.label} <div className=\"count\">{showCount ? option.count : \"\"}</div> </FilterItem>)} </FilterMenu>} </div> </>; `, ModalDonation: ` const useCart = () => useContext(\"cart-context\"); const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useDonationModal = () => useContext(\"donation-modal\"); const A_204 = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem 2rem; gap: 1.5rem; @media only screen and (max-width: 480px) { padding: 1.5rem 1.125rem; }\\`;const ContentScrollable = styled.div\\` display: flex; flex-direction: column; gap: 1.5rem; overflow-y: scroll; height: 450px;\\`;const A_205 = styled.div\\` font-weight: 500; margin-bottom: 4px;\\`;const Amout = styled.div\\` display: flex; align-items: center; gap: 0.5rem; cursor: pointer; span { font-weight: 600; } div { font-weight: 600; font-size: 1rem; } .usd-amount { color: #7b7b7b; } .toggle-icon { width: 8px; display: flex; margin-left: auto; overflow: hidden; svg { width: 100%; transition: all 300ms ease-in-out; } } img, svg { width: 20px; }\\`;const SvgIcon = styled.svg\\` width: 16px;\\`;const FeesRemoval = styled.div\\` display: flex; flex-direction: column; gap: 1rem; .check { display: flex; align-items: center; flex-wrap: wrap; } .label { margin-right: 4px; margin-left: 8px; } .address { font-weight: 600; gap: 4px; display: flex; align-items: center; color: #292929; transition: all 300ms; &:hover { color: #dd3345; text-decoration: none; } } .profile-image { width: 17px; height: 17px; display: flex !important; } @media only screen and (max-width: 480px) { .address { margin-left: 34px; width: 100%; } }\\`;const ButtonWrapper = styled.div\\` display: flex; margin-top: 4rem; margin-bottom: 0.5rem; gap: 1rem; justify-content: flex-end; @media only screen and (max-width: 480px) { margin-top: 2rem; }\\`;const A_206 = styled.div\\` padding: 8px 0; display: flex; flex-direction: column; border-radius: 8px; border: 1px solid #ebebeb; background: rgba(235, 235, 235, 0.24); transition: all 300ms ease-in-out; &.hidden { max-height: 0; overflow: hidden; opacity: 0; } .project { display: flex; align-items: center; gap: 1rem; padding: 0.5rem 1rem; transition: 300ms ease-in-out; } .profile-image { width: 40px; height: 40px; box-shadow: 0px 0px 1px 0px #a6a6a6 inset; border-radius: 50%; } .info { display: flex; flex-direction: column; .name { font-weight: 600; } .address { color: #7b7b7b; transition: all 300ms; &:hover { text-decoration: none; color: #dd3345; } } }\\`;const ProjectAmount = styled.div\\` margin-left: auto; display: flex; gap: 1rem; align-items: center; div { font-weight: 600; } svg { width: 16px; }\\`; const ConfirmPot = ({ selectedDenomination, bypassProtocolFee, bypassChefFee, updateState, potDetail, potId, referrerId, accountId, amount, openDonationSuccessModal, selectedProjects, donationType, toggleAmount, onClose }) => { const ProfileImg = ({ accountId, profile }) => <ProfileImage accountId={accountId} profile={profile} style={{}} />; const CheckBoxWrapper = ({ id, checked, onClick }) => <CheckBox {...{ id, checked, onClick }} />; const getFeesBasisPoints = (protocolConfig, potDetail) => { if (protocolConfig) { return [protocolConfig.account_id, protocolConfig.basis_points, potDetail.referral_fee_public_round_basis_points]; } else { return [\"\", 0, 0]; } }; const pollForDonationSuccess = ({ projectIds, afterTs }) => { const pollIntervalMs = 1000; const pollId = setInterval(() => { PotSDK.asyncGetDonationsForDonor(potId, accountId).then((alldonations) => { const donations = {}; for (const donation of alldonations) { const { project_id, donated_at_ms, donated_at } = donation; if (projectIds.includes(project_id) && (donated_at_ms || donated_at) > afterTs) { donations[project_id] = { ...donation, potId }; } } if (Object.keys(donations).length === projectIds.length) { clearInterval(pollId); openDonationSuccessModal(donations); } }).catch((err) => { console.log(err); onClose(); }); setTimeout(() => { onClose(); clearInterval(pollId); }, 60000); }, pollIntervalMs); }; const protocolConfigContractId = potDetail.protocol_config_provider.split(\":\")[0]; const protocolConfigViewMethodName = potDetail.protocol_config_provider.split(\":\")[1]; const protocolConfig = protocolConfigContractId && protocolConfigViewMethodName ? Near.view(protocolConfigContractId, protocolConfigViewMethodName, {}) : null; const [protocolFeeRecipientAccount, protocolFeeBasisPoints, referralFeeBasisPoints] = getFeesBasisPoints(protocolConfig, potDetail); const chefFeeBasisPoints = potDetail?.chef_fee_basis_points; const donationAmountIndivisible = (num) => Big(num).mul(new Big(10).pow(selectedDenomination.decimals)); const projectAmount = parseFloat(amount) / Object.keys(selectedProjects).length; const autoProjectAmount = donationAmountIndivisible(projectAmount); const handleDonate = () => { const now = Date.now(); const successArgs = { projectIds: Object.keys(selectedProjects), afterTs: now }; const transactions = []; Object.keys(selectedProjects).forEach((project) => { let amount = 0; if (donationType === \"auto\") { amount = autoProjectAmount; } else { amount = donationAmountIndivisible(selectedProjects[project]); } if (amount) { transactions.push({ contractName: potId, methodName: \"donate\", args: { referrer_id: referrerId, project_id: project, bypass_protocol_fee: bypassProtocolFee, ...(bypassChefFee ? { custom_chef_fee_basis_points: 0 } : {}) }, deposit: amount.toFixed(0), gas: \"300000000000000\" }); } }); Near.call(transactions); pollForDonationSuccess(successArgs); }; return <A_204> <ContentScrollable> <div> <A_205>Total amount</A_205> <Amout onClick={() => updateState({ toggleAmount: !toggleAmount })}> <div>{selectedDenomination.icon ? <img src={selectedDenomination.icon} /> : <A_237 />}</div> <div> {amount} <span>{selectedDenomination.text}</span> </div> {A_236 && <div className=\"usd-amount\">~\\${(A_236 * amount).toFixed(2)}</div>} <div className=\"toggle-icon\"> <svg viewBox=\"0 0 8 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style={{ rotate: !toggleAmount ? \"\" : \"90deg\" }}> <path d=\"M1.70501 0L0.295013 1.41L4.87501 6L0.295013 10.59L1.70501 12L7.70501 6L1.70501 0Z\" fill=\"#7B7B7B\" /> </svg> </div> </Amout> </div> <A_206 className={\\`\\${!toggleAmount ? \"hidden\" : \"\"}\\`}> {Object.keys(selectedProjects).map((project_id) => { const profile = Social.getr(\\`\\${project_id}/profile\\`); return selectedProjects[project_id] > 0 || donationType === \"auto\" ? <div className={\\`project\\`} key={project_id}> <ProfileImg profile={profile} /> <div className=\"info\"> {profile?.name && <div className=\"name\">{_address(profile?.name, 20)}</div>} <a className=\"address\" href={hrefWithParams(\\`?tab=project&projectId=\\${project_id}\\`)} target=\"_blank\"> {_address(project_id, 20)} </a> </div> <ProjectAmount> <div> {\" \"} {donationType === \"auto\" ? projectAmount < 0.01 ? \"<0.01\" : projectAmount.toFixed(2) : selectedProjects[project_id]}{\" \"} </div> <A_237 /> </ProjectAmount> </div> : \"\"; })} </A_206> <Widget loading=\" \" code={props.alem.componentsCode.BreakdownSummary} props={{ ...{ ...{ ...props, referrerId, protocolFeeBasisPoints, referralFeeBasisPoints, bypassChefFee, chef: potDetail?.chef, chefFeeBasisPoints, totalAmount: amount, bypassProtocolFee: bypassProtocolFee, ftIcon: selectedDenomination.icon }, ...props } }} /> <FeesRemoval> <div className=\"check\"> <CheckBoxWrapper id=\"bypassProtocolFeeSelector\" checked={bypassProtocolFee} onClick={(e) => { updateState({ bypassProtocolFee: e.target.checked }); }} /> <div className=\"label\">Remove {protocolFeeBasisPoints / 100 || \"-\"}% protocol fee</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${protocolFeeRecipientAccount}\\`)} className=\"address\" target=\"_blank\"> <ProfileImg accountId={protocolFeeRecipientAccount} /> {protocolFeeRecipientAccount} </a> </div> {potDetail?.chef && chefFeeBasisPoints > 0 && <div className=\"check\"> <CheckBoxWrapper id=\"bypassChefFeeSelector\" checked={bypassChefFee} onClick={(e) => { updateState({ bypassChefFee: e.target.checked }); }} /> <div className=\"label\"> Remove {chefFeeBasisPoints / 100 || \"-\"}% chef fee</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${potDetail?.chef}\\`)} className=\"address\" target=\"_blank\"> <ProfileImg accountId={potDetail?.chef} /> {potDetail?.chef} </a> </div>} </FeesRemoval> </ContentScrollable> <ButtonWrapper> <A_247 className=\"filled\" onClick={handleDonate}> Confirm donation </A_247> </ButtonWrapper> </A_204>; }; const A_207 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_208 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_209 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`;const A_210 = styled.textarea\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; padding: 0.5em 0.75em; width: 100%; gap: 0.5em; background: #ffffff; border: 1px solid #d0d5dd; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); border-radius: 4px;\\`; const TextArea = (__props__) => { const label = __props__.label ?? \"Label\"; const placeholder = __props__.placeholder ?? \"Placeholder\"; const value = __props__.value ?? \"\"; const onChange = __props__.onChange ?? (() => {}); const validate = __props__.validate ?? (() => {}); const error = __props__.error ?? \"\"; return <A_207 style={__props__.containerStyle ?? {}}> {!__props__.noLabel && <A_208 style={__props__.labelStyle ?? {}}>{label}</A_208>} <A_210 placeholder={placeholder} value={value} onChange={({ target: { value } }) => onChange(value)} onBlur={() => validate()} rows={__props__.inputRows ?? 5} style={__props__.inputStyle ?? {}} disabled={!!__props__.disabled} /> <A_209 style={__props__.errorStyle ?? {}} className={error ? \"show\" : \"\"}> {error} </A_209> </A_207>;}; const A_216 = styled.div\\` display: flex; gap: 8px; flex-direction: row; align-items: center;\\`;const CheckBoxContent = styled.input\\` width: 18px; height: 18px; padding: 0px; appearance: checkbox; cursor: pointer; /* TODO: update background color when selected */\\`;const A_217 = styled.label\\`\\`;const A_218 = styled.span\\` display: inline-block; font-style: normal; font-weight: 400; font-size: 0.75em; line-height: 1.25em; color: #ff4d4f; height: 0; overflow: hidden; transition: height 0.3s ease-in-out; &.show { height: 1.25em; }\\`; const CheckBox = (__props__) => { const { id, disabled, checked, onClick } = __props__; const containerStyle = __props__.containerStyle ?? {}; const checkBoxStyle = __props__.checkBoxStyle ?? {}; const labelStyle = __props__.labelStyle ?? {}; const error = __props__.error ?? \"\"; return <A_216 style={containerStyle}> <CheckBoxContent type=\"checkbox\" style={checkBoxStyle} id={id} disabled={disabled} checked={checked} onClick={onClick} /> {__props__.label && <A_217 htmlFor={id} style={labelStyle}> {__props__.label} </A_217>} <A_218 className={error ? \"show\" : \"\"}>{error}</A_218> </A_216>;}; const A_219 = styled.div\\` display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem 2rem;\\`;const A_220 = styled.div\\` font-weight: 500; margin-bottom: 4px;\\`;const A_221 = styled.div\\` display: flex; align-items: center; gap: 0.5rem; span { font-weight: 600; } div { font-weight: 600; font-size: 1rem; } .usd-amount { color: #7b7b7b; } img, svg { width: 20px; }\\`;const A_222 = styled.svg\\` width: 16px;\\`;const NoteWrapper = styled.div\\` display: flex; cursor: pointer; align-items: center; gap: 8px; svg { width: 14px; } div { font-weight: 500; }\\`;const A_223 = styled.div\\` display: flex; flex-direction: column; gap: 1rem; .check { display: flex; flex-wrap: wrap; align-items: center; } .label { margin-right: 4px; margin-left: 8px; } .address { font-weight: 600; gap: 4px; display: flex; align-items: center; color: #292929; transition: all 300ms; &:hover { color: #dd3345; text-decoration: none; } } .profile-image { width: 17px; height: 17px; display: flex !important; } @media only screen and (max-width: 480px) { .address { margin-left: 34px; width: 100%; } }\\`;const A_224 = styled.div\\` display: flex; margin-top: 2rem; margin-bottom: 0.5rem; justify-content: flex-end;\\`; const donateContractId = \"donate.potlock.near\";const DonateSDK = { getContractId: () => donateContractId, getConfig: () => { return Near.view(donateContractId, \"get_config\", {}); }, asyncGetConfig: () => { return Near.asyncView(donateContractId, \"get_config\", {}); }, getDonations: (fromIndex, limit) => { return Near.view(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, asyncGetDonations: (fromIndex, limit) => { return Near.asyncView(donateContractId, \"get_donations\", { from_index: fromIndex || null, limit: limit || null }); }, getDonationsForRecipient: recipientId => { return Near.view(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: recipientId => { return Near.asyncView(donateContractId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getDonationsForProject: projectId => {}, getDonationsForDonor: donorId => { return Near.view(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }, asyncGetDonationsForDonor: donorId => { return Near.asyncView(donateContractId, \"get_donations_for_donor\", { donor_id: donorId }); }};DonateSDK; const ConfirmDirect = ({ selectedDenomination, bypassProtocolFee, bypassChefFee, donationNote, donationNoteError, addNote, updateState, selectedRound, projectId, referrerId, accountId, amount, openDonationSuccessModal, donationType, onClose }) => { const getFeesBasisPoints = (protocolConfig, potDetail, donationContractConfig) => { if (protocolConfig) { return [protocolConfig.account_id, protocolConfig.basis_points, potDetail.referral_fee_public_round_basis_points]; } else if (donationContractConfig) { return [donationContractConfig.protocol_fee_recipient_account, donationContractConfig.protocol_fee_basis_points, donationContractConfig.referral_fee_basis_points]; } else { return [\"\", 0, 0]; } }; const pollForDonationSuccess = ({ projectId: donatedProject, afterTs, accountId, isPotDonation }) => { const pollIntervalMs = 1000; const totalPollTimeMs = 60000; const pollId = setInterval(() => { (isPotDonation ? PotSDK.asyncGetDonationsForDonor(selectedRound, accountId) : DonateSDK.asyncGetDonationsForDonor(accountId)).then((donations) => { for (const donation of donations) { const { recipient_id, project_id, donated_at_ms, donated_at } = donation; if ((project_id || recipient_id) === donatedProject && (donated_at_ms || donated_at) > afterTs || donated_at > afterTs) { clearInterval(pollId); openDonationSuccessModal({ [donatedProject]: donation }); } } }); setTimeout(() => { onClose(); clearInterval(pollId); }, totalPollTimeMs); }, pollIntervalMs); }; const ProfileImg = ({ accountId }) => <ProfileImage accountId={accountId} style={{}} />; const CheckBoxWrapper = ({ id, checked, onClick }) => <CheckBox {...{ id, checked, onClick }} />; const potDetail = PotSDK.getConfig(selectedRound); const protocolConfigContractId = potDetail ? potDetail.protocol_config_provider.split(\":\")[0] : \"\"; const protocolConfigViewMethodName = potDetail ? potDetail.protocol_config_provider.split(\":\")[1] : \"\"; const protocolConfig = protocolConfigContractId && protocolConfigViewMethodName ? Near.view(protocolConfigContractId, protocolConfigViewMethodName, {}) : null; const donationContractConfig = !potDetail ? DonateSDK.getConfig() || {} : null; const [protocolFeeRecipientAccount, protocolFeeBasisPoints, referralFeeBasisPoints] = getFeesBasisPoints(protocolConfig, potDetail, donationContractConfig); const chefFeeBasisPoints = donationType === \"pot\" ? potDetail?.chef_fee_basis_points : \"\"; const storageBalanceBounds = Near.view(selectedDenomination.id, \"storage_balance_bounds\", {}); const storageBalanceProtocolFeeRecipient = Near.view(selectedDenomination.id, \"storage_balance_of\", { account_id: protocolFeeRecipientAccount }); const storageBalanceReferrer = referrerId ? Near.view(selectedDenomination.id, \"storage_balance_of\", { account_id: referrerId }) : null; const storageBalanceDonationContract = Near.view(selectedDenomination.id, \"storage_balance_of\", { account_id: constants.DONATION_CONTRACT_ID }); const handleDonate = () => { const donationAmountIndivisible = Big(amount).mul(new Big(10).pow(selectedDenomination.decimals)); const args = { referrer_id: referrerId, bypass_protocol_fee: bypassProtocolFee, message: donationNote, ...(bypassChefFee ? { custom_chef_fee_basis_points: 0 } : {}) }; const potId = selectedRound || null; const isPotDonation = potId && donationType === \"pot\"; const now = Date.now(); const successArgs = { projectId, afterTs: now, accountId, isPotDonation }; if (isPotDonation) { args.project_id = projectId; if (bypassChefFee) { args.custom_chef_fee_basis_points = 0; } } else { args.recipient_id = projectId; } const transactions = []; const isFtDonation = selectedDenomination.text !== \"NEAR\"; if (isFtDonation) { const ftId = selectedDenomination.id; let requiredDepositFloat = 0.012; requiredDepositFloat += 0.0001 * args.message.length; transactions.push({ contractName: constants.DONATION_CONTRACT_ID, methodName: \"storage_deposit\", args: {}, deposit: Big(requiredDepositFloat).mul(Big(10).pow(24)), gas: \"100000000000000\" }); const { min, max } = storageBalanceBounds; const storageMaxBig = Big(max); if (!args.bypass_protocol_fee && (!storageBalanceProtocolFeeRecipient || Big(storageBalanceProtocolFeeRecipient.total).lt(storageMaxBig))) { transactions.push({ contractName: ftId, methodName: \"storage_deposit\", args: { account_id: protocolFeeRecipientAccount }, deposit: storageMaxBig.minus(Big(storageBalanceProtocolFeeRecipient || 0)), gas: \"100000000000000\" }); } if (referrerId && (!storageBalanceReferrer || Big(storageBalanceReferrer.total).lt(storageMaxBig))) { transactions.push({ contractName: ftId, methodName: \"storage_deposit\", args: { account_id: referrerId }, deposit: storageMaxBig.minus(Big(storageBalanceReferrer || 0)), gas: \"100000000000000\" }); } if (!storageBalanceDonationContract || Big(storageBalanceDonationContract.total).lt(storageMaxBig)) { transactions.push({ contractName: ftId, methodName: \"storage_deposit\", args: { account_id: constants.DONATION_CONTRACT_ID }, deposit: storageMaxBig.minus(Big(storageBalanceDonationContract || 0)), gas: \"100000000000000\" }); } Near.asyncView(ftId, \"storage_balance_of\", { account_id: projectId }).then((balance) => { if (!balance || Big(balance.total).lt(storageMaxBig)) { transactions.push({ contractName: ftId, methodName: \"storage_deposit\", args: { account_id: projectId }, deposit: storageMaxBig.minus(Big(balance || 0)), gas: \"100000000000000\" }); } transactions.push({ contractName: ftId, methodName: \"ft_transfer_call\", args: { receiver_id: constants.DONATION_CONTRACT_ID, amount: donationAmountIndivisible.toFixed(0), msg: JSON.stringify({ recipient_id: projectId, referrer_id: referrerId || null, bypass_protocol_fee: bypassProtocolFee, message: args.message }) }, deposit: \"1\", gas: \"300000000000000\" }); Near.call(transactions); pollForDonationSuccess(successArgs); }); } else { transactions.push({ contractName: isPotDonation ? potId : constants.DONATION_CONTRACT_ID, methodName: \"donate\", args, deposit: donationAmountIndivisible.toFixed(0), gas: \"300000000000000\" }); Near.call(transactions); pollForDonationSuccess(successArgs); } }; const MAX_NOTE_LENGTH = 60; return <A_219> <div> <A_220>Total amount</A_220> <A_221> <div>{selectedDenomination.icon ? <img src={selectedDenomination.icon} /> : <A_237 />}</div> <div> {amount} <span>{selectedDenomination.text}</span> </div> {A_236 && selectedDenomination.text === \"NEAR\" && <div className=\"usd-amount\">~\\${(A_236 * amount).toFixed(2)}</div>} </A_221> </div> <div> <Widget loading=\" \" code={props.alem.componentsCode.BreakdownSummary} props={{ ...{ ...{ ...props, referrerId, protocolFeeBasisPoints, referralFeeBasisPoints, bypassChefFee, chef: potDetail?.chef, chefFeeBasisPoints, totalAmount: amount, bypassProtocolFee: bypassProtocolFee, ftIcon: selectedDenomination.icon }, ...props } }} /> </div> <A_223> <div className=\"check\"> <CheckBoxWrapper id=\"bypassProtocolFeeSelector\" checked={bypassProtocolFee} onClick={(e) => { updateState({ bypassProtocolFee: e.target.checked }); }} /> <div className=\"label\">Remove {protocolFeeBasisPoints / 100 || \"-\"}% protocol fee</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${protocolFeeRecipientAccount}\\`)} className=\"address\" target=\"_blank\"> <ProfileImg accountId={protocolFeeRecipientAccount} /> {protocolFeeRecipientAccount} </a> </div> {potDetail?.chef && chefFeeBasisPoints > 0 && <div className=\"check\"> <CheckBoxWrapper id=\"bypassChefFeeSelector\" checked={bypassChefFee} onClick={(e) => { updateState({ bypassChefFee: e.target.checked }); }} /> <div className=\"label\"> Remove {chefFeeBasisPoints / 100 || \"-\"}% chef fee</div> <a href={hrefWithParams(\\`?tab=profile&accountId=\\${potDetail?.chef}\\`)} className=\"address\" target=\"_blank\"> <ProfileImg accountId={potDetail?.chef} /> {potDetail?.chef} </a> </div>} </A_223> {addNote ? <TextArea {...{ label: \"Note\", inputRows: 2, inputStyle: { background: \"#FAFAFA\" }, placeholder: \\`Add an optional note for the project (max \\${MAX_NOTE_LENGTH} characters)\\`, value: donationNote, onChange: (donationNote) => updateState({ donationNote }), validate: () => { if (donationNote.length > MAX_NOTE_LENGTH) { updateState({ donationNoteError: \\`Note must be less than \\${MAX_NOTE_LENGTH} characters\\` }); return; } updateState({ donationNoteError: \"\" }); }, error: donationNoteError }} /> : <NoteWrapper onClick={() => updateState({ addNote: true })}> <svg viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0.249054 13.7509H3.06155L11.3566 5.4559L8.54405 2.6434L0.249054 10.9384V13.7509ZM1.74905 11.5609L8.54405 4.7659L9.23405 5.4559L2.43905 12.2509H1.74905V11.5609Z\" fill=\"#7B7B7B\" /> <path d=\"M11.7766 0.468398C11.4841 0.175898 11.0116 0.175898 10.7191 0.468398L9.34655 1.8409L12.1591 4.6534L13.5316 3.2809C13.8241 2.9884 13.8241 2.5159 13.5316 2.2234L11.7766 0.468398Z\" fill=\"#7B7B7B\" /> </svg> <div>Add Note</div> </NoteWrapper>} <A_224> <A_247 className=\"filled\" onClick={handleDonate}> Confirm donation </A_247> </A_224> </A_219>; }; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const _address = (address, max) => { const limit = max || 10; if (address.length > limit) return address.slice(0, limit) + \"...\";else return address;}; const ProfileImage = (profileImgProps) => { const { profile: _profile, accountId: _accountId, style: _style, imageClassName: _imageClassName, image: _image, title: _title, name: _name, className: _className, imageStyle: _imageStyle, imageWrapperStyle: _imageWrapperStyle, thumbnail: _thumbnail, tooltip: _tooltip, fast: _fast } = profileImgProps; const accountId = _accountId ?? context.accountId; const className = _className ?? \"profile-image d-inline-block\"; const style = _style ?? { width: \"3em\", height: \"3em\" }; const imageStyle = _imageStyle ?? { objectFit: \"cover\" }; const imageWrapperStyle = _imageWrapperStyle ?? { width: \"100%\", height: \"100%\" }; const imageClassName = _imageClassName ?? \"rounded-circle w-100 h-100\"; const thumbnail = _thumbnail ?? \"thumbnail\"; const profile = _profile ?? Social.getr(\\`\\${accountId}/profile\\`); const name = _name || \"No-name profile\"; const image = _image || profile.image; const title = _title ?? \\`\\${name} @\\${accountId}\\`; const tooltip = _tooltip && (_tooltip === true ? title : _tooltip); const fast = _fast || !_profile && !!accountId; if (accountId !== state.accountId) { State.update({ fastImageUrl: \\`https://i.near.social/magic/\\${thumbnail || \"large\"}/https://near.social/magic/img/account/\\${accountId}\\`, accountId }); } const fallbackUrl = props.fallbackUrl; const imageProps = { image, alt: title, className: imageClassName, style: imageStyle, thumbnail, fallbackUrl }; const inner = fast ? <div className={className} style={style} key={state.fastImageUrl}> <img className={imageClassName} style={imageStyle} src={state.fastImageUrl} alt={title} onError={() => { if (state.fastImageUrl !== fallbackUrl) { State.update({ fastImageUrl: fallbackUrl }); } }} /> </div> : <div className={className} style={style} key={JSON.stringify(image)}> <Widget loading=\" \" code={props.alem.componentsCode.Image} props={{ ...{ ...imageProps, ...props } }} /> </div>; return props.tooltip ? <OverlayTrigger accountId={accountId}>{inner}</OverlayTrigger> : inner; }; const Form = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem 0;\\`;const A_225 = styled.div\\` display: flex; flex-direction: column; padding: 0 2rem; @media only screen and (max-width: 480px) { padding: 0 1.125rem; }\\`;const A_226 = styled.div\\` font-weight: 500; margin-bottom: 0.5rem; margin-top: 0.5rem;\\`;const CurrentBalance = styled.div\\` display: flex; margin-top: 0.5rem; gap: 0.5rem; flex-wrap: wrap; justify-content: flex-end; .amount-alert { color: #e54141; } .balance { display: flex; gap: 0.5rem; div:last-of-type { color: #7b7b7b; } }\\`;const TotalAmount = styled.div\\` display: flex; gap: 0.5rem; align-items: center; margin-left: auto; .label { color: #7b7b7b; } .amount { font-weight: 600; .usd { color: #7b7b7b; } } @media only screen and (max-width: 480px) { width: 100%; justify-content: space-between; }\\`;const A_227 = styled.div\\` padding: 8px 0; border-top: 1px solid #ebebeb; margin-top: 1.5rem; display: flex; flex-direction: column; height: 238px; overflow-y: scroll; .project { display: flex; align-items: center; gap: 1rem; cursor: pointer; padding: 0.5rem 2rem; transition: 300ms ease-in-out; &:hover, &.selected { background: rgba(235, 235, 235, 0.24); .check { border-color: #dd3345; svg { display: block; } } } } .profile-image { width: 40px; height: 40px; box-shadow: 0px 0px 1px 0px #a6a6a6 inset; border-radius: 50%; } .info { display: flex; flex-direction: column; .name { font-weight: 600; } .address { color: #7b7b7b; transition: all 300ms; &:hover { text-decoration: none; color: #dd3345; } } } .check { margin-left: auto; display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; border: 2px solid #c7c7c7; border-radius: 50%; svg { display: none; width: 12px; } &.selected { border-color: #dd3345; svg { display: block; } } } @media only screen and (max-width: 480px) { .project { padding: 0.5rem 1.125rem; } }\\`;const A_228 = styled.div\\` margin-left: auto; position: relative; display: flex; border-radius: 6px; background: rgb(246, 245, 243); box-shadow: rgb(255, 255, 255) 0px 1px 0px 0px, rgba(41, 41, 41, 0.1) 0px 0px 4px 0px, rgba(41, 41, 41, 0.1) 0px 2px 4px -1px inset, rgba(41, 41, 41, 0.1) 0px 8px 16px -4px inset; input { padding: 10px 16px; padding-right: 46px; text-align: right; width: 120px; background: transparent; border: none; } svg { position: absolute; top: 50%; transform: translateY(-50%); right: 1rem; width: 16px; }\\`;const CustomButton = styled.div\\` display: flex; margin-top: 4rem; margin-bottom: 0.5rem; padding: 0 2rem; gap: 1rem; justify-content: flex-end; @media only screen and (max-width: 480px) { padding: 0px 1.125rem; margin-top: 2rem; }\\`; const FormPot = ({ amount, amountError, DENOMINATION_OPTION, updateState, selectedDenomination, donationType, ftBalance, selectedProjects, selectedRound, projects: _projects, handleAddToCart, potDetail}) => { const donationTypes = [{ label: \"Auto\", info: \"(allocate funds evenly across multiple projects)\", val: \"auto\", disabled: false }, { label: \"Manual\", info: \"(manually specify amount for each project)\", val: \"manual\", disabled: false }]; const isEmpty = (obj) => { return Object.keys(obj).length === 0; }; const projects = _projects ?? []; const projectHegiht = 58; const projectsContaienrHegiht = projects.length > 4 ? 234 : projectHegiht * projects.length; const HandleAmoutChange = (amount) => { amount = amount.replace(/[^0-9.]+/g, \"\"); if (amount === \".\") amount = \"0.\"; updateState({ amount, amountError: \"\" }); if (amount > ftBalance) { updateState({ amountError: \"You don’t have enough balance to complete this transaction.\" }); } else if (parseFloat(amount) < 0.1) { updateState({ amountError: \"Minimum donation is 0.1 NEAR\" }); } }; const handleAddProject = (project) => { const updatedProjects = selectedProjects; if (selectedProjects[project] === \"\") { delete updatedProjects[project]; } else { updatedProjects[project] = \"\"; } updateState({ selectedProjects: updatedProjects }); }; let totalAmountAllocated = 0; Object.values(selectedProjects).forEach((amount) => totalAmountAllocated += parseFloat(amount || 0)); totalAmountAllocated = parseFloat(totalAmountAllocated.toFixed(1)); const handleProjectAmount = (project, amount) => { amount = amount.replace(/[^\\\\d.]/g, \"\"); if (amount === \".\") amount = \"0.\"; const updatedProjects = selectedProjects; updatedProjects[project] = amount; let totalAmount = 0; Object.values(updatedProjects).forEach((amount) => totalAmount += parseFloat(amount)); if (totalAmount > ftBalance && ftBalance !== null) { updateState({ amountError: \"You don’t have enough balance to complete this transaction.\" }); } else if (parseFloat(amount) < 0.1 && parseFloat(amount) !== 0) { updateState({ amountError: \"Minimum donation is 0.1 NEAR\" }); } else { updateState({ amountError: \"\" }); } updateState({ selectedProjects: updatedProjects }); }; const { NADABOT_HUMAN_METHOD, NADABOT_CONTRACT_ID } = constants; const isUserHumanVerified = Near.view(NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, { account_id: context.accountId }); const isDisabled = isEmpty(selectedProjects) || (donationType === \"auto\" ? amountError || parseFloat(amount) === 0 || !amount : totalAmountAllocated > ftBalance || amountError || parseFloat(totalAmountAllocated.toString()) === 0); return <A_242> <A_225> <A_226>How do you want to allocate funds?</A_226> <Checks options={donationTypes} value={donationType} onClick={(val) => { console.log(\"donationType\", val); updateState({ selectedProjects: {}, donationType: val }); }} /> {donationType === \"auto\" && <> <A_226 style={{ marginTop: \"1.5rem\" }}> Amount </A_226> <AmountInput value={amount} donationType={donationType} HandleAmoutChange={HandleAmoutChange} updateState={updateState} denominationOptions={DENOMINATION_OPTION} selectedDenomination={selectedDenomination} /> </>} <A_244> {ftBalance && <div className=\"balance\"> <div> {ftBalance} <span> {selectedDenomination.text} </span> </div> <div>available</div> </div>} {donationType === \"manual\" && <TotalAmount> <div className=\"label\">Total amount allocated</div> <div className=\"amount\"> {totalAmountAllocated} <span>NEAR</span> {A_236 && <span className=\"usd\"> ~\\$ {A_236} </span>} </div> </TotalAmount>} </A_244> {amountError && <Alert error={amountError} />} {isUserHumanVerified !== null && !isUserHumanVerified && <VerifyInfo />} </A_225> <A_227 style={{ height: projectsContaienrHegiht + \"px\" }}> {projects.map(({ project_id }) => { const profile = Social.getr(\\`\\${project_id}/profile\\`); return <div className={\\`project \\${selectedProjects[project_id] === \"\" ? \"selected\" : \"\"}\\`} style={{ cursor: donationType == \"auto\" ? \"pointer\" : \"default\" }} key={project_id} onClick={() => donationType == \"auto\" ? handleAddProject(project_id) : {}}> <ProfileImage profile={profile} style={{}} />{\" \"} <div className=\"info\"> {profile?.name && <div className=\"name\">{_address(profile?.name, 20)}</div>} <a className=\"address\" href={hrefWithParams(\\`?tab=project&projectId=\\${project_id}\\`)} target=\"_blank\"> {_address(project_id, 20)} </a> </div> {donationType === \"manual\" ? <A_228> <input className=\"amount\" type=\"text\" placeholder=\"0.00\" onChange={(e) => handleProjectAmount(project_id, e.target.value)} /> <svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg> </A_228> : <div className=\"check\"> <svg viewBox=\"0 0 12 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_468_92)\"> <path d=\"M1 5.1618L4 8.16197L11.1621 1\" stroke=\"#DD3345\" stroke-width=\"2\" /> </g> <defs> <clipPath id=\"clip0_468_92\"> <rect width=\"12\" height=\"10\" fill=\"white\" /> </clipPath> </defs> </svg> </div>} </div>; })} </A_227> <CustomButton> <A_247 className={\\`filled \\${isDisabled ? \"disabled\" : \"\"}\\`} onClick={() => { if (donationType === \"auto\") updateState({ currentPage: \"confirmPot\" });else { updateState({ currentPage: \"confirmPot\", amount: totalAmountAllocated }); } }}> Proceed to donate </A_247> <A_247 className={\\`outline \\${isDisabled ? \"disabled\" : \"\"}\\`} onClick={() => { const cartItems = Object.entries(selectedProjects).map(([project_id, project_amount]) => ({ id: project_id, amount: project_amount || parseFloat(amount) / Object.keys(selectedProjects).length, token: selectedDenomination, potId: selectedRound, potDetail })); handleAddToCart(cartItems); }}> Add to cart </A_247> </CustomButton> </A_242>;}; const A_229 = styled.div\\` display: flex; flex-direction: column; border-radius: 12px; background: #fff; font-size: 14px; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); overflow: hidden; border-radius: 6px; @media only screen and (max-width: 480px) { top: 0; border-radius: 0; position: fixed; left: 0; width: 100vw; height: 100vh; overflow-y: scroll; display: flex; z-index: 1000; }\\`;const A_230 = styled.div\\` position: relative; display: flex; flex-direction: column; padding: 1.5rem 2rem; gap: 0.5rem; overflow: hidden; background: #f8d3b0; color: white; font-size: 22px; div { color: #3f130b; font-size: 20px; font-weight: 600; } .left-pattern { position: absolute; left: 0; top: 0; width: 30%; transform: translate(-10%, -10%) scaleX(-1); pointer-events: none; } .right-pattern { position: absolute; right: 0; top: 0; width: 30%; transform: translate(10%, -10%); pointer-events: none; } @media only screen and (max-width: 480px) { padding: 1.125rem; }\\`;const A_231 = styled.div\\` display: flex; align-items: center; justify-content: space-between; svg { width: 14px; cursor: pointer; transition: all 300ms ease-in-out; path { fill: #3f130b; } } .close-icon { margin-left: auto; &:hover { rotate: 90deg; } } div { cursor: pointer; display: flex; } .back-arrow:hover svg { transform: translateX(-10px); }\\`; const BannerBg = __props__ => <svg {...__props__} viewBox=\"0 0 145 152\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <rect x=\"157.654\" y=\"-37\" width=\"20\" height=\"161.118\" rx=\"10\" transform=\"rotate(45 157.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"189.654\" y=\"-37\" width=\"20\" height=\"245.972\" rx=\"10\" transform=\"rotate(45 189.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"221.654\" y=\"-37\" width=\"20\" height=\"164.654\" rx=\"10\" transform=\"rotate(45 221.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"125.654\" y=\"-37\" width=\"20\" height=\"177.702\" rx=\"10\" transform=\"rotate(45 125.654 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> <rect x=\"93.6543\" y=\"-37\" width=\"20\" height=\"78.4889\" rx=\"10\" transform=\"rotate(45 93.6543 -37)\" fill=\"white\" fill-opacity=\"0.08\" /> </svg>; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; const VerifyInfo = () => <VerifyInfoWrapper> <div className=\"icon\"> <svg width=\"18\" height=\"16\" viewBox=\"0 0 18 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M0.75 15.125H17.25L9 0.875L0.75 15.125ZM9.75 12.875H8.25V11.375H9.75V12.875ZM9.75 9.875H8.25V6.875H9.75V9.875Z\" fill=\"#ECC113\" /> </svg> </div> <div className=\"text\"> Your contribution won't be matched unless verified as human before the matching round ends. </div> <a href=\"https://app.nada.bot/\" target=\"_blank\"> Verify you’re human </a> </VerifyInfoWrapper>; const AlertBanner = styled.div\\` display: flex; padding: 0.75rem 1rem; color: #ed464f; gap: 1rem; align-items: center; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; margin-top: 1.5rem; div { font-weight: 500; } .icon { width: 22px; }\\`;const NadabotBanner = styled.div\\` display: flex; align-items: center; padding: 0.75rem 1rem; border: 1px solid #f4b37d; border-radius: 6px; background: #fef6ee; flex-wrap: wrap; margin-top: 1.5rem; .label { display: flex; align-items: center; font-weight: 500; gap: 1rem; img { width: 24px; height: 24px; } } .verify { color: #dd3345; font-weight: 500; margin-left: auto; &:hover { text-decoration: none; } } @media only screen and (max-width: 480px) { flex-direction: column; align-items: flex-start; gap: 0px; .labe { align-items: flex-start; } .verify { margin-left: 40px; } }\\`;const VerifyInfoWrapper = styled.div\\` display: flex; align-items: center; gap: 14px; padding: 1rem; border-radius: 6px; border: 1px solid #ecc113; background: #fbf9c6; box-shadow: 0px 2px 1px 1px rgba(255, 255, 255, 0.8) inset, 0px -2px 4px 0px rgba(15, 15, 15, 0.15) inset; font-size: 14px; color: #3f2209; margin-top: 1.5rem; .icon { width: 17px; display: flex; height: fit-content; svg { width: 100%; } } .text { flex: 1; line-height: 150%; } a { font-weight: 500; color: #dd3345; :hover { text-decoration: none; } } @media only screen and (max-width: 480px) { flex-wrap: wrap; a { width: 100%; text-align: center; } }\\`; const Alert = ({ error}) => <AlertBanner> <div className=\"icon\"> <svg viewBox=\"0 0 22 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11 4.49L18.53 17.5H3.47L11 4.49ZM11 0.5L0 19.5H22L11 0.5ZM12 14.5H10V16.5H12V14.5ZM12 8.5H10V12.5H12V8.5Z\" fill=\"#F6767A\" /> </svg> </div> <div>{error}</div> </AlertBanner>; const A_232 = styled.div\\` position: relative; display: flex; border-radius: 6px; border: 1px solid #dbdbdb; border-bottom: 2px solid #dbdbdb; background: #fff; input { padding: 0.75rem 1rem; flex: 1; background: transparent; border: none; border-radius: 0; &:focus { box-shadow: none; } } .usd-amount { padding-right: 12px; color: #7b7b7b; display: flex; align-items: center; border-right: 1px solid #dbdbdb; }\\`;const DropdownWrapper = styled.div\\` display: flex; span { font-weight: 500; }\\`;const PotDenomination = styled.div\\` display: flex; align-items: center; gap: 4px; padding: 0 1rem; .text { font-weight: 500; }\\`; const A_233 = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0px; gap: 0.45em; width: 100%;\\`;const A_234 = styled.label\\` font-weight: 500; font-size: 14px; line-height: 16px; word-wrap: break-word; color: #2e2e2e;\\`;const A_235 = styled.div\\` box-sizing: border-box; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 10px; background: #ffffff; border: 1px solid #d0d5dd; /* box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); */ box-shadow: 0px -2px 0px rgba(93, 93, 93, 0.24) inset; border-radius: 4px; color: #101828; width: 100%;\\`;const Placeholder = styled.span\\` color: #a0a3a8;\\`;const scaleOut = styled.keyframes\\` from { transform: scaleY(0); } to { transform: scaleY(1); }\\`;const SelectContent = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; gap: 0.5em; width: 100%; border: 1px solid #d0d5dd; border-radius: 4px; background: #ffffff; z-index: 3 !important;\\`;const Viewport = styled.div\\` display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; padding: 0; width: 100%;\\`;const Item = styled.button\\` display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em 0.75em; gap: 0.5em; width: 100%; cursor: pointer; background: transparent; border: none; transition: background 0.2s ease-in-out; &:nth-child(n + 1) { border-top: 1px solid #d0d5dd; } &:hover { background: #d0d5dd; boder: none; } &:focus { outline: none; }\\`; const Select = (componentProps) => { const label = componentProps.label ?? \"Label\"; const noLabel = componentProps.noLabel ?? false; const placeholder = componentProps.placeholder ?? \"Select an option\"; const value = componentProps.value ?? \"\"; const options = componentProps.options ?? []; const onChange = componentProps.onChange ?? (() => {}); const validate = componentProps.validate ?? (() => {}); const error = componentProps.error ?? \"\"; return <A_233 style={componentProps.containerStyles || {}}> {noLabel ? <></> : <A_234>{label}</A_234>} <Select.Root value={value?.value} onValueChange={(value) => onChange(options.find((option) => option.value === value))}> <Select.Trigger asChild={true}> <A_235 style={componentProps.inputStyles || {}}> {componentProps.iconLeft && componentProps.iconLeft} <Select.Value aria-label={value.value} placeholder={<Placeholder>{placeholder}</Placeholder>} /> {} <Select.Icon> <svg width=\"12\" height=\"8\" viewBox=\"0 0 12 8\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1 1.5L6 6.5L11 1.5\" stroke=\"currentColor\" stroke-width=\"1.66667\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </svg> </Select.Icon> {} </A_235> </Select.Trigger> <Select.Content asChild={true}> <SelectContent> <Select.Viewport asChild={true}> <Viewport> {options.map(({ text, value }) => <Select.Item value={value} asChild={true}> <Item> <Select.ItemText>{text}</Select.ItemText> <Select.ItemIndicator> <svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" /> </svg> </Select.ItemIndicator> </Item> </Select.Item>)} </Viewport> </Select.Viewport> </SelectContent> </Select.Content> </Select.Root> </A_233>;}; const A_236 = useCache(() => asyncFetch(\"https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd\").then((res) => { if (res.ok) { return res.body.near.usd; }}), \"nearToUsd\"); const A_237 = (__props__) => <svg {...__props__} style={{ width: 16}} viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <g clip-path=\"url(#clip0_454_78)\"> <circle cx=\"8\" cy=\"8\" r=\"7.25\" stroke=\"#292929\" stroke-width=\"1.5\" /> <path d=\"M11.1477 4C10.851 4 10.5763 4.15333 10.421 4.406L8.74866 6.88867C8.72453 6.92441 8.71422 6.96772 8.71967 7.01051C8.72511 7.05329 8.74594 7.09264 8.77826 7.1212C8.81057 7.14976 8.85218 7.1656 8.89531 7.16574C8.93844 7.16589 8.98015 7.15034 9.01266 7.122L10.6587 5.69467C10.6683 5.68598 10.6802 5.68028 10.6931 5.67828C10.7059 5.67628 10.719 5.67806 10.7308 5.6834C10.7426 5.68875 10.7526 5.69742 10.7596 5.70836C10.7665 5.7193 10.7702 5.73203 10.77 5.745V10.215C10.77 10.2287 10.7658 10.2421 10.7579 10.2534C10.7501 10.2646 10.7389 10.2732 10.726 10.2778C10.7131 10.2825 10.6991 10.2831 10.6858 10.2795C10.6726 10.2758 10.6608 10.2682 10.652 10.2577L5.67667 4.30167C5.59667 4.20709 5.49701 4.1311 5.38463 4.079C5.27226 4.0269 5.14987 3.99994 5.026 4H4.85233C4.62628 4 4.40949 4.0898 4.24964 4.24964C4.0898 4.40949 4 4.62628 4 4.85233V11.1477C4 11.3333 4.06061 11.5139 4.17263 11.6619C4.28465 11.81 4.44194 11.9174 4.6206 11.9679C4.79926 12.0184 4.98952 12.0091 5.16245 11.9416C5.33538 11.874 5.48152 11.7519 5.57867 11.5937L7.251 9.111C7.27513 9.07525 7.28544 9.03194 7.27999 8.98916C7.27455 8.94637 7.25372 8.90703 7.22141 8.87846C7.18909 8.8499 7.14748 8.83407 7.10435 8.83392C7.06122 8.83377 7.01951 8.84932 6.987 8.87766L5.341 10.3053C5.33134 10.3139 5.31939 10.3195 5.3066 10.3215C5.29381 10.3234 5.28074 10.3216 5.26898 10.3162C5.25721 10.3108 5.24726 10.3021 5.24034 10.2912C5.23342 10.2803 5.22983 10.2676 5.23 10.2547V5.784C5.22997 5.77027 5.23418 5.75687 5.24206 5.74563C5.24993 5.73438 5.26109 5.72584 5.274 5.72117C5.28691 5.71651 5.30094 5.71594 5.31419 5.71955C5.32743 5.72315 5.33924 5.73076 5.348 5.74133L10.3227 11.698C10.4847 11.8893 10.7227 11.9997 10.9733 12H11.147C11.373 12.0001 11.5898 11.9104 11.7498 11.7507C11.9097 11.591 11.9997 11.3744 12 11.1483V4.85233C11.9999 4.62631 11.9101 4.40956 11.7503 4.24974C11.5904 4.08992 11.3737 4.00009 11.1477 4Z\" fill=\"#292929\" /> </g> <defs> <clipPath id=\"clip0_454_78\"> <rect width=\"16\" height=\"16\" fill=\"white\" /> </clipPath> </defs> </svg>; const AmountInput = (__props__) => { const Dropdown = ({ selectedDenomination, denominationOptions, updateState }) => <DropdownWrapper> <Select {...{ noLabel: true, placeholder: \"\", options: denominationOptions, value: { text: selectedDenomination.text, value: selectedDenomination.value }, onChange: ({ value }) => { updateState({ selectedDenomination: denominationOptions.find((option) => option.value === value) }); }, containerStyles: { width: \"auto\" }, inputStyles: { border: \"none\", boxShadow: \"none\", width: \"auto\", padding: \"12px 16px\", height: \"100%\", color: \"#292929\" }, iconLeft: selectedDenomination.icon ? <img src={selectedDenomination.icon} style={{ height: \"16px\", width: \"16px\" }} /> : <A_237 /> }} /> </DropdownWrapper>; const { value, HandleAmoutChange, donationType, denominationOptions, selectedDenomination } = __props__; return <A_232> <input type=\"text\" value={value} placeholder=\"0\" onChange={(e) => HandleAmoutChange(e.target.value)} name=\"amount\" /> <div className=\"usd-amount\"> {\" \"} {A_236 && selectedDenomination.value === \"NEAR\" ? \\`~\\$ \\${(A_236 * value).toFixed(2)}\\` : \"\"} </div> {donationType === \"pot\" || denominationOptions.length === 1 ? <PotDenomination> <A_237 /> <div className=\"text\">{denominationOptions[0].text}</div> </PotDenomination> : <Dropdown {...__props__} />} </A_232>;}; const SelectPot = ({ selectedRound, activeRoundsOptions, updateState }) => { const PotSelector = styled.div\\` display: flex; > div:last-of-type { width: 100%; } .custom-menu-style { left: 0; right: auto; } \\`; return <PotSelector> <Widget loading=\" \" code={props.alem.componentsCode.A_238} props={{ ...{ ...{ sortVal: activeRoundsOptions ? activeRoundsOptions[selectedRound].label : \"\", showCount: false, sortList: Object.values(activeRoundsOptions), buttonStyle: { border: \"1px solid #dbdbdb\", padding: \"0.75rem 1rem\", borderBottomWidth: \"2px\", borderRadius: \"6px\", justifyContent: \"space-between\" }, menuStyle: { top: \"120%\" }, FilterMenuCustomClass: \"custom-menu-style\", handleSortChange: ({ val }) => {updateState({ selectedRound: val });} }, ...props } }} /> </PotSelector>; }; const A_239 = styled.div\\` display: flex; flex-direction: column; gap: 0.75rem; > div { display: flex; border-radius: 8px; align-items: center; gap: 0.5rem; padding: 1rem; cursor: pointer; border: 1px solid #dbdbdb; color: #7b7b7b; background: white; transition: all 300ms; .text { flex: 1; font-weight: 500; } &.active { box-shadow: 0px 0px 1.4px 2px #fee6e5; color: #dd3345; border-color: #dd3345; span { color: #7b7b7b; } } &.disabled { pointer-events: none; color: #a6a6a6; background: #f6f5f3; span { color: #a6a6a6; } } }\\`;const A_240 = styled.div\\` width: 20px; height: 20px; border: 2px solid #d9d9d9; display: flex; border-radius: 50%; div { width: 10px; height: 10px; background: transparent; border-radius: 50%; margin: auto; } &.active { border-color: #dd3345; div { background: #dd3345; } }\\`; const Checks = ({ options, value, onClick}) => { return <A_239> {options.map((option) => <div key={option.val} onClick={() => onClick(option.val)} className={\\`\\${value === option.val ? \"active\" : \"\"} \\${option.disabled ? \"disabled\" : \"\"}\\`}> <A_240 className={\\`\\${value === option.val ? \"active\" : \"\"}\\`}> <div></div> </A_240> <div className=\"text\"> {option.label} {option.info && <span> {option.info} </span>} {option.disabled && <span> {option.disabledText} </span>} </div> </div>)} </A_239>;}; const A_241 = () => <div className=\"spinner-border text-secondary\" role=\"status\" />; const constants = { NADABOT_CONTRACT_ID: props.alem.getAlemEnvironment() === \"staging\" ? \"v1.staging.nadabot.near\" : \"v1.nadabot.near\", ownerId: \"potlock.near\", PROJECT_STATUSES: [\"Pending\", \"Approved\", \"Rejected\", \"Graylisted\", \"Blacklisted\"], DONATION_CONTRACT_ID: \"donate.potlock.near\", NADABOT_HUMAN_METHOD: \"is_human\", IPFS_BASE_URL: \"https://ipfs.near.social/ipfs/\", ONE_TGAS: Big(1_000_000_000_000), MAX_DONATION_MESSAGE_LENGTH: 100, NADA_BOT_URL: \"https://app.nada.bot\", SUPPORTED_FTS: { NEAR: { iconUrl: \"https://nftstorage.link/ipfs/bafkreidnqlap4cp5o334lzbhgbabwr6yzkj6albia62l6ipjsasokjm6mi\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) }, USD: { iconUrl: \"\\$\", toIndivisible: amount => new Big(amount).mul(new Big(10).pow(24)), fromIndivisible: (amount, decimals) => Big(amount).div(Big(10).pow(24)).toFixed(decimals || 2) } }, ToDo: styled.div\\` position: relative; &::before { content: \"TODO: \"; position: absolute; left: 0; top: 0; transform: translate(-110%, 0); background-color: yellow; } \\`}; const A_242 = styled.div\\` display: flex; flex-direction: column; padding: 1.5rem 2rem; @media only screen and (max-width: 480px) { padding: 1.5rem 1.125rem; }\\`;const A_243 = styled.div\\` font-weight: 500; margin-bottom: 0.5rem; margin-top: 0.5rem;\\`;const A_244 = styled.div\\` display: flex; margin-top: 0.5rem; gap: 0.5rem; justify-content: flex-end; .amount-alert { color: #e54141; } .balance { display: flex; gap: 0.5rem; div:last-of-type { color: #7b7b7b; } }\\`;const PotWrapper = styled.div\\` display: flex; flex-direction: column; margin-top: 1.5rem;\\`;const A_245 = styled.div\\` display: flex; > div:last-of-type { width: 100%; }\\`;const A_246 = styled.div\\` border-radius: 6px; border: 1px solid #dbdbdb; border-bottom-width: 2px; background: #fff; padding: 0.75rem 1rem;\\`;const DirectButton = styled.div\\` display: flex; justify-content: flex-end; margin-top: 4rem; margin-bottom: 0.5rem; gap: 1rem; @media only screen and (max-width: 480px) { margin-top: 2rem; }\\`;const A_247 = styled.button\\` padding: 9px 12px; border-radius: 6px; font-weight: 500; font-size: 14px; transition: background 200ms ease; outline: none; border: none; width: fit-content; &.filled { background: #464646; color: white; box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.84) inset, 0px 1px 1px 1px rgba(166, 166, 166, 0.4) inset, 0px 0px 0px 2px rgba(166, 166, 166, 0.4) inset, 0px 1px 2px 0px rgba(15, 15, 15, 0.15), 0px 1px 3px -1px rgba(5, 5, 5, 0.08); &:hover { background: #525252; } &.disabled { color: #a6a6a6; background: var(--Neutral-100, #ebebeb); box-shadow: 0px 0px 0px 1px rgba(15, 15, 15, 0.15) inset; } } &.outline { background: #fff; color: #292929; box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.22) inset, 0px -1px 0px 0px rgba(15, 15, 15, 0.15) inset, 0px 1px 2px -0.5px rgba(5, 5, 5, 0.08); &:hover { background: #f7f7f7; } &.disabled { color: #c7c7c7; background: var(--Neutral-White, #fff); box-shadow: 0px 0px 0px 1px rgba(15, 15, 15, 0.15) inset; } } &:focus { box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.84) inset, 0px 1px 1px 1px rgba(166, 166, 166, 0.3) inset, 0px 0px 0px 2px rgba(166, 166, 166, 0.3) inset, 0px 0px 0px 2px #fff, 0px 0px 0px 4px rgba(0, 0, 0, 0.84); } &.disabled { pointer-events: none; }\\`; const FormDirect = (__props__) => { const { projectId, profile, amount, amountError, denominationOptions, updateState, selectedDenomination, donationType, ftBalance, activeRounds, accountId, selectedRound, handleAddToCart } = __props__; const { NADABOT_HUMAN_METHOD, NADABOT_CONTRACT_ID } = constants; const isUserHumanVerified = Near.view(NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, { account_id: accountId }); const needsToVerify = isUserHumanVerified === false && donationType === \"pot\"; const donationTypes = [{ label: \"Direct donation\", val: \"direct\", disabled: false }, { label: \"Quadratically matched donation\", val: \"pot\", disabled: !activeRounds || activeRounds.length === 0, disabledText: \"(no pots available)\" }]; const activeRoundsOptions = {}; (activeRounds || []).forEach((round) => { activeRoundsOptions[round] = { label: PotSDK.getConfig(round)?.pot_name || round, val: round }; }); const isFtDonation = selectedDenomination.text !== \"NEAR\"; const HandleAmoutChange = (amount) => { amount = amount.replace(/[^0-9.]+/g, \"\"); if (amount === \".\") amount = \"0.\"; updateState({ amount, amountError: \"\" }); if (amount > ftBalance && ftBalance !== null) { updateState({ amountError: \"You don’t have enough balance to complete this transaction.\" }); } else if (!isFtDonation && parseFloat(amount) < 0.1) { updateState({ amountError: \"Minimum donation is 0.1 NEAR\" }); } }; const isLoading = donationType === \"pot\" ? isUserHumanVerified === null || activeRounds === null : false; const isDisabled = amountError || !amount || !accountId; return projectId ? profile === null ? <A_241 /> : <A_242> <A_243>How do you want to donate?</A_243> <Checks options={donationTypes} value={donationType} onClick={(val) => updateState({ donationType: val })} /> {donationType === \"pot\" && <PotWrapper> <A_243>Select Pot</A_243> <SelectPot {...__props__} activeRoundsOptions={activeRoundsOptions} /> </PotWrapper>} <A_243 style={{ marginTop: \"1.5rem\" }}> Amount </A_243> <AmountInput value={amount} donationType={donationType} HandleAmoutChange={HandleAmoutChange} updateState={updateState} denominationOptions={denominationOptions} selectedDenomination={selectedDenomination} /> {ftBalance && <A_244> <div className=\"balance\"> <div> {ftBalance} <span> {selectedDenomination.text} </span> </div> <div>available</div> </div> </A_244>} {amountError && <Alert error={amountError} />} {needsToVerify && !isLoading && <VerifyInfo />} <DirectButton> <A_247 {...{ className: \\`filled \\${isDisabled ? \"disabled\" : \"\"}\\`, onClick: () => updateState({ currentPage: \"confirm\" }) }}> {\" \"} {!accountId ? \"Sign In to Proceed\" : isLoading ? \"Loading...\" : \"Proceed to donate\"}{\" \"} </A_247> <A_247 {...{ className: \\`outline \\${isDisabled ? \"disabled\" : \"\"}\\`, onClick: () => handleAddToCart([{ id: projectId, amount, token: selectedDenomination, potId: donationType === \"pot\" ? selectedRound : null }]) }}> Add to cart </A_247> </DirectButton> </A_242> : \"\";}; const ModalOverlay = ({ children, onOverlayClick, contentStyle}) => { const ModalOverlay = styled.div\\` position: fixed; padding: 0 10px; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 1000; \\`; const ModalContent = styled.div\\` width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; position: relative; z-index: 1; box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); \\`; const Screen = styled.div\\` position: absolute; height: 100%; width: 100%; top: 0; left: 0; \\`; return <ModalOverlay style={contentStyle}> <Screen onClick={onOverlayClick} /> <ModalContent style={contentStyle}>{children}</ModalContent> </ModalOverlay>;}; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const DENOMINATION_OPTIONS = [{ text: \"NEAR\", value: \"NEAR\", decimals: 24 }]; const { donationModalProps, setSuccessfulDonation, setDonationModalProps } = useDonationModal(); const { addItemstoCart } = useCart(); const onClose = () => { setDonationModalProps(null); }; const { potId, referrerId } = props.alem.useParams(); const { projectId, multiple } = donationModalProps || {}; const potDetail = donationModalProps?.potDetail ?? PotSDK.getConfig(potId); const accountId = context.accountId; State.init({ amount: \"\", donationType: multiple ? \"auto\" : \"direct\", showBreakdown: false, bypassProtocolFee: false, bypassChefFee: false, addNote: false, donationNote: \"\", donationNoteError: \"\", allPots: null, intervalId: null, ftBalances: null, selectedDenomination: DENOMINATION_OPTIONS[0], denominationOptions: DENOMINATION_OPTIONS, selectedRound: \"\", currentPage: multiple ? \"formPot\" : \"form\", selectedProjects: {}, toggleAmount: true }); const { donationType, ftBalances, denominationOptions, selectedDenomination, selectedRound, currentPage } = state; const [activeRounds, setActiveRounds] = useState(null); const profile = Social.getr(\\`\\${projectId}/profile\\`); const profileName = profile?.name || projectId; const pages = { form: FormDirect, formPot: FormPot, confirm: ConfirmDirect, confirmPot: ConfirmPot }; const ActivePageComponent = pages[currentPage]; const pots = useCache(() => PotFactorySDK.asyncGetPots().then(pots => { const activePots = pots.map(pot => PotSDK.isRoundActive(pot.id).then(isActive => isActive && pot.id).catch(e => { console.error(\"error checking active round for pot: \" + pot.id, e); })); return Promise.all(activePots); }).catch(e => { console.error(\"error getting pots: \", e); }), \"active-pots\"); useEffect(() => { if (potId && !activeRounds) { setActiveRounds([potId]); State.update({ selectedRound: potId, donationType: multiple ? \"auto\" : \"pot\" }); } else if (!activeRounds?.length && projectId) { if (!pots) setActiveRounds([]); (pots ?? []).forEach((pot, idx) => { if (pot) { PotSDK.asyncGetApplicationByProjectId(pot, projectId).then(application => { if (application.status === \"Approved\") { setActiveRounds(prev => { const prevRounds = prev || []; if (!prevRounds.includes(pot)) { return [...prevRounds, pot]; } }); if (!selectedRound) State.update({ selectedRound: pot }); } else if (pots.length - 1 === idx && !activeRounds) { setActiveRounds(prev => [...(prev || [])]); } }).catch(err => { console.log(err); setActiveRounds(prev => [...(prev || [])]); }); } }); } }, [pots]); useEffect(() => { if (donationType === \"direct\") { asyncFetch(\\`https://near-mainnet.api.pagoda.co/eapi/v1/accounts/\\${accountId}/balances/FT\\`, { headers: { \"Content-Type\": \"application/json\", \"x-api-key\": \"dce81322-81b0-491d-8880-9cfef4c2b3c2\" } }).then(ftBalancesRes => { if (ftBalancesRes) { const ftBalances = ftBalancesRes.body.balances; State.update({ ftBalances: ftBalances, denominationOptions: DENOMINATION_OPTIONS.concat(ftBalances.map(({ amount, contract_account_id, metadata }) => ({ amount, id: contract_account_id, text: metadata.symbol, value: metadata.symbol, icon: metadata.icon, decimals: metadata.decimals })).filter(option => option.text.length < 10)) }); } }).catch(err => console.log(\"fetching Ft balances faild\")); } }, [ftBalances, donationType]); const nearBalanceRes = fetch(\\`https://near-mainnet.api.pagoda.co/eapi/v1/accounts/\\${accountId}/balances/NEAR\\`, { headers: { \"Content-Type\": \"application/json\", \"x-api-key\": \"dce81322-81b0-491d-8880-9cfef4c2b3c2\" } }); const ftBalance = useMemo(() => { if (selectedDenomination.text === \"NEAR\") { const nearBalance = nearBalanceRes?.body?.balance; return nearBalance ? parseFloat(Big(nearBalance.amount).div(Big(10).pow(24)).toFixed(2)) : null; } const balance = denominationOptions.find(option => option.text === selectedDenomination.text); return balance ? parseFloat(Big(balance.amount).div(Big(10).pow(balance.decimals)).toFixed(2)) : null; }, [selectedDenomination, ftBalances, nearBalanceRes]); return <ModalOverlay onOverlayClick={e => { e.stopPropagation(); onClose(); }} contentStyle={{ padding: \"0px\" }}> <A_229> <div> <A_230> <BannerBg className=\"left-pattern\" /> <BannerBg className=\"right-pattern\" /> <A_231> {![\"form\", \"formPot\"].includes(currentPage) && <div className=\"back-arrow\" onClick={() => State.update({ currentPage: multiple ? \"formPot\" : \"form\" })}> <svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M16 7H3.83L9.42 1.41L8 0L0 8L8 16L9.41 14.59L3.83 9H16V7Z\" fill=\"#FCCFCF\" /> </svg> </div>} <svg onClick={() => onClose()} className=\"close-icon\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z\" fill=\"#FCCFCF\" /> </svg> </A_231> {[\"confirmPot\", \"confirm\"].includes(currentPage) ? <div> Confirm donation</div> : currentPage === \"formPot\" ? <div>Donate to Projects in {potDetail?.pot_name}</div> : <div> Donate to {profileName}</div>} </A_230> </div> <ActivePageComponent {...donationModalProps} {...state} accountId={accountId} potId={potId} referrerId={referrerId} updateState={State.update} ftBalance={ftBalance} activeRounds={activeRounds} DENOMINATION_OPTION={DENOMINATION_OPTIONS} onClose={onClose} potDetail={potDetail} handleAddToCart={items => { addItemstoCart(items); onClose(); }} openDonationSuccessModal={successfulDonation => { setSuccessfulDonation(successfulDonation); }} /> </A_229> </ModalOverlay>; `, A_250: ` const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const routesPath = { CREATE_PROJECT_TAB: \"createproject\", EDIT_PROJECT_TAB: \"editproject\", PROJECTS_LIST_TAB: \"projects\", PROJECT_DETAIL_TAB: \"project\", CART_TAB: \"cart\", FEED_TAB: \"feed\", POTS_TAB: \"pots\", DEPLOY_POT_TAB: \"deploypot\", POT_DETAIL_TAB: \"pot\", DONORS_TAB: \"donors\", PROFILE_TAB: \"profile\", EDIT_PROFILE_TAB: \"editprofile\"}; const CartButton = styled.div\\` padding: \\${__props__ => __props__.numCartItems > 0 ? \"8px 8px 8px 16px\" : \"8px 16px\"}; background: #2e2e2e; border-radius: 6px; cursor: pointer; display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const CartText = styled.div\\` color: white; font-size: 14px; font-weight: 600; line-height: 20px; word-wrap: break-word; text-align: center;\\`;const CartCountContainer = styled.div\\` display: flex; flex-direction: row; justify-content: center; align-items: center; background: #f86b3f; border-radius: 50%; width: 18px; height: 18px; margin-left: 8px;\\`; const useCart = () => useContext(\"cart-context\"); const NavItem = () => { const { cart } = useCart(); const numCartItems = cart ? Object.keys(cart).length : 0; return <RouteLink to={routesPath.CART_TAB}> <CartButton numCartItems={numCartItems} onClick={() => { numCartItems > 0 ? navigate.to(\"cart\") : {}; }}> <CartText>Cart</CartText> {numCartItems > 0 && <CartCountContainer> <CartText style={{ fontSize: \"12px\" }}>{numCartItems}</CartText> </CartCountContainer>} </CartButton> </RouteLink>;}; const navHeightPx = 110;const navHeightPxMobile = 96;const NavContainer = styled.div\\` width: 100%; display: flex; padding: 0 40px; justify-content: start; align-items: center; align-self: stretch; height: \\${navHeightPx}px; background: #ffffff; z-index: 1000; @media screen and (max-width: 768px) { padding: 24px 8px 24px 16px; height: \\${navHeightPxMobile}px; } @media screen and (max-width: 480px) { padding: 24px 8px 24px 0px; } & > a { width: 10rem; }\\`;const NavLeft = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const NavRight = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center;\\`;const NavRightMobile = styled.div\\` display: none; @media screen and (max-width: 768px) { display: flex; flex-direction: row; align-items: center; justify-content: flex-end; gap: 16px; padding-right: 16px; }\\`;const NavLogo = styled.div\\` a { display: flex; gap: 7px; align-items: baseline; text-align: center; color: #2e2e2e; font-size: 23.95px; font-weight: 700; line-height: 23.95px; word-wrap: break-word; margin-right: 48px; text-decoration: none; @media screen and (max-width: 480px) { font-size: 20px; margin-right: 1rem; } :hover { text-decoration: none; } img { height: 1em; } }\\`;const NavTabs = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center; @media screen and (max-width: 768px) { display: none; }\\`;const NavTab = styled.div\\` a { margin-right: 32px; cursor: \\${(__props__) => __props__.disabled ? \"not-allowed\" : \"pointer\"}; color: \\${(__props__) => __props__.selected ? \"#2E2E2E\" : \"#7B7B7B\"}; font-size: 14px; font-weight: \\${(__props__) => __props__.selected ? 500 : 400}; line-height: 16px; word-wrap: break-word; text-decoration: none; position: relative; :not(:last-child) { margin-right: 32px; } :hover { text-decoration: none; } }\\`;const A_248 = styled.div\\` width: 100%; background: #dd3345; display: flex; flex-direction: row; justify-content: center; align-items: center; padding: 8px 0;\\`;const A_249 = styled.div\\` text-align: center; color: white; font-size: 16px; font-weight: 600; margin-left: 8px; @media screen and (max-width: 768px) { font-size: 12px; margin-left: 4px; }\\`;const BannerLinkContainer = styled.a\\` display: flex; cursor: pointer; text-align: center; font-weight: bold; color: white; font-size: 14px; line-height: 21px; margin-left: 16px; gap: 8px; &:hover { text-decoration: none; } @media screen and (max-width: 768px) { font-size: 12px; margin-left: 8px; gap: 4px; }\\`;const BannerLinkSvg = styled.svg\\` width: 20px; height: 20px; fill: none; transition: transform 0.2s ease; &:hover { transform: rotate(45deg); } @media screen and (max-width: 768px) { width: 16px; height: 16px; }\\`;const BannerAlertSvg = styled.svg\\` width: 18px; @media screen and (max-width: 768px) { width: 14px; }\\`;const NavMenu = styled.div\\` display: none; background: white; padding: 24px; width: 100%; gap: 16px; @media screen and (max-width: 768px) { display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; }\\`;const NavMenuItem = styled.a\\` color: \\${(__props__) => __props__.selected ? \"#2E2E2E\" : \"#7B7B7B\"}; font-size: 14px; font-weight: \\${(__props__) => __props__.selected ? 500 : 400}; line-height: 20px; word-wrap: break-word; cursor: pointer;\\`; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const navigate = { to: (route, params) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } if (routeContext.routes.includes(route)) { routeContext.updateRouteParameters({ ...routeContext, activeRoute: route, routeParams: params || {} }); } }, back: () => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"navigate is being used without Router on top of it.\"); } if (props.alem.isDevelopment && routeContext.routeType === \"URLBased\") { console.warn('The route type is \"URLBased\", \"navigate\" should only be used with the \"ContentBased\" type.'); } const updatedHistory = routeContext.history; if (updatedHistory) { updatedHistory.pop(); const routeProps = updatedHistory.at(-1); if (routeProps.route) { routeContext.updateRouteParameters({ ...routeContext, history: updatedHistory, activeRoute: routeProps.route, routeParams: routeProps.routeParams }); } } }}; const RouteLink = ({ to, href, target, params, label, className, style, onClick, children}) => { const routeContext = useContext(\"alemRoutes\"); if (!routeContext) { console.error(\"RouteLink component is being used without Router on top of it.\"); } const onClickHandler = () => { if (onClick) { onClick(); } if (routeContext.routeType === \"ContentBased\") { navigate.to(to, params); } }; if (routeContext.routeType === \"URLBased\") { let strParams = \"\"; if (params) { Object.keys(params).forEach(paramKey => { strParams += \\`&\\${paramKey}=\\${params[paramKey]}\\`; }); } const Link = styled(\"Link\")\\`\\`; return <Link onClick={onClickHandler} className={className} style={{ cursor: \"pointer\", textDecoration: \"none\", ...style }} target={target} href={href ? href : \\`?\\${routeContext.routeParameterName || \"path\"}=\\${to}\\${strParams}\\`}> {label || children} </Link>; } return <a style={{ cursor: \"pointer\", textDecoration: \"none\", ...style }} className={className} onClick={onClickHandler}> {label || children} </a>;}; const [isNavMenuOpen, setIsNavMenuOpen] = useState(false); const params = props.alem.useParams(); const tabOptions = [{ href: null, newTab: null, text: \"Projects\", link: \"projects\", disabled: false }, { href: null, newTab: null, text: \"Feed\", link: \"feed\", disabled: false }, { href: null, newTab: null, text: \"Pots\", link: \"pots\", disabled: false }, { href: null, newTab: null, text: \"Donors\", link: \"donors\", disabled: false }]; return <> {false && <A_248> <BannerAlertSvg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"white\" aria-hidden=\"true\"> <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z\"></path> </BannerAlertSvg> <A_249>This app is in beta. It has not been audited.</A_249> <BannerLinkContainer href=\"https://docs.potlock.io/general-information/beta-phase\" target=\"_blank\"> <span>Learn more</span> <BannerLinkSvg viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" className=\"w-[18px] group-hover:rotate-[45deg] transition-all\"> <path d=\"M11.6652 6.77894C11.0834 6.78279 10.5015 6.78574 9.91929 6.78777C9.06125 6.78766 8.20376 6.79135 7.34566 6.78145C6.762 6.77478 6.29535 6.33298 6.30266 5.81732C6.31009 5.32123 6.77706 4.88706 7.32973 4.89083C9.53277 4.89897 11.7351 4.91291 13.9368 4.93265C14.6025 4.93925 14.9748 5.32235 14.9826 6.0022C15.0022 8.19227 15.0157 10.3823 15.0231 12.5723C15.0251 13.2043 14.6477 13.6102 14.0912 13.6135C13.5527 13.6152 13.1403 13.1552 13.1372 12.5298C13.1307 11.2364 13.133 9.9431 13.1287 8.64975C13.1284 8.51553 13.113 8.38013 13.0963 8.12137L12.7089 8.50873C10.6829 10.5347 8.64711 12.5508 6.63972 14.5954C6.22161 15.0212 5.62148 14.9861 5.28149 14.6461C4.88466 14.2493 4.90002 13.7158 5.32463 13.2846C7.35705 11.2478 9.39203 9.21284 11.4295 7.17969L11.7105 6.89876L11.6652 6.77894Z\" fill=\"currentColor\"></path> </BannerLinkSvg> </BannerLinkContainer> </A_248>} <NavContainer> <NavLeft> <NavLogo> <RouteLink to={routesPath.PROJECTS_LIST_TAB}> <> <img src=\"https://ipfs.near.social/ipfs/bafkreiafms2jag3gjbypfceafz2uvs66o25qc7m6u6hkxfyrzfoeyvj7ru\" alt=\"logo\" /> POTLOCK </> </RouteLink> </NavLogo> </NavLeft> <NavRight> <NavTabs> {(tabOptions ?? []).map(tab => { return <NavTab disabled={tab.disabled} selected={tab.link === params.tab}> <RouteLink href={tab.href} to={tab.link} target={tab.newTab ? \"_blank\" : \"\"} onClick={e => { if (tab.disabled) e.preventDefault(); }}> {tab.text} </RouteLink> </NavTab>; })} <NavItem /> </NavTabs> </NavRight> <NavRightMobile> <NavItem /> <NavTab onClick={() => setIsNavMenuOpen(!isNavMenuOpen)}> <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\"> <path d=\"M3 18H21V16H3V18ZM3 13H21V11H3V13ZM3 6V8H21V6H3Z\" fill=\"#7B7B7B\" /> </svg> </NavTab> </NavRightMobile> </NavContainer> {isNavMenuOpen && <NavMenu> {tabOptions.map(tab => { return <NavMenuItem href={hrefWithParams(\\`?tab=\\${tab.link}\\`)} onClick={e => { if (tab.disabled) e.preventDefault(); }} selected={props.tab === tab.link}> {tab.text} {tab.disabled && \" (Coming Soon)\"} </NavMenuItem>; })} </NavMenu>} </>; `, A_253: ` const ReferrerIcon = __props__ => <svg {...__props__} viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M12.375 9.5625C14.6925 7.455 16.875 5.4825 16.875 3.7875C16.875 2.4 15.7875 1.3125 14.4 1.3125C13.62 1.3125 12.8625 1.68 12.375 2.25C11.88 1.68 11.13 1.3125 10.35 1.3125C8.9625 1.3125 7.875 2.4 7.875 3.7875C7.875 5.4825 10.0575 7.455 12.375 9.5625ZM10.35 2.8125C10.68 2.8125 11.0175 2.97 11.235 3.225L12.375 4.5675L13.515 3.225C13.7325 2.97 14.07 2.8125 14.4 2.8125C14.955 2.8125 15.375 3.2325 15.375 3.7875C15.375 4.6275 13.845 6.165 12.375 7.53C10.905 6.165 9.375 4.62 9.375 3.7875C9.375 3.2325 9.795 2.8125 10.35 2.8125Z\" fill=\"#7B7B7B\" /> <path d=\"M14.625 11.8125H13.125C13.125 10.9125 12.5625 10.1025 11.7225 9.7875L7.1025 8.0625H1.125V16.3125H5.625V15.2325L10.875 16.6875L16.875 14.8125V14.0625C16.875 12.8175 15.87 11.8125 14.625 11.8125ZM2.625 14.8125V9.5625H4.125V14.8125H2.625ZM10.8525 15.12L5.625 13.6725V9.5625H6.8325L11.1975 11.19C11.4525 11.2875 11.625 11.535 11.625 11.8125C11.625 11.8125 10.1325 11.775 9.9 11.7L8.115 11.1075L7.6425 12.5325L9.4275 13.125C9.81 13.2525 10.2075 13.32 10.6125 13.32H14.625C14.9175 13.32 15.18 13.4925 15.3 13.74L10.8525 15.12Z\" fill=\"#7B7B7B\" /> </svg>; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const A_251 = styled.div\\` height: 46px; width: 100%; margin-top: 1rem; @media screen and (max-width: 480px) { height: 72px; }\\`;const A_252 = styled.div\\` position: fixed; z-index: 999; bottom: 0; left: 0; background: #faa7a8; width: 100%; height: 46px; color: white; overflow: hidden; font-size: 14px; > div { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; background-blend-mode: overlay, normal; background: radial-gradient(circle, #fef3f2 0%, #feefe0 55.1%, #f8d3b0 100%); } .text { font-size: 14px; font-weight: 500; color: #192c07; line-height: 150%; display: flex; gap: 1rem; .link { display: flex; align-items: center; font-weight: 500; gap: 8px; color: #dd3345; text-decoration: none; svg { height: 15px; path { fill: #f6767a; transition: fill 300ms ease-in-out; } } :hover svg path { fill: #dd3345; } } } @media screen and (max-width: 480px) { height: 72px; .text { flex-direction: column; align-items: center; gap: 4px; } }\\`; const potfactoryContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"potfactory.staging.potlock.near\" : \"v1.potfactory.potlock.near\";const PotFactorySDK = { getContractId: () => potfactoryContractId, getConfig: () => { return Near.view(potfactoryContractId, \"get_config\", {}); }, getPots: () => { return Near.view(potfactoryContractId, \"get_pots\", {}); }, asyncGetPots: () => { return Near.asyncView(potfactoryContractId, \"get_pots\", {}); }, getProtocolConfig: () => { return Near.view(potfactoryContractId, \"get_protocol_config\", {}); }, canUserDeployPot: accountId => { const config = PotFactorySDK.getConfig(); if (config) { return !config.require_whitelist || config.whitelisted_deployers.includes(accountId); } }};PotFactorySDK; function isEmpty(obj) { return Object.keys(obj).length === 0;}function getSocialProfile(keys) { return Near.asyncView(\"social.near\", \"get\", { keys });}const PotSDK = { getConfig: potId => { return Near.view(potId, \"get_config\", {}); }, asyncGetConfig: potId => { return Near.asyncView(potId, \"get_config\", {}); }, isUserPotAdminOrGreater: (potId, accountId) => { const config = Near.view(potId, \"get_config\", {}); if (config) { return config.owner === accountId || config.admins.includes(accountId); } }, isRoundActive: potId => { return Near.asyncView(potId, \"is_round_active\", {}); }, getMatchingPoolDonations: potId => { return Near.view(potId, \"get_matching_pool_donations\", {}); }, asyncGetMatchingPoolDonations: potId => { return Near.asyncView(potId, \"get_matching_pool_donations\", {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, \"get_public_round_donations\", { ...(args || {}) }); }, asyncGetPublicRoundDonations: (potId, args) => { return Near.asyncView(potId, \"get_public_round_donations\", { ...(args || {}) }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, asyncGetDonationsForDonor: (potId, accountId) => { return Near.asyncView(potId, \"get_donations_for_donor\", { donor_id: accountId }); }, getDonationsForProject: (potId, projectId) => { return Near.view(potId, \"get_donations_for_project\", { project_id: projectId }); }, asyncGetDonationsForProject: (potId, projectId) => { return Near.asyncView(potId, \"get_donations_for_project\", { project_id: projectId }); }, getDonationsForRecipient: (potId, recipientId) => { return Near.view(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, asyncGetDonationsForRecipient: (potId, recipientId) => { return Near.asyncView(potId, \"get_donations_for_recipient\", { recipient_id: recipientId }); }, getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, \"get_application_by_project_id\", { project_id: projectId }); }, asyncGetApplicationByProjectId: (potId, projectId) => { return Near.asyncView(potId, \"get_application_by_project_id\", { project_id: projectId }); }, getApprovedApplications: potId => { return Near.view(potId, \"get_approved_applications\", {}); }, asyncGetApprovedApplications: potId => { return Near.asyncView(potId, \"get_approved_applications\", {}); }, getApplications: potId => { return Near.view(potId, \"get_applications\", {}); }, asyncGetApplications: potId => { return Near.asyncView(potId, \"get_applications\", {}); }, getPayoutsChallenges: potId => { return Near.view(potId, \"get_payouts_challenges\", {}); }, challengePayouts: (potId, reason) => { const depositFloat = reason.length * 0.00003 + 0.003; const transaction = { contractName: potId, methodName: \"challenge_payouts\", args: { reason }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, adminUpdatePayoutsChallenge: (potId, challengerId, notes, shouldResolveChallenge) => { const depositFloat = notes.length * 0.00003; const transaction = { contractName: potId, methodName: \"admin_update_payouts_challenge\", args: { challenger_id: challengerId, notes, resolve_challenge: shouldResolveChallenge }, deposit: Big(depositFloat).mul(Big(10).pow(24)), gas: \"300000000000000\" }; Near.call([transaction]); }, chefSetPayouts: (potId, payouts) => { const transaction = { contractName: potId, methodName: \"chef_set_payouts\", args: { payouts }, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, adminProcessPayouts: potId => { const transaction = { contractName: potId, methodName: \"admin_process_payouts\", args: {}, deposit: \"1\", gas: \"300000000000000\" }; Near.call([transaction]); }, getFlaggedAccounts: (potDetail, potId) => { const roles = [\"owner\", \"admins\", \"chef\"]; const allUsers = {}; roles.forEach(role => { const users = potDetail[role]; if (typeof users === \"object\") { users.forEach(user => { allUsers[user] = role === \"admins\" ? \"admin\" : role; }); } else { allUsers[users] = role; } }); const flaggedAccounts = []; const socialKeys = Object.keys(allUsers).map(user => \\`\\${user}/profile/**\\`); return new Promise((resolve, reject) => { getSocialProfile(socialKeys).then(profiles => { Object.entries(profiles).forEach(([user, { profile }]) => { const pLBlacklistedAccounts = JSON.parse(profile.pLBlacklistedAccounts || \"{}\"); const potFlaggedAcc = pLBlacklistedAccounts[potId] || {}; if (!isEmpty(potFlaggedAcc)) { flaggedAccounts.push({ flaggedBy: user, role: allUsers[user], potFlaggedAcc }); } }); resolve(flaggedAccounts); }).catch(error => { console.error(\"Error fetching social profiles:\", error); reject(error); }); }); }};PotSDK; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const useRoutes = () => { const contextData = useContext(\"alemRoutes\"); if (!contextData) { console.error(\"useRoutes: You need to call \\`RouterProvider()\\` first.\"); } const data = { routesInitialized: contextData.routesInitialized, activeRoute: contextData.activeRoute, routeParameterName: contextData.routeParameterName, routes: contextData.routes, routeType: contextData.routeType, routeParams: contextData.routeParams, history: contextData.history }; return data;}; const getLocation = () => { const routes = useRoutes(); return { pathname: routes.activeRoute, routes: routes.routes, isRoutesReady: routes.routes && routes.routes.length > 0 };}; const [activeRounds, setActiveRounds] = useState([]); const { pathname } = getLocation(); const showLiveBanner = !(pathname === \"pots\" || pathname === \"pot\"); const pots = PotFactorySDK.getPots(); const now = Date.now(); useEffect(() => { if (pots) { pots.forEach(pot => { PotSDK.asyncGetConfig(pot.id).then(potConfig => { const { public_round_start_ms, public_round_end_ms } = potConfig; if (public_round_start_ms < now && public_round_end_ms > now) { setActiveRounds(prevActiveRounds => [...prevActiveRounds, { ...potConfig, pot_id: pot.id }]); } }).catch(e => { console.error(\"error getting pot detail: \", e); }); }); } }, [pots]); const isSingleRound = activeRounds.length === 1; const limit = isSingleRound ? 20 : 10; const potName = activeRounds[0].pot_name.length > limit ? activeRounds[0].pot_name.slice(0, limit).trim() + \"...\" : activeRounds[0].pot_name; const textForOneRound = \\`\\${potName} round is live\\`; const textForMultipleRounds = \\`Pot round is live for \\${potName} and +\\${activeRounds.length - 1} More\\`; if (!activeRounds.length) return \"\"; return showLiveBanner ? <A_251> <A_252> <div> <div className=\"text\"> {isSingleRound ? textForOneRound : textForMultipleRounds} <a href={hrefWithParams(isSingleRound ? \\`?tab=pot&potId=\\${activeRounds[0].pot_id}\\` : \\`?tab=pots\\`)} className=\"link\"> <ReferrerIcon /> Donate now </a> </div> </div> </A_252> </A_251> : \"\"; `, App: ` const DonationModalProvider = () => { const { setDefaultData, updateData, getSelf } = createContext(\"donation-modal\"); setDefaultData({ successfulDonation: null, donationModalProps: null, setSuccessfulDonation: successfulDonation => { updateData({ successfulDonation, donationModalProps: null }); }, setDonationModalProps: donationModalProps => { updateData({ donationModalProps }); } });}; const Spinner = () => <div className=\"spinner-border\" role=\"status\"> <span className=\"visually-hidden\">Loading...</span> </div>; const DEFAULT_CART = {};const CART_KEY = \"cart\";const getCart = () => JSON.parse(Storage.get(CART_KEY)) || DEFAULT_CART;const CartProvider = () => { const { setDefaultData, updateData, getSelf } = createContext(\"cart-context\"); const updateCart = cart => { Storage.set(CART_KEY, JSON.stringify(cart)); updateData({ cart }); }; setDefaultData({ cart: getCart(), addItemstoCart: items => { const { cart } = getSelf(); items.forEach(item => { cart[item.id] = item; }); updateCart(cart); }, clearCart: () => { updateData({ cart: DEFAULT_CART }); updateCart(DEFAULT_CART); }, removeItemsFromCart: idsToRemove => { const { cart } = getSelf(); idsToRemove.forEach(id => { if (cart.hasOwnProperty(id)) { delete cart[id]; } }); updateCart(cart); } });}; const HeaderContainer = styled.div\\` width: 100%; padding: 80px 64px; @media (max-width: 768px) { padding: 36px 24px; }\\`;const HeaderContent = styled.div\\` display: flex; flex-direction: column;\\`;const HeaderTitle = styled.div\\` color: #2e2e2e; font-size: 88px; font-weight: 500; word-wrap: break-word; position: relative; text-align: center; z-index: 1; position: relative; font-family: \"Lora\"; @media (max-width: 768px) { font-size: 48px; }\\`;const HeaderDescription = styled.div\\` color: #2e2e2e; font-size: 32px; font-weight: 400; word-wrap: break-word; max-width: 866px; margin-top: 32px; @media (max-width: 768px) { font-size: 24px; text-align: center; }\\`;const A_17 = styled.div\\` display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 32px; margin-top: 32px;\\`;const Underline = styled.div\\` position: absolute; top: 44px; left: -40px; z-index: -1; @media (max-width: 768px) { top: 30px; left: -30px; }\\`; const Header = (__props__) => { const containerStyle = __props__.containerStyle ?? {}; const { tab } = props.alem.useParams(); const showStats = !tab || tab == \"projects\"; return <HeaderContainer style={containerStyle}> <HeaderContent style={{ alignItems: __props__.centered ? \"center\" : \"flex-start\" }}> <HeaderTitle> {__props__.title1} <Underline> <svg width=\"340\" height=\"42\" viewBox=\"0 0 340 42\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M7.29967 39C-14.0566 35.9491 49.9788 32.436 71.4774 30.6444C151.734 23.9564 232.915 20.5161 312.9 15\" stroke=\"#DD3345\" stroke-width=\"5\" stroke-linecap=\"round\" /> <path d=\"M31.2997 27C9.94337 23.9491 73.9788 20.436 95.4774 18.6444C175.734 11.9564 256.915 8.51608 336.9 3\" stroke=\"#DD3345\" stroke-width=\"5\" stroke-linecap=\"round\" /> </svg> </Underline> </HeaderTitle> {__props__.title2 && <HeaderTitle>{__props__.title2}</HeaderTitle>} <HeaderDescription style={{ textAlign: __props__.centered ? \"center\" : \"start\" }}> {__props__.description} </HeaderDescription> </HeaderContent> {__props__.children && __props__.children} <A_17> {__props__.buttonPrimary && __props__.buttonPrimary} {__props__.buttonSecondary && __props__.buttonSecondary} </A_17> {showStats && <Widget loading=\" \" code={props.alem.componentsCode.DonationStats} props={{ ...{ ...props } }} />} </HeaderContainer>; }; const CreateProject = () => { const { tab } = props.alem.useParams(); const edit = tab === \"editproject\"; return <> <Header {...{ title1: edit ? \"Edit your project\" : \"Create new project\", description: \\`\\${edit ? \"Update your \" : \"Create a \"} profile for your impact project to receive direct donations, qualify for funding rounds, join NEAR's accelerator, and get discovered across social platforms.\\`, centered: false, containerStyle: { background: \"#FEF6EE\" } }} /> <Widget loading=\" \" code={props.alem.componentsCode.CreateForm} props={{ ...{ edit: edit, ...props } }} /> </>; }; const svgContent = \\`<svg width=\"1320\" height=\"332\" viewBox=\"0 0 1320 332\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"1320\" height=\"1\" fill=\"#FEF6EE\"/><mask id=\"mask0_11854_22101\" style=\"mask-type:alpha\" maskUnits=\"userSpaceOnUse\" x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\"><rect x=\"-60\" y=\"-486\" width=\"1440\" height=\"1024\" fill=\"url(#paint0_radial_11854_22101)\"/></mask><g mask=\"url(#mask0_11854_22101)\"><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" fill=\"#FEF6EE\"/><path d=\"M358.131 1057C1475.87 1039 934.083 337 1942 337L1885.87 304C877.952 304 1419.74 1006 302 1024L358.131 1057Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" fill=\"#FEF6EE\"/><path d=\"M316.097 1021C1433.15 1003 891.698 301 1899 301L1842.9 268C835.601 268 1377.06 970 260 988L316.097 1021Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" fill=\"#F8D3B0\"/><path d=\"M262.097 985C1379.15 967 837.698 265 1845 265L1788.9 232C781.601 232 1323.06 934 206 952L262.097 985Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" fill=\"#F8D3B0\"/><path d=\"M218.131 949C1335.87 931 794.083 229 1802 229L1745.87 196C737.952 196 1279.74 898 162 916L218.131 949Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" fill=\"#F8D3B0\"/><path d=\"M161.097 914C1278.15 896 736.698 194 1744 194L1687.9 161C680.601 161 1222.06 863 105 881L161.097 914Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" fill=\"#F8D3B0\"/><path d=\"M104.131 878C1221.87 860 680.083 158 1688 158L1631.87 125C623.952 125 1165.74 827 48 845L104.131 878Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" fill=\"#F8D3B0\"/><path d=\"M49.0967 841C1166.15 823 624.698 121 1632 121L1575.9 88C568.601 88 1110.06 790 -7 808L49.0967 841Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" fill=\"#F8D3B0\"/><path d=\"M-7.86905 805C1109.87 787 568.083 85 1576 85L1519.87 52C511.952 52 1053.74 754 -64 772L-7.86905 805Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" fill=\"#F8D3B0\"/><path d=\"M-63.9375 770C1052.44 752 511.312 50 1518 50L1461.94 17C455.25 17 996.375 719 -120 737L-63.9375 770Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" fill=\"#F8D3B0\"/><path d=\"M-119.903 733C997.153 715 455.698 13 1463 13L1406.9 -20C399.601 -20 941.057 682 -176 700L-119.903 733Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" fill=\"#FEF6EE\"/><path d=\"M-423.903 588C693.153 570 151.698 -132 1159 -132L1102.9 -165C95.6012 -165 637.057 537 -480 555L-423.903 588Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" fill=\"#FEF6EE\"/><path d=\"M-460.869 565C656.869 547 115.083 -155 1123 -155L1066.87 -188C58.9524 -188 600.738 514 -517 532L-460.869 565Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" fill=\"#FEF6EE\"/><path d=\"M-514.903 529C602.153 511 60.6979 -191 1068 -191L1011.9 -224C4.6012 -224 546.057 478 -571 496L-514.903 529Z\" stroke=\"#FCE9D5\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" fill=\"#F8D3B0\"/><path d=\"M-568.903 493C548.153 475 6.69794 -227 1014 -227L957.903 -260C-49.3988 -260 492.057 442 -625 460L-568.903 493Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" fill=\"#F8D3B0\"/><path d=\"M-625.869 457C491.869 439 -49.9167 -263 958 -263L901.869 -296C-106.048 -296 435.738 406 -682 424L-625.869 457Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" fill=\"#F8D3B0\"/><path d=\"M-682.903 422C434.153 404 -107.302 -298 900 -298L843.903 -331C-163.399 -331 378.057 371 -739 389L-682.903 422Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" fill=\"#F8D3B0\"/><path d=\"M-739.869 386C377.869 368 -163.917 -334 844 -334L787.869 -367C-220.048 -367 321.738 335 -796 353L-739.869 386Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.3\"><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" fill=\"#F8D3B0\"/><path d=\"M-794.903 349C322.153 331 -219.302 -371 788 -371L731.903 -404C-275.399 -404 266.057 298 -851 316L-794.903 349Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" fill=\"#F8D3B0\"/><path d=\"M-851.869 313C265.869 295 -275.917 -407 732 -407L675.869 -440C-332.048 -440 209.738 262 -908 280L-851.869 313Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" fill=\"#F8D3B0\"/><path d=\"M-907.938 278C208.438 260 -332.688 -442 674 -442L617.938 -475C-388.75 -475 152.375 227 -964 245L-907.938 278Z\" stroke=\"#F4B37D\"/></g><g style=\"mix-blend-mode:multiply\" opacity=\"0.2\"><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" fill=\"#F8D3B0\"/><path d=\"M-963.903 241C153.153 223 -388.302 -479 619 -479L562.903 -512C-444.399 -512 97.0565 190 -1020 208L-963.903 241Z\" stroke=\"#F4B37D\"/></g></g><defs><radialGradient id=\"paint0_radial_11854_22101\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(660 -158.5) rotate(90) scale(724.5 1018.83)\"><stop stop-color=\"#FCE9D5\"/><stop offset=\"0.855072\" stop-color=\"#FEF6EE\" stop-opacity=\"0\"/></radialGradient></defs></svg>\\`;const HomeBannerStyle = { backgroundImage: \\`url(\"data:image/svg+xml;charset=utf-8,\\${encodeURIComponent(svgContent)}\")\\`, backgroundSize: \"cover\", backgroundRepeat: \"no-repeat\", backgroundColor: \"#FEF6EE\"}; const Button = ({ type, disabled, href, onClick, stopPropagation, style, target, iconSrc, text}) => { const getButtonBackground = () => { if (type === \"primary\") { if (disabled) { return \"#e5e5e5\"; } return \"#dd3345\"; } else if (type === \"secondary\") { return \"#FEF6EE\"; } else if (type === \"tertiary\") { return \"white\"; } }; const getButtonTextColor = () => { if (type === \"primary\") { if (disabled) { return \"darkgrey\"; } return \"white\"; } else if (type === \"secondary\") { return \"#2E2E2E\"; } }; const tag = href ? \"a\" : \"button\"; const ButtonContainer = styled[tag]\\` flex-direction: row; justify-content: center; align-items: center; padding: 16px 24px; background: \\${getButtonBackground()}; overflow: hidden; box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; border-radius: 6px; border: 1px solid #4a4a4a; gap: 8px; display: inline-flex; text-align: center; color: \\${getButtonTextColor()}; font-size: 14px; line-height: 16px; font-weight: 600; cursor: \\${disabled ? \"not-allowed\" : \"pointer\"} !important; &:hover { text-decoration: none; } \\`; const Icon = styled.img\\` width: 20px; height: 20px; object-fit: contain; \\`; return <ButtonContainer onClick={e => { if (stopPropagation) e.stopPropagation(); if (onClick) { onClick(e); } }} href={href} style={{ ...style }} target={target}> {iconSrc && <Icon src={iconSrc} />} {text} </ButtonContainer>;}; const A_32 = styled.div\\` display: flex; flex-direction: column; align-items: center; justify-content: center; > div:last-of-type { padding: 0px 175px; } @media only screen and (max-width: 992px) { > div:last-of-type { padding: 0px 20px; } }\\`;const A_33 = styled.div\\` display: flex; flex-direction: column; align-items: center; width: 100%; gap: 24px;\\`;const A_34 = styled.div\\` color: #292929; font-size: 60px; font-weight: 400; line-height: 72px; word-wrap: break-word; font-family: Lora;\\`;const A_35 = styled.div\\` display: flex; flex-direction: column; position: relative; width: 100%; justify-content: center; min-height: 400px; overflow: hidden; .background { position: absolute; pointer-events: none; height: 100%; left: 0; top: 0; } .content { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: center; padding: 64px 175px; } .sub-title { letter-spacing: 1.12px; font-weight: 500; font-size: 14px; margin-top: 0; margin-bottom: 24px; text-transform: uppercase; } .title { letter-spacing: -0.4px; font-weight: 500; font-size: 40px; font-family: \"Lora\"; margin: 0; } .info { display: flex; align-items: center; gap: 8px; font-size: 14px; margin-top: 24px; > svg { height: 1em; } } @media only screen and (max-width: 992px) { .content { padding: 64px 20px; } .title { font-size: 36px; } .btns { flex-direction: column; gap: 1rem; margin-top: 24px; } .line-break { display: none; } } @media only screen and (max-width: 480px) { .btns a { width: 100%; padding: 12px 0; } }\\`; const DeployPot = (__props__) => { return __props__.deploymentSuccess || state.deploymentSuccess ? <A_33> <A_34>Deployment Successful!</A_34> <Button {...{ type: \"primary\", text: \"View all pots\", style: __props__.style || {}, href: hrefWithParams(\\`?tab=pots\\`) }} /> </A_33> : <A_32> <A_35 style={{ ...HomeBannerStyle }}> <div className=\"content\"> <h3 className=\"sub-title\">Deploy pot</h3> <h1 className=\"title\"> Deploy a Quadratic <br className=\"line-break\" /> Funding Round </h1> <div className=\"info\"> <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M6.3335 3.66732H7.66683V5.00065H6.3335V3.66732ZM6.3335 6.33398H7.66683V10.334H6.3335V6.33398ZM7.00016 0.333984C3.32016 0.333984 0.333496 3.32065 0.333496 7.00065C0.333496 10.6807 3.32016 13.6673 7.00016 13.6673C10.6802 13.6673 13.6668 10.6807 13.6668 7.00065C13.6668 3.32065 10.6802 0.333984 7.00016 0.333984ZM7.00016 12.334C4.06016 12.334 1.66683 9.94065 1.66683 7.00065C1.66683 4.06065 4.06016 1.66732 7.00016 1.66732C9.94016 1.66732 12.3335 4.06065 12.3335 7.00065C12.3335 9.94065 9.94016 12.334 7.00016 12.334Z\" fill=\"#7B7B7B\" /> </svg> <div>Know More about Quadratic Funding</div> </div> </div> </A_35> <Widget loading=\" \" code={props.alem.componentsCode.ConfigForm} props={{ ...{ ...props } }} /> </A_32>; }; const potlockRegistryListId = 1;const _listContractId = props.alem.getAlemEnvironment() === \"staging\" ? \"lists.staging.potlock.near\" : \"lists.potlock.near\";const ListsSDK = { getContractId: () => _listContractId, getList: listId => { return Near.view(_listContractId, \"get_list\", { list_id: listId }); }, getPotlockRegistry: () => { return ListsSDK.getList(potlockRegistryListId); }, isRegistryAdmin: accountId => { const registry = ListsSDK.getPotlockRegistry(); return registry.admins && registry.admins.includes(accountId); }, getRegistrations: listId => { return Near.view(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, asyncGetRegistrations: listId => { return Near.asyncView(_listContractId, \"get_registrations_for_list\", { list_id: listId || potlockRegistryListId }); }, getRegistration: (listId, registrantId) => { const registrations = Near.view(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }); if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.view(_listContractId, \"get_registration\", { registration_id: registration.id }); } }, asyncGetRegistration: (listId, registrantId) => { return Near.asyncView(_listContractId, \"get_registrations_for_registrant\", { registrant_id: registrantId }).then(registrations => { if (registrations) { const registration = registrations.find(registration => registration.list_id === (listId || potlockRegistryListId)); return Near.asyncView(_listContractId, \"get_registration\", { registration_id: registration.id }); } }); }, isRegistrationApproved: (listId, registrantId) => { const registration = ListsSDK.getRegistration(listId, registrantId); return registration && registration.status === \"Approved\"; }};ListsSDK; const FeedPage = () => { const registrations = ListsSDK.getRegistrations() || []; const registrantIds = registrations.filter(reg => reg.status === \"Approved\").map(reg => reg.registrant_id); const Container = styled.div\\` padding: 24px 64px; @media screen and (max-width: 768px) { padding: 24px 16px; } \\`; return <Container> <Feed key=\"feed\" accounts={registrantIds} /> </Container>;}; const EditProfile = () => { return <div style={{ paddingTop: \"24px\" }}> <Widget src=\"mob.near/widget/ProfileEditor\" /> </div>;}; const A_152 = styled.div\\` display: flex; justify-content: space-between; align-items: center; padding: 68px 105px; border-radius: 12px; background: #f6f5f3; .text { font-family: \"Lora\"; max-width: 290px; font-size: 22px; font-style: italic; font-weight: 500; color: #292929; } img { width: 60%; } @media screen and (max-width: 768px) { flex-direction: column-reverse; padding: 24px 16px; .text { font-size: 16px; } img { width: 100%; } }\\`; const MergedIndexFeed = (compProps) => { if (!compProps.index) { return \"props.index is not defined\"; } const indices = JSON.parse(JSON.stringify(Array.isArray(compProps.index) ? compProps.index : [compProps.index])); const filter = compProps.filter; const renderItem = compProps.renderItem ?? ((item) => <div key={JSON.stringify(item)}> #{item.blockHeight}: {JSON.stringify(item)} </div>); const cachedRenderItem = (item, i) => { const key = JSON.stringify(item); if (!(key in state.cachedItems)) { state.cachedItems[key] = renderItem(item, i); } return state.cachedItems[key]; }; const initialRenderLimit = compProps.initialRenderLimit ?? 10; const addDisplayCount = compProps.nextLimit ?? initialRenderLimit; const reverse = !!compProps.reverse; const computeFetchFrom = (items, limit, desc) => { if (!items || items.length < limit) { return false; } const blockHeight = items[items.length - 1].blockHeight; return desc ? blockHeight - 1 : blockHeight + 1; }; const mergeItems = (iIndex, oldItems, newItems, desc) => { const index = indices[iIndex]; const items = [...new Set([...newItems.map((item) => ({ ...item, action: index.action, key: index.key, index: iIndex })), ...oldItems].map((i) => JSON.stringify(i)))].map((i) => JSON.parse(i)); items.sort((a, b) => a.blockHeight - b.blockHeight); if (desc) { items.reverse(); } return items; }; const jIndices = JSON.stringify(indices); if (jIndices !== state.jIndices) { State.update({ jIndices, feeds: indices.map(() => ({})), items: [], displayCount: initialRenderLimit, cachedItems: {} }); } let stateChanged = false; for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; let feedChanged = false; index.options = index.options || {}; index.options.limit = Math.min(Math.max(initialRenderLimit + addDisplayCount * 2, index.options.limit), 100); const desc = index.options.order === \"desc\"; const initialItems = Social.index(index.action, index.key, index.options, index.cacheOptions); if (initialItems === null) { continue; } const jInitialItems = JSON.stringify(initialItems); const nextFetchFrom = computeFetchFrom(initialItems, index.options.limit, desc); if (feed.jInitialItems !== jInitialItems) { feed.jInitialItems = jInitialItems; feedChanged = true; if (nextFetchFrom !== feed.initialNextFetchFrom) { feed.fetchFrom = false; feed.items = mergeItems(iIndex, [], initialItems, desc); feed.initialNextFetchFrom = nextFetchFrom; feed.nextFetchFrom = nextFetchFrom; } else { feed.items = mergeItems(iIndex, feed.items, initialItems, desc); } } feed.usedCount = 0; if (feedChanged) { state.feeds[iIndex] = feed; stateChanged = true; } } const filteredItems = []; while (filteredItems.length < state.displayCount) { let bestItem = null; for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; const desc = index.options.order === \"desc\"; if (!feed.items) { continue; } const item = feed.items[feed.usedCount]; if (!item) { continue; } if (bestItem === null || (desc ? item.blockHeight > bestItem.blockHeight : item.blockHeight < bestItem.blockHeight)) { bestItem = item; } } if (!bestItem) { break; } state.feeds[bestItem.index].usedCount++; if (filter) { if (filter.ignore) { if (bestItem.accountId in filter.ignore) { continue; } } if (filter.require) { if (!(bestItem.accountId in filter.require)) { continue; } } } filteredItems.push(bestItem); } for (let iIndex = 0; iIndex < indices.length; ++iIndex) { const index = indices[iIndex]; const feed = state.feeds[iIndex]; const desc = index.options.order === \"desc\"; let feedChanged = false; if ((feed.items.length || 0) - feed.usedCount < addDisplayCount * 2 && !feed.fetchFrom && feed.nextFetchFrom && feed.nextFetchFrom !== feed.fetchFrom) { feed.fetchFrom = feed.nextFetchFrom; feedChanged = true; } if (feed.fetchFrom) { const limit = addDisplayCount; const newItems = Social.index(index.action, index.key, Object.assign({}, index.options, { from: feed.fetchFrom, subscribe: undefined, limit })); if (newItems !== null) { feed.items = mergeItems(iIndex, feed.items, newItems, desc); feed.fetchFrom = false; feed.nextFetchFrom = computeFetchFrom(newItems, limit, desc); feedChanged = true; } } if (feedChanged) { state.feeds[iIndex] = feed; stateChanged = true; } } if (stateChanged) { State.update(); } const makeMoreItems = () => { State.update({ displayCount: state.displayCount + addDisplayCount }); }; const loader = <div className=\"loader\" key={\"loader\"}> <span className=\"spinner-grow spinner-grow-sm me-1\" role=\"status\" aria-hidden=\"true\" /> Loading ... </div>; const fetchMore = compProps.manual && (state.feeds.some((f) => !!f.fetchFrom) && filteredItems.length < state.displayCount ? loader : state.displayCount < filteredItems.length && <div key={\"loader more\"}> <a href=\"javascript:void\" onClick={(e) => makeMoreItems()}> {compProps.loadMoreText ?? \"Load more...\"} </a> </div>); const items = filteredItems ? filteredItems.slice(0, state.displayCount) : []; if (reverse) { items.reverse(); } const renderedItems = items.length === 0 ? <A_152> <div className=\"text\">This {compProps.tab === \"profile\" ? \"user\" : \"project\"} has not posted yet.</div> <img src=\"https://ipfs.near.social/ipfs/bafkreicwz5cuku3kxxp3wzldslpfzmfqgukpm2wwy7t7zuevkdp4gbl2uq\" alt=\"pots\" /> </A_152> : items.map(cachedRenderItem); return compProps.manual ? <> {reverse && fetchMore} {renderedItems} {!reverse && fetchMore} </> : <InfiniteScroll pageStart={0} loadMore={makeMoreItems} threshold={compProps.threshold ?? 250} hasMore={state.displayCount <= filteredItems.length} loader={loader}> {renderedItems} </InfiniteScroll>;}; const Post = postProps => { return <Widget loading={<div className=\"w-100\" style={{ height: \"200px\" }} />} src=\"mob.near/widget/MainPage.N.Post\" props={postProps} />;}; const Feed = (compProps) => { const post = compProps.post === undefined ?? true; const hashtags = compProps.hashtags || []; const indexKey = compProps.indexKey ?? \"main\"; const index = [{ action: \"post\", key: indexKey, options: { limit: 10, order: \"desc\", accountId: compProps.accounts }, cacheOptions: { ignoreCache: true } }, { action: \"repost\", key: indexKey, options: { limit: 10, order: \"desc\", accountId: compProps.accounts }, cacheOptions: { ignoreCache: true } }]; const isPremiumFeed = compProps.isPremiumFeed; const commentAccounts = compProps.commentAccounts; const renderedPosts = {}; const makePostItem = (a) => ({ type: \"social\", path: \\`\\${a.accountId}/post/main\\`, blockHeight: a.blockHeight }); const renderPost = (a) => { if (a.value.type !== \"md\") { return false; } const item = JSON.stringify(makePostItem(a)); if (item in renderedPosts) { return false; } renderedPosts[item] = true; const postProps = { accountId: a.accountId, blockHeight: a.blockHeight, isPremiumFeed, commentAccounts, indexKey, groupId: compProps.groupId, permissions: compProps.permissions }; return <div key={JSON.stringify(a)}> <Post {...postProps} /> </div>; }; const repostSvg = <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 2 24 24\" stroke=\"currentColor\" strokeWidth=\"1\"> <path fill-rule=\"evenodd\" d=\"M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z\" transform=\"rotate(180, 12, 12), translate(0, 4)\" /> <path fill-rule=\"evenodd\" d=\"M4.854 1.146a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L4 2.707V12.5A2.5 2.5 0 0 0 6.5 15h8a.5.5 0 0 0 0-1h-8A1.5 1.5 0 0 1 5 12.5V2.707l3.146 3.147a.5.5 0 1 0 .708-.708l-4-4z\" transform=\"translate(0, 4)\" /> </svg>; const extractParentPost = (item) => { if (!item || item.type !== \"social\" || !item.path || !item.blockHeight) { return undefined; } const accountId = item.path.split(\"/\")[0]; const parentPost = { accountId, blockHeight: item.blockHeight }; return \\`\\${accountId}/post/main\\` === item.path ? parentPost : undefined; }; const renderRepost = (a) => { if (a.value.type !== \"repost\") { return false; } const post = extractParentPost(a.value.item); if (!post) { return false; } const item = JSON.stringify(makePostItem(post)); if (item in renderedPosts) { return false; } renderedPosts[item] = true; const profileLineProps = { accountId: a.accountId, hideImage: true, hideAccountId: true, tooltip: true }; const postProps = { accountId: post.accountId, blockHeight: post.blockHeight, reposted: true, isPremiumFeed, commentAccounts, indexKey, groupId: compProps.groupId, permissions: compProps.permissions }; const postLoading = <div className=\"w-100\" style={{ height: \"200px\" }} />; return <div key={JSON.stringify(a)}> <div className=\"text-muted\" style={{ fontSize: \"13px\", fontWeight: 700, marginLeft: \"24px\", marginBottom: \"-24px\", paddingTop: \"4px\", position: \"relative\", zIndex: 1 }}> {repostSvg}{\" \"} <span style={{ marginLeft: \"8px\" }}> Reposted by <Widget loading={a.accountId} src=\"mob.near/widget/N.ProfileLine\" /> </span> </div> <Widget loading={postLoading} src=\"mob.near/widget/MainPage.N.Post\" props={postProps} /> </div>; }; const renderItem = (item) => { return item.action === \"post\" ? renderPost(item) : renderRepost(item); }; const Container = styled.div\\` display: flex; flex-direction: column; .post-btn { background: rgb(46, 46, 46); border-radius: 6px; padding: 12px 16px; border: none; color: white; } \\`; return <Container> {post && <Widget loading=\" \" code={props.alem.componentsCode.Compose} props={{ ...{ ...props } }} />} <MergedIndexFeed index={index} renderItem={renderItem} filter={compProps.filter} threshold={800} /> </Container>; }; const A_158 = styled.div\\` display: flex; flex-direction: column; .tab-content { padding: 1rem 0; }\\`;const Nav = styled.div\\` .nav-pills { background: #fbfbfb; font-weight: 500; --bs-nav-pills-border-radius: 0; --bs-nav-link-color: #000; --bs-nav-pills-link-active-color: #000; --bs-nav-pills-link-active-bg: #fbfbfb; --bs-nav-link-padding-y: 0.75rem; border-bottom: 1px solid #eee; padding-top: 3px; } .nav-link.active { border-bottom: 3px solid #dd3345; } .nav-item:not(:has(> .disabled)):hover { background: #dd334456; .nav-link { color: #dd3345; } } margin: 0 -12px;\\`; const hrefWithParams = href => { const env = props.alem.getAlemEnvironment() === \"staging\" ? \"staging\" : null; if (env) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}env=\\${env}\\`; } if (props.referrerId) { href = \\`\\${href}\\${href.includes(\"?\") ? \"&\" : \"?\"}referrerId=\\${props.referrerId}\\`; } return href;}; const FollowTabs = (__props__) => { const { accountId, projectId, nav } = __props__; const profileLink = hrefWithParams(\\`?tab=profile&accountId=\\${accountId}\\`); return <A_158> <Nav> <ul className=\"nav nav-pills nav-fill\" role=\"tablist\"> <li className=\"nav-item\" role=\"presentation\"> <a href={\\`\\${profileLink}&nav=followers\\`} className={\\`btn nav-link \\${nav === \"followers\" ? \"active\" : \"\"}\\`} role=\"tab\"> Followers </a> </li> <li className=\"nav-item\" role=\"presentation\"> <a href={\\`\\${profileLink}&nav=following\\`} className={\\`btn nav-link \\${nav === \"following\" ? \"active\" : \"\"}\\`} role=\"tab\"> Following </a> </li> </ul> </Nav> <div className=\"tab-content\"> <div className=\"tab-pane fade in show active\" role=\"tabpanel\"> <Widget loading=\" \" code={props.alem.componentsCode.FollowersList} props={{ ...{ accountId: projectId || accountId, nav: nav, ...props } }} /> </div> </div> </A_158>; }; const donorOptions = (accountId) => [{ label: \"Social Feed\", id: \"feed\", disabled: false, source: (componentProps) => <Feed {...componentProps} />, href: hrefWithParams(\\`?tab=profile&accountId=\\${accountId}&nav=feed\\`) }, { label: \"Donations\", id: \"donations\", disabled: false, source: (componentProps) => <Widget loading=\" \" code={props.alem.componentsCode.A_131} props={{ ...{ ...componentProps, ...props } }} />, href: hrefWithParams(\\`?tab=profile&accountId=\\${accountId}&nav=donations\\`) }, { label: \"\", id: \"followers\", disabled: false, source: FollowTabs }, { label: \"\", id: \"following\", disabled: false, source: FollowTabs }]; const A_132 = styled.div\\` display: flex; flex-direction: column;\\`; const DonorPage = () => { const { accountId: _accountId, nav } = props.alem.useParams(); const accountId = _accountId || context.accountId; const profile = Social.getr(\\`\\${accountId}/profile\\`); return <A_132> <Widget loading=\" \" code={props.alem.componentsCode.Body} props={{ ...{ nav: nav ?? \"donations\", navOptions: donorOptions(accountId), profile: profile, post: accountId === context.accountId, ...props } }} /> </A_132>; }; const routesPath = { CREATE_PROJECT_TAB: \"createproject\", EDIT_PROJECT_TAB: \"editproject\", PROJECTS_LIST_TAB: \"projects\", PROJECT_DETAIL_TAB: \"project\", CART_TAB: \"cart\", FEED_TAB: \"feed\", POTS_TAB: \"pots\", DEPLOY_POT_TAB: \"deploypot\", POT_DETAIL_TAB: \"pot\", DONORS_TAB: \"donors\", PROFILE_TAB: \"profile\", EDIT_PROFILE_TAB: \"editprofile\"}; const Routes = () => { const ProjectsRoute = props.alem.createRoute(routesPath.PROJECTS_LIST_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.ProjectsPage} props={{ ...{ ...props } }} />); const ProjectRoute = props.alem.createRoute(routesPath.PROJECT_DETAIL_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.ProjectPage} props={{ ...{ ...props } }} />); const DonorRoute = props.alem.createRoute(routesPath.PROFILE_TAB, () => <DonorPage />); const EditRoute = props.alem.createRoute(routesPath.EDIT_PROFILE_TAB, () => <EditProfile />); const PotsHomeRoute = props.alem.createRoute(routesPath.POTS_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.PotsHome} props={{ ...{ ...props } }} />); const PotRoute = props.alem.createRoute(routesPath.POT_DETAIL_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.Pot} props={{ ...{ ...props } }} />); const FeedRoute = props.alem.createRoute(routesPath.FEED_TAB, () => <FeedPage />); const DeployPotRoute = props.alem.createRoute(routesPath.DEPLOY_POT_TAB, () => <DeployPot />); const LeaderboardRoute = props.alem.createRoute(routesPath.DONORS_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.Leaderboard} props={{ ...{ ...props } }} />); const CartRoute = props.alem.createRoute(routesPath.CART_TAB, () => <Widget loading=\" \" code={props.alem.componentsCode.Cart} props={{ ...{ ...props } }} />); const CreateProjectRoute = props.alem.createRoute(routesPath.CREATE_PROJECT_TAB, () => <CreateProject />); const EditProjectRoute = props.alem.createRoute(routesPath.EDIT_PROJECT_TAB, () => <CreateProject />); const routes = [ProjectsRoute, ProjectRoute, DonorRoute, EditRoute, PotsHomeRoute, PotRoute, FeedRoute, DeployPotRoute, LeaderboardRoute, CartRoute, CreateProjectRoute, EditProjectRoute]; return <Widget loading=\" \" code={props.alem.componentsCode.Router} props={{ ...{ routes: routes, parameterName: \"tab\", ...props } }} />; }; const useDonationModal = () => useContext(\"donation-modal\"); const ModulesContext = () => { const { setDefaultData, updateData, getSelf } = createContext(\"alemModulesContext\"); setDefaultData({ calls: {}, callModule: (setupCode, code, callId, onComplete) => { if (!code || !callId) return; const codeStructure = \\` \\${setupCode}; event.source.postMessage({response: \\${code.replaceAll(\";\", \"\")}, forCallId: \\${callId}}, \"*\"); \\`; const updatedCalls = { ...getSelf().calls }; updatedCalls[callId] = { code: codeStructure, handler: onComplete }; updateData({ calls: updatedCalls }); }, removeCall: callId => { const updatedCalls = {}; const currentCalls = getSelf().calls; const calls = Object.keys(currentCalls); calls.forEach(call => { if (call !== callId.toString()) { updatedCalls[call] = currentCalls[call]; } }); updateData({ calls: updatedCalls }); } });}; const ModulesProvider = () => { ModulesContext(); const modulesHandler = \\` <script> window.addEventListener(\"message\", (event) => { if (event.data.code) { eval(event.data.code); } }, false); </script> \\`; const modules = useContext(\"alemModulesContext\"); const calls = modules.calls; const callsKeys = Object.keys(calls); return <> {callsKeys.map(callKey => <iframe style={{ height: 0, width: 0 }} srcDoc={modulesHandler} message={{ code: calls[callKey].code }} onMessage={message => { if (message) { calls[message.forCallId].handler(message.response); modules.removeCall(message.forCallId); } }} />)} </>;}; const Main = () => { const { transactionHashes: _transactionHashes } = props.alem.useParams(); const { successfulDonation, donationModalProps } = useDonationModal(); return <> <ModulesProvider /> <Widget loading=\" \" code={props.alem.componentsCode.A_250} props={{ ...{ ...props } }} /> <div className=\"app-content\"> <Routes /> </div> <Widget loading=\" \" code={props.alem.componentsCode.A_253} props={{ ...{ ...props } }} /> {(successfulDonation || _transactionHashes) && <Widget loading=\" \" code={props.alem.componentsCode.ModalSuccess} props={{ ...{ ...props } }} />} {donationModalProps && <Widget loading=\" \" code={props.alem.componentsCode.ModalDonation} props={{ ...{ ...props } }} />} </>; }; const createContext = contextKey => { const setDefaultData = defaultStateValue => { if (!state[contextKey] || !state[contextKey].initialized) { const stateKeys = Object.keys(defaultStateValue); let mainKeys = [...stateKeys]; mainKeys = mainKeys.filter((item, index) => mainKeys.indexOf(item) === index); State.update({ ...state, [contextKey]: { initialized: true, keys: mainKeys, ...defaultStateValue } }); } props = { ...props, ...state, [contextKey]: { ...state[contextKey] } }; }; const updateData = updates => { const updatedState = { [contextKey]: { ...state[contextKey], ...updates } }; State.update(updatedState); props = { ...props, ...updatedState }; }; const getSelf = () => props[contextKey]; return { setDefaultData, updateData, getSelf };}; const useContext = contextKey => { const wasContextInitialized = props[contextKey].initialized; if (!wasContextInitialized) { return {}; } const contextKeys = props[contextKey].keys; const contextItems = {}; contextKeys.forEach(key => { contextItems[key] = props[contextKey][key]; }); return contextItems;}; const ALEM_ROUTES_CONTEXT_KEY = \"alemRoutes\";const RouterContext = () => { const { setDefaultData, updateData, getSelf } = createContext(ALEM_ROUTES_CONTEXT_KEY); const updateAlemRoutesState = updatedState => { updateData({ ...updatedState }); }; const alemRoutesState = () => useContext(ALEM_ROUTES_CONTEXT_KEY); setDefaultData({ routesInitialized: false, activeRoute: \"\", routeParams: {}, history: [], routeParameterName: \"path\", routes: [], routeType: \"URLBased\", updateRouteParameters: routeProps => { const currentHistory = alemRoutesState().history; const hasPreviousHistory = currentHistory.length === 0 && routeProps.history; const updatedHistory = hasPreviousHistory ? routeProps.history : alemRoutesState().history; if (routeProps.activeRoute) { const newHistory = { route: routeProps.activeRoute, routeParams: routeProps.routeParams }; if (updatedHistory.length > 10) { updatedHistory.shift(); } if (updatedHistory.at(-1).route !== routeProps.activeRoute) { updatedHistory.push(newHistory); } } updateAlemRoutesState({ routes: routeProps.routes || getSelf().routes, routeType: routeProps.routeType || getSelf().routeType, activeRoute: routeProps.activeRoute || getSelf().activeRoute, routeParams: routeProps.routeParams || getSelf().routeParams, routeParameterName: routeProps.routeParameterName || getSelf().routeParameterName, history: updatedHistory, routesInitialized: true }); if (props.alem.keepRoute && routeProps.activeRoute) { Storage.privateSet(\"alem::keep-route\", updatedHistory); } } });}; const fontsLoaded = props.alem.loadExternalStyles([\"https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&display=swap\"]); RouterContext(); CartProvider(); DonationModalProvider(); return <div className=\"app-container\">{fontsLoaded ? <Main /> : <Spinner />}</div>; `, }, },};if (props.alem.keepRoute) { if (!props.alem.ready) { props.alem.promisify( () => Storage.privateGet(\"alem::keep-route\"), (data) => { updateAlemState({ previousRoute: data.route, previousRouteParams: data.routeParams, ready: true, }); }, () => { updateAlemState({ previousRoute: null, ready: true, }); }, 300, ); }} else { updateAlemState({ previousRoute: null, ready: true, });}const alemCssBody = `.app-container { margin-top: calc(-1 * var(--body-top-padding, 0));}.app-content { width: 100%; height: 100%; background: #ffffff; border-radius: 0rem 0rem 1.5rem 1.5rem; border-top: 1px solid var(--ui-elements-light, #eceef0); background: var(--base-white, #fff); &.form { border: none; background: #fafafa; }}* { font-family: \"Mona-Sans\"; font-style: normal; font-weight: 400;}@font-face { font-family: mona-sans; font-style: normal; font-weight: 400; src: local(\"Mona-Sans\"), url(https://fonts.cdnfonts.com/s/91271/Mona-Sans-Regular.woff) format(\"woff\");}@font-face { font-family: mona-sans; font-style: normal; font-weight: 500; src: local(\"Mona-Sans\"), url(https://fonts.cdnfonts.com/s/91271/Mona-Sans-Medium.woff) format(\"woff\");}@font-face { font-family: mona-sans; font-style: normal; font-weight: 600; src: local(\"Mona-Sans\"), url(https://fonts.cdnfonts.com/s/91271/Mona-Sans-SemiBold.woff) format(\"woff\");}@font-face { font-family: mona-sans; font-style: normal; font-weight: 700; src: local(\"Mona-Sans\"), url(https://fonts.cdnfonts.com/s/91271/Mona-Sans-Bold.woff) format(\"woff\");}`;const AlemTheme = styled.div` ${state.alem.alemExternalStylesBody} ${alemCssBody}`;const AlemApp = useMemo(() => { if (!props.alem.ready) { return \"\"; } const widgetLayer2code = ` const props = { ...props, alem: { ...props.alem, m: { }, } }; return ( <Widget loading=\" \" code={props.alem.componentsCode.App} props={props} /> ) `; return ( <AlemTheme> <Widget loading=\" \" code={widgetLayer2code} props={{ alem: props.alem }} /> </AlemTheme> );}, [props.alem.ready, props.alem.alemExternalStylesBody, props.alem.rootProps]);return AlemApp;\n " } } } } }
Result:
{ "block_height": "118156290" }
No logs
Receipt:
Predecessor ID:
Receiver ID:
Gas Burned:
223 Ggas
Tokens Burned:
0 
Transferred 0.17341  to potlock.near
Empty result
No logs