diff --git a/package.json b/package.json index 2ee261d..032289d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@poap-xyz/poap-family", - "version": "1.13.16", + "version": "1.13.17", "author": { "name": "POAP", "url": "https://poap.xyz" diff --git a/src/components/EventsCompare.js b/src/components/EventsCompare.js new file mode 100644 index 0000000..e365f2d --- /dev/null +++ b/src/components/EventsCompare.js @@ -0,0 +1,230 @@ +import PropTypes from 'prop-types' +import { createRef, useContext, useEffect, useMemo, useState } from 'react' +import { SettingsContext } from 'stores/cache' +import { DropProps } from 'models/drop' +import { + getAddressInCommonAddresses, + getAddressInCommonEventIds, +} from 'models/in-common' +import { intersection } from 'utils/array' +import { getColorForSeed } from 'utils/color' +import Card from 'components/Card' +import EventHeader from 'components/EventHeader' +import AddressOwner from 'components/AddressOwner' +import EventButtonGroup from 'components/EventButtonGroup' + +/** + * @param {PropTypes.InferProps} props + */ +function EventsCompare({ + eventIds, + events, + inCommon, + createHeaderActions, + createBottomButtons, +}) { + const { settings } = useContext(SettingsContext) + /** + * @type {ReturnType>} + */ + const [highlighted, setHighlighted] = useState(null) + /** + * @type {ReturnType>>>>>} + */ + const [liRefs, setLiRefs] = useState({}) + + const adressesColors = useMemo( + () => eventIds.length < 2 + ? {} + : Object.fromEntries( + intersection( + // @ts-ignore + ...eventIds.map((eventId) => inCommon[eventId]) + ) + .map( + (address) => [ + address, + getColorForSeed(address), + ] + ) + ), + [eventIds, inCommon] + ) + + useEffect( + () => { + if (eventIds.length > 0) { + /** + * @type {Record>>>} + */ + const refs = {} + for (const eventId of eventIds) { + if (inCommon[eventId].length > 0) { + refs[eventId] = {} + for (const owner of inCommon[eventId]) { + /** + * @type {ReturnType>} + */ + const ref = createRef() + refs[eventId][owner] = ref + } + } + } + if (Object.keys(refs).length > 0) { + setLiRefs(refs) + } + } + }, + [eventIds, inCommon] + ) + + /** + * @param {number} ownerEventId + * @param {string} owner + */ + const onOwnerEnter = (ownerEventId, owner) => { + if ( + owner in adressesColors && + settings && + settings.autoScrollCollectors + ) { + for (const eventId of eventIds) { + if ( + eventId !== ownerEventId && + eventId && liRefs && + owner in liRefs[eventId] && + liRefs[eventId][owner].current + ) { + liRefs[eventId][owner].current.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }) + } + } + if ( + ownerEventId in liRefs && + owner in liRefs[ownerEventId] && + liRefs[ownerEventId][owner].current + ) { + liRefs[ownerEventId][owner].current.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }) + } + } + setHighlighted((current) => ( + current !== owner && + owner in adressesColors + ? owner + : current + )) + } + + /** + * @param {number} ownerEventId + * @param {string} owner + */ + const onOwnerLeave = (ownerEventId, owner) => { + setHighlighted((current) => ( + current === owner && + owner in adressesColors + ? null + : current + )) + } + + return ( +
+ {eventIds.map((eventId) => +
+ + + {createHeaderActions != null && ( +
+ {createHeaderActions(eventId)} +
+ )} +

+ {inCommon[eventId].length}{' '} + collector{inCommon[eventId].length === 1 ? '' : 's'} + {' '}in common +

+
+
    + {inCommon[eventId].map((owner) => { + const inCommonEventIds = getAddressInCommonEventIds( + inCommon, + owner + ) + const inCommonAddresses = getAddressInCommonAddresses( + inCommon, + inCommonEventIds, + owner + ) + return ( +
  • { + onOwnerEnter(eventId, owner) + }} + onMouseLeave={() => { + onOwnerLeave(eventId, owner) + }} + > + +
  • + ) + })} +
+
+ + {createBottomButtons != null && + createBottomButtons(eventId)} + +
+
+ )} +
+ ) +} + +EventsCompare.propTypes = { + eventIds: PropTypes.arrayOf(PropTypes.number.isRequired).isRequired, + events: PropTypes.objectOf( + PropTypes.shape(DropProps).isRequired + ).isRequired, + inCommon: PropTypes.objectOf( + PropTypes.arrayOf( + PropTypes.string.isRequired + ).isRequired + ).isRequired, + createHeaderActions: PropTypes.func, + createBottomButtons: PropTypes.func, +} + +export default EventsCompare diff --git a/src/components/EventsOwners.js b/src/components/EventsOwners.js index d277327..c1d679e 100644 --- a/src/components/EventsOwners.js +++ b/src/components/EventsOwners.js @@ -55,7 +55,10 @@ function EventsOwners({ */ const [showAll, setShowAll] = useState(all) - let ownersEntries = inverseOwnersSortedEntries(owners) + let ownersEntries = useMemo( + () => inverseOwnersSortedEntries(owners), + [owners] + ) const ownersEventIds = Object.keys(owners).map( (rawEventId) => parseInt(rawEventId) @@ -67,13 +70,18 @@ function EventsOwners({ /** * @type {string[]} */ - const inCommonAddresses = [] - - for (const [ownerAddress, ownerEventIds] of ownersEntries) { - if (ownerEventIds.length === eventsTotal) { - inCommonAddresses.push(ownerAddress) - } - } + const inCommonAddresses = useMemo( + () => { + const inCommonAddresses = [] + for (const [ownerAddress, ownerEventIds] of ownersEntries) { + if (ownerEventIds.length === eventsTotal) { + inCommonAddresses.push(ownerAddress) + } + } + return inCommonAddresses + }, + [ownersEntries, eventsTotal] + ) const inCommonOwnersTotal = inCommonAddresses.length diff --git a/src/components/InCommon.js b/src/components/InCommon.js index 3d3d1d0..a805309 100644 --- a/src/components/InCommon.js +++ b/src/components/InCommon.js @@ -1,23 +1,16 @@ import PropTypes from 'prop-types' -import { createRef, useContext, useEffect, useMemo, useState } from 'react' -import { SettingsContext } from 'stores/cache' +import { useMemo, useState } from 'react' import { DropProps } from 'models/drop' import { filterInCommon, - getAddressInCommonAddresses, - getAddressInCommonEventIds, INCOMMON_EVENTS_LIMIT, sortInCommonEntries, } from 'models/in-common' -import { intersection } from 'utils/array' -import { getColorForSeed } from 'utils/color' import ButtonLink from 'components/ButtonLink' import Card from 'components/Card' import ErrorMessage from 'components/ErrorMessage' -import EventHeader from 'components/EventHeader' import EventsPowers from 'components/EventsPowers' -import EventButtonGroup from 'components/EventButtonGroup' -import AddressOwner from 'components/AddressOwner' +import EventsCompare from 'components/EventsCompare' import ButtonGroup from 'components/ButtonGroup' import ButtonClose from 'components/ButtonClose' import 'styles/in-common.css' @@ -50,7 +43,6 @@ function InCommon({ */ (eventId) => [], }) { - const { settings } = useContext(SettingsContext) /** * @type {ReturnType>} */ @@ -59,14 +51,6 @@ function InCommon({ * @type {ReturnType>} */ const [activeEventIds, setActiveEventIds] = useState([]) - /** - * @type {ReturnType>} - */ - const [ownerHighlighted, setOwnerHighlighted] = useState(null) - /** - * @type {ReturnType>>>>>} - */ - const [liRefs, setLiRefs] = useState({}) const inCommonEntries = useMemo(() => sortInCommonEntries( @@ -150,109 +134,9 @@ function InCommon({ } } - const activeAdressesColors = useMemo( - () => activeEventIds.length < 2 - ? {} - : Object.fromEntries( - intersection( - // @ts-ignore - ...activeEventIds.map((activeEventId) => inCommon[activeEventId]) - ) - .map( - (address) => [ - address, - getColorForSeed(address), - ] - ) - ), - [activeEventIds, inCommon] - ) - const inCommonTotal = inCommonEntries.length const hasMore = inCommonTotal > inCommonLimit - useEffect( - () => { - if (activeEventIds.length > 0) { - /** - * @type {Record>>>} - */ - const refs = {} - for (const activeEventId of activeEventIds) { - if (inCommon[activeEventId].length > 0) { - refs[activeEventId] = {} - for (const owner of inCommon[activeEventId]) { - /** - * @type {ReturnType>} - */ - const ref = createRef() - refs[activeEventId][owner] = ref - } - } - } - if (Object.keys(refs).length > 0) { - setLiRefs(refs) - } - } - }, - [activeEventIds, inCommon] - ) - - /** - * @param {number} activeEventId - * @param {string} owner - */ - const onOwnerEnter = (activeEventId, owner) => { - if ( - owner in activeAdressesColors && - settings && - settings.autoScrollCollectors - ) { - for (const eventId of activeEventIds) { - if ( - eventId !== activeEventId && - eventId && liRefs && - owner in liRefs[eventId] && - liRefs[eventId][owner].current - ) { - liRefs[eventId][owner].current.scrollIntoView({ - behavior: 'smooth', - block: 'center', - }) - } - } - if ( - activeEventId in liRefs && - owner in liRefs[activeEventId] && - liRefs[activeEventId][owner].current - ) { - liRefs[activeEventId][owner].current.scrollIntoView({ - behavior: 'smooth', - block: 'center', - }) - } - } - setOwnerHighlighted((current) => ( - current !== owner && - owner in activeAdressesColors - ? owner - : current - )) - } - - /** - * @param {number} activeEventId - * @param {string} owner - */ - const onOwnerLeave = (activeEventId, owner) => { - setOwnerHighlighted((current) => ( - current === owner && - owner in activeAdressesColors - ? null - : current - )) - } - return (
@@ -297,80 +181,25 @@ function InCommon({ )} {activeEventIds.length > 0 && showActive && -
- {activeEventIds.map((activeEventId) => -
- - -
- {createActiveTopButtons != null && - createActiveTopButtons(activeEventId)} - removeActiveEventId(activeEventId)} - /> -
-

- {inCommon[activeEventId].length}{' '} - collector{inCommon[activeEventId].length === 1 ? '' : 's'} - {' '}in common -

-
-
    - {inCommon[activeEventId].map((owner) => { - const inCommonEventIds = getAddressInCommonEventIds( - inCommon, - owner - ) - const inCommonAddresses = getAddressInCommonAddresses( - inCommon, - inCommonEventIds, - owner - ) - return ( -
  • { - onOwnerEnter(activeEventId, owner) - }} - onMouseLeave={() => { - onOwnerLeave(activeEventId, owner) - }} - > - -
  • - ) - })} -
-
- - {createActiveBottomButtons != null && - createActiveBottomButtons(activeEventId)} - -
-
- )} -
+ { + const buttons = [] + if (createActiveTopButtons) { + buttons.push(...createActiveTopButtons(eventId)) + } + buttons.push( + removeActiveEventId(eventId)} + /> + ) + return buttons + }} + createBottomButtons={createActiveBottomButtons} + /> }
)