Skip to content

Commit

Permalink
feat: Add disable Third Party (#3153)
Browse files Browse the repository at this point in the history
* feat: Add disable Third Party

* fix: Tests
  • Loading branch information
LautaroPetaccio authored Aug 12, 2024
1 parent 06268da commit 667bf68
Show file tree
Hide file tree
Showing 29 changed files with 556 additions and 51 deletions.
18 changes: 18 additions & 0 deletions src/components/ActivityPage/Transaction/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
RECLAIM_NAME_SUCCESS,
CLAIM_NAME_TRANSACTION_SUBMITTED
} from 'modules/ens/actions'
import { DISABLE_THIRD_PARTY_SUCCESS } from 'modules/thirdParty/actions'
import { getSaleAddress, getTotalAmountOfMintedItems } from 'modules/collection/utils'
import { isEnoughClaimMana } from 'modules/ens/utils'
import { includes } from 'lib/address'
Expand Down Expand Up @@ -212,6 +213,23 @@ const Transaction = (props: Props) => {
/>
)
}
case DISABLE_THIRD_PARTY_SUCCESS: {
const { thirdPartyId, thirdPartyName } = tx.payload
return (
<TransactionDetail
thirdPartyId={thirdPartyId}
text={
<T
id="transaction.disable_third_party"
values={{
name: thirdPartyName
}}
/>
}
tx={tx}
/>
)
}
case PUBLISH_COLLECTION_SUCCESS:
case APPROVE_COLLECTION_SUCCESS:
case REJECT_COLLECTION_SUCCESS:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Atlas } from 'components/Atlas'
import CollectionImage from 'components/CollectionImage'
import ItemImage from 'components/ItemImage'
import Profile from 'components/Profile'
import { ThirdPartyImage } from 'components/ThirdPartyImage'
import { ENSImage } from '../ENSImage'
import { Props } from './TransactionDetail.types'
import './TransactionDetail.css'
Expand All @@ -22,7 +23,7 @@ const getHref = (tx: Transaction) => {
}

const Image = (props: Props) => {
const { selection, address, collectionId, item, subdomain, slotsToyBuy } = props
const { selection, address, collectionId, item, subdomain, slotsToyBuy, thirdPartyId } = props

const set = useMemo(() => new Set((selection || []).map(coord => coordsToId(coord.x, coord.y))), [selection])
const selectedStrokeLayer: Layer = useCallback((x, y) => (set.has(coordsToId(x, y)) ? { color: '#ff0044', scale: 1.4 } : null), [set])
Expand All @@ -41,6 +42,8 @@ const Image = (props: Props) => {
return <ENSImage subdomain={subdomain} isSmall />
} else if (slotsToyBuy) {
return <div className="slot-image" />
} else if (thirdPartyId) {
return <ThirdPartyImage thirdPartyId={thirdPartyId} />
} else {
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type Props = {
item?: Item
subdomain?: string
slotsToyBuy?: number
thirdPartyId?: string
text: React.ReactNode
tx: Transaction
}
2 changes: 1 addition & 1 deletion src/components/CollectionsPage/CollectionsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export default function CollectionsPage(props: Props) {
<Field
placeholder={t('itemdrawer.search_items')}
className="collections-search-field"
input={{ icon: 'search', iconPosition: 'left' }}
input={{ icon: 'search' }}
onChange={handleSearchChange}
icon={<UIIcon name="search" className="searchIcon" />}
iconPosition="left"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ import { rejectCollectionRequest, REJECT_COLLECTION_REQUEST } from 'modules/coll
import { getLoading as getLoadingCollection, hasPendingCurationTransaction } from 'modules/collection/selectors'
import { getLoading as getLoadingCuration } from 'modules/curations/collectionCuration/selectors'
import { rejectCollectionCurationRequest, REJECT_COLLECTION_CURATION_REQUEST } from 'modules/curations/collectionCuration/actions'
import { MapStateProps, MapDispatchProps, MapDispatch } from './RejectionModal.types'
import { hasPendingDisableThirdPartyTransaction, isDisablingThirdParty } from 'modules/thirdParty/selectors'
import { disableThirdPartyRequest } from 'modules/thirdParty/actions'
import { MapStateProps, MapDispatchProps, MapDispatch, OwnProps } from './RejectionModal.types'
import RejectionModal from './RejectionModal'

const mapState = (state: RootState): MapStateProps => ({
const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => ({
isLoading:
isLoadingType(getLoadingCollection(state), REJECT_COLLECTION_REQUEST) ||
isLoadingType(getLoadingCuration(state), REJECT_COLLECTION_CURATION_REQUEST),
hasPendingTransaction: hasPendingCurationTransaction(state)
isLoadingType(getLoadingCuration(state), REJECT_COLLECTION_CURATION_REQUEST) ||
isDisablingThirdParty(state),
hasPendingTransaction:
hasPendingCurationTransaction(state) ||
Boolean(ownProps.thirdParty && hasPendingDisableThirdPartyTransaction(state, ownProps.thirdParty.id))
})

const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
onReject: collection => dispatch(rejectCollectionRequest(collection)),
onRejectCuration: collection => dispatch(rejectCollectionCurationRequest(collection))
onRejectCuration: collection => dispatch(rejectCollectionCurationRequest(collection)),
onDisableThirdParty: (id: string) => dispatch(disableThirdPartyRequest(id))
})

export default connect(mapState, mapDispatch)(RejectionModal)
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const RejectionModal = ({
open,
type,
isLoading,
thirdParty,
hasPendingTransaction,
onDisableThirdParty,
collection,
curation,
onReject,
Expand All @@ -29,6 +31,8 @@ const RejectionModal = ({
return i18nBase + '.disable_collection'
case RejectionType.REJECT_CURATION:
return i18nBase + '.reject_curation'
case RejectionType.DISABLE_THIRD_PARTY:
return i18nBase + '.disable_third_party'
default:
return ''
}
Expand All @@ -42,21 +46,27 @@ const RejectionModal = ({
case RejectionType.REJECT_CURATION:
onRejectCuration(curation!.collectionId)
break
case RejectionType.DISABLE_THIRD_PARTY:
if (thirdParty) {
onDisableThirdParty(thirdParty.id)
}
break
default:
}
}

const shouldShowVeredict =
const shouldShowVerdict =
(type === RejectionType.DISABLE_COLLECTION && !collection.isApproved) ||
(type === RejectionType.DISABLE_THIRD_PARTY && !thirdParty?.isApproved) ||
(type === RejectionType.REJECT_COLLECTION && !collection.isApproved) ||
(type === RejectionType.REJECT_CURATION && curation?.status === CurationStatus.REJECTED)

return (
<Modal size="tiny" className="RejectionModal" open={open}>
{hasPendingTransaction ? (
<PendingTransaction i18nKey={i18nKey} />
) : shouldShowVeredict ? (
<Veredict link={collection.forumLink} onClose={onClose} />
) : shouldShowVerdict ? (
<Verdict link={collection.forumLink} onClose={onClose} />
) : (
<Confirmation i18nKey={i18nKey} isLoading={isLoading} onClose={onClose} onConfirm={handleReview} />
)}
Expand All @@ -82,7 +92,7 @@ const PendingTransaction = ({ i18nKey }: { i18nKey: string }) => (
</>
)

const Veredict = ({ link, onClose }: { link?: string; onClose: () => void }) => (
const Verdict = ({ link, onClose }: { link?: string; onClose: () => void }) => (
<>
<Modal.Header>{t(i18nBase + '.veredict_explanation')}</Modal.Header>
<Modal.Content>{t(i18nBase + '.go_to_forum')}</Modal.Content>
Expand Down Expand Up @@ -119,7 +129,9 @@ const Confirmation = ({
<NetworkButton primary onClick={onConfirm} disabled={isLoading} loading={isLoading} network={Network.MATIC}>
{t(`${i18nKey}.action`)}
</NetworkButton>
<Button onClick={onClose}>{t('global.cancel')}</Button>
<Button disabled={isLoading} onClick={onClose}>
{t('global.cancel')}
</Button>
</Modal.Actions>
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import { InitiateApprovalFlowAction, rejectCollectionRequest, RejectCollectionRe
import { Collection } from 'modules/collection/types'
import { rejectCollectionCurationRequest, RejectCollectionCurationRequestAction } from 'modules/curations/collectionCuration/actions'
import { CollectionCuration } from 'modules/curations/collectionCuration/types'
import { disableThirdPartyRequest, DisableThirdPartyRequestAction } from 'modules/thirdParty/actions'
import { ThirdParty } from 'modules/thirdParty/types'

export enum RejectionType {
REJECT_COLLECTION = 'REJECT_COLLECTION',
REJECT_CURATION = 'REJECT_CURATION',
DISABLE_COLLECTION = 'DISABLE_COLLECTION'
DISABLE_COLLECTION = 'DISABLE_COLLECTION',
DISABLE_THIRD_PARTY = 'DISABLE_THIRD_PARTY'
}

export type Props = {
Expand All @@ -17,12 +20,16 @@ export type Props = {
hasPendingTransaction: boolean
collection: Collection
curation: CollectionCuration | null
thirdParty: ThirdParty | null
onReject: typeof rejectCollectionRequest
onDisableThirdParty: (...args: Parameters<typeof disableThirdPartyRequest>) => unknown
onRejectCuration: typeof rejectCollectionCurationRequest
onClose: () => void
}

export type MapStateProps = Pick<Props, 'isLoading' | 'hasPendingTransaction'>
export type MapDispatchProps = Pick<Props, 'onReject' | 'onRejectCuration'>
export type MapDispatch = Dispatch<RejectCollectionRequestAction | RejectCollectionCurationRequestAction | InitiateApprovalFlowAction>
export type OwnProps = Pick<Props, 'open' | 'type' | 'collection' | 'curation'>
export type MapDispatchProps = Pick<Props, 'onReject' | 'onRejectCuration' | 'onDisableThirdParty'>
export type MapDispatch = Dispatch<
RejectCollectionRequestAction | RejectCollectionCurationRequestAction | InitiateApprovalFlowAction | DisableThirdPartyRequestAction
>
export type OwnProps = Pick<Props, 'open' | 'type' | 'collection' | 'curation' | 'thirdParty'>
9 changes: 7 additions & 2 deletions src/components/ItemEditorPage/TopPanel/TopPanel.container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import { isWalletCommitteeMember } from 'modules/committee/selectors'
import { getSelectedCollectionId, isReviewing } from 'modules/location/selectors'
import { setCollectionCurationAssigneeRequest } from 'modules/curations/collectionCuration/actions'
import { FETCH_COLLECTION_REQUEST, initiateApprovalFlow, initiateTPApprovalFlow } from 'modules/collection/actions'
import { MapStateProps, MapDispatchProps, MapDispatch } from './TopPanel.types'
import TopPanel from './TopPanel'
import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors'
import { getCollection } from 'modules/collection/selectors'
import { FETCH_ITEM_CURATIONS_REQUEST } from 'modules/curations/itemCuration/actions'
import { FETCH_COLLECTION_ITEMS_REQUEST } from 'modules/item/actions'
import { getCollectionItems, getLoading as getLoadingItems, getPaginationData } from 'modules/item/selectors'
import { getCuration } from 'modules/curations/collectionCuration/selectors'
import { getItemCurations, getLoading as getLoadingItemCurations } from 'modules/curations/itemCuration/selectors'
import { getCollectionThirdParty } from 'modules/thirdParty/selectors'
import { isTPCollection } from 'modules/collection/utils'
import { MapStateProps, MapDispatchProps, MapDispatch } from './TopPanel.types'
import TopPanel from './TopPanel'

const mapState = (state: RootState): MapStateProps => {
const selectedCollectionId = getSelectedCollectionId(state)
Expand All @@ -23,12 +25,15 @@ const mapState = (state: RootState): MapStateProps => {
const itemCurations = collection ? getItemCurations(state, collection.id) : []
const curation = selectedCollectionId ? getCuration(state, selectedCollectionId) : null
const itemsPaginationData = selectedCollectionId ? getPaginationData(state, selectedCollectionId) : undefined
const thirdParty = collection && isTPCollection(collection) ? getCollectionThirdParty(state, collection) : null

return {
address: getAddress(state),
items,
totalItems: itemsPaginationData?.total || null,
collection,
itemCurations,
thirdParty,
curation,
chainId: getChainId(state),
isConnected: isConnected(state),
Expand Down
24 changes: 14 additions & 10 deletions src/components/ItemEditorPage/TopPanel/TopPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default class TopPanel extends React.PureComponent<Props, State> {
setShowRejectionModal = (showRejectionModal: RejectionType | null) => this.setState({ showRejectionModal })

renderPage = (collection: Collection) => {
const { items, itemCurations, curation, totalItems } = this.props
const { items, itemCurations, curation, totalItems, thirdParty } = this.props
const { showRejectionModal, showApproveConfirmModal } = this.state
const { chainId } = this.props
const type = getCollectionType(collection)
Expand Down Expand Up @@ -71,6 +71,7 @@ export default class TopPanel extends React.PureComponent<Props, State> {
open={true}
collection={collection}
curation={curation}
thirdParty={thirdParty}
onClose={() => this.setShowRejectionModal(null)}
/>
)}
Expand All @@ -87,7 +88,7 @@ export default class TopPanel extends React.PureComponent<Props, State> {
}

renderButton = (type: ButtonType, collection: Collection, curation: CollectionCuration | null) => {
const { address, onInitiateApprovalFlow, onInitiateTPApprovalFlow, reviewedItems, totalItems } = this.props
const { address, thirdParty, onInitiateApprovalFlow, onInitiateTPApprovalFlow, reviewedItems, totalItems } = this.props

const onClickMap = {
[ButtonType.APPROVE]: () =>
Expand All @@ -97,7 +98,8 @@ export default class TopPanel extends React.PureComponent<Props, State> {
? onInitiateTPApprovalFlow(collection)
: onInitiateApprovalFlow(collection),
[ButtonType.ENABLE]: () => onInitiateApprovalFlow(collection),
[ButtonType.DISABLE]: () => this.setShowRejectionModal(RejectionType.DISABLE_COLLECTION),
[ButtonType.DISABLE]: () =>
this.setShowRejectionModal(thirdParty ? RejectionType.DISABLE_THIRD_PARTY : RejectionType.DISABLE_COLLECTION),
[ButtonType.REJECT]: () => {
if (curation?.status === CurationStatus.PENDING) {
this.setShowRejectionModal(RejectionType.REJECT_CURATION)
Expand Down Expand Up @@ -147,18 +149,20 @@ export default class TopPanel extends React.PureComponent<Props, State> {
}

renderTPButtons = (collection: Collection, collectionCuration: CollectionCuration | null, itemCurations: ItemCuration[] | null) => {
const { reviewedItems, totalItems } = this.props
const shouldShowApproveButton = itemCurations?.some(itemCuration => itemCuration.status === CurationStatus.PENDING)
const { reviewedItems, totalItems, thirdParty } = this.props
const areCurationsPending = itemCurations?.some(itemCuration => itemCuration.status === CurationStatus.PENDING)
return (
<>
<Header sub>
{t('item_editor.top_panel.reviewed_counter', { count: reviewedItems.length, threshold: getTPThresholdToReview(totalItems!) })}
{areCurationsPending
? t('item_editor.top_panel.reviewed_counter', { count: reviewedItems.length, threshold: getTPThresholdToReview(totalItems!) })
: t('item_editor.top_panel.not_enough_items_to_curate_more')}
{reviewedItems.length >= getTPThresholdToReview(totalItems!) ? <Icon name="check circle" /> : null}
</Header>
{shouldShowApproveButton ? this.renderButton(ButtonType.APPROVE, collection, collectionCuration) : null}
{this.renderButton(ButtonType.REJECT, collection, collectionCuration)}
{/* TODO: the disable button from below is not the same as the original disable one, it will be implemented once the sagas are ready */}
{/* {this.renderButton(ButtonType.DISABLE, collection, collectionCuration)} */}
{areCurationsPending && this.renderButton(ButtonType.APPROVE, collection, collectionCuration)}
{areCurationsPending && this.renderButton(ButtonType.REJECT, collection, collectionCuration)}
{!areCurationsPending && thirdParty?.isApproved && this.renderButton(ButtonType.DISABLE, collection, null)}
{/* TODO: Add when enabling is possible {!areCurationsPending && !thirdParty?.isApproved && thirdParty?.root && this.renderButton(ButtonType.ENABLE, collection, null)} */}
</>
)
}
Expand Down
5 changes: 4 additions & 1 deletion src/components/ItemEditorPage/TopPanel/TopPanel.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@ import {
setCollectionCurationAssigneeRequest,
SetCollectionCurationAssigneeRequestAction
} from 'modules/curations/collectionCuration/actions'
import { RejectionType } from './RejectionModal/RejectionModal.types'
import { Collection } from 'modules/collection/types'
import { CollectionCuration } from 'modules/curations/collectionCuration/types'
import { ItemCuration } from 'modules/curations/itemCuration/types'
import { ThirdParty } from 'modules/thirdParty/types'
import { RejectionType } from './RejectionModal/RejectionModal.types'

export type Props = {
items: Item[]
collection: Collection | null
curation: CollectionCuration | null
itemCurations: ItemCuration[] | null
thirdParty: ThirdParty | null
isLoading: boolean
reviewedItems: Item[]
totalItems: number | null
Expand Down Expand Up @@ -57,6 +59,7 @@ export type MapStateProps = Pick<
| 'collection'
| 'curation'
| 'itemCurations'
| 'thirdParty'
| 'isLoading'
| 'address'
| 'chainId'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,3 @@
margin: auto;
margin-bottom: 20px;
}

.thirdPartyLogo {
width: 48px;
height: 48px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import {
Loader,
Dropdown,
DropdownProps,
InfoTooltip,
Blockie
InfoTooltip
} from 'decentraland-ui'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import { getArrayOfPagesFromTotal } from 'lib/api/pagination'
Expand All @@ -24,6 +23,7 @@ import LoggedInDetailPage from 'components/LoggedInDetailPage'
import CollectionProvider from 'components/CollectionProvider'
import NotFound from 'components/NotFound'
import BuilderIcon from 'components/Icon'
import { ThirdPartyImage } from 'components/ThirdPartyImage'
import Back from 'components/Back'
import { shorten } from 'lib/address'
import { CopyToClipboard } from 'components/CopyToClipboard'
Expand Down Expand Up @@ -198,7 +198,7 @@ export default function ThirdPartyCollectionDetailPage({
<Back absolute onClick={handleGoBack} />
<div className={styles.content}>
<div className={styles.title}>
<Blockie className={styles.thirdPartyLogo} seed={thirdParty.id} size={8} scale={8} shape="circle" />
<ThirdPartyImage thirdPartyId={thirdParty.id} />
<Header size="large" className={styles.name} onClick={handleEditName}>
{collection.name}
</Header>
Expand Down
4 changes: 4 additions & 0 deletions src/components/ThirdPartyImage/ThirdPartyImage.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.main {
width: 48px;
height: 48px;
}
9 changes: 9 additions & 0 deletions src/components/ThirdPartyImage/ThirdPartyImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Blockie } from 'decentraland-ui'
import classNames from 'classnames'
import styles from './ThirdPartyImage.module.css'
import { Props } from './ThirdPartyImage.types'

export const ThirdPartyImage = (props: Props) => {
const { thirdPartyId, className, shape } = props
return <Blockie className={classNames(styles.main, className)} seed={thirdPartyId} size={8} scale={8} shape={shape ?? 'circle'} />
}
5 changes: 5 additions & 0 deletions src/components/ThirdPartyImage/ThirdPartyImage.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type Props = {
className?: string
thirdPartyId: string
shape?: 'circle' | 'square'
}
2 changes: 2 additions & 0 deletions src/components/ThirdPartyImage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './ThirdPartyImage'
export * from './ThirdPartyImage.types'
Loading

0 comments on commit 667bf68

Please sign in to comment.