From 7aedb50ca84b602d8343d6c69d6823125f6a34d1 Mon Sep 17 00:00:00 2001 From: sebasfavaron Date: Thu, 25 Nov 2021 16:55:00 -0300 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Add:=20disabling=20of=20csv=20b?= =?UTF-8?q?utton=20while=20data=20is=20incomplete=20(PCD-113)=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Add: disabling of csv button while data is incomplete + eliminated react warning (unique key on children) + search now starts with 3 or more chars (previously 2 or more) + ens names error (mainly caused by emojis) now don't cause all other ens names to fail * 🐛Fix: eliminate react dependency warnings on useEffect * 🐛Fix: issue where events with less than 1000 tokens failed to load + added option to download csv without ens data + fixed onhover style for csv download button * 🐛Fix: tooltip not showing on Download button without ens * ✨ Add: toast service and styled components * ✨ Add: nl2br that uses p tags instead of appending br tags * ✨ Add: toast notifications + made CSV_STATUS use easier to read + refactored duplicated JSX code in event.js render * 🐛Fix: unused import and unnecessary char escape * ✨ Add: constants + replaced ternary conditional with three simpler conditionals * ♻️ Refactor: event table mobile and desktop components joined +some inline styles replaced with css classes +functions simplified +status texts moved into constants +default error message moved into constant * ♻️ Refactor: simplified loading event checks +remove log +replace ternary condition with individual conditions * 🐛Fix: loading screen showing error message * ♻️ Refactor: stop using styled components * ♻️ Refactor: remove styled components --- package.json | 1 + src/App.js | 8 + src/components/activityTable.js | 6 +- src/components/eventCard.js | 4 +- src/pages/event.js | 631 ++++++++++++++--------------- src/pages/gallery.js | 21 +- src/scss/components/_poapapp.scss | 5 + src/scss/globals/_boilerplate.scss | 27 ++ src/scss/globals/_forms.scss | 12 + src/store/index.js | 34 +- src/store/mutations.js | 16 +- src/utilities/utilities.js | 7 + yarn.lock | 113 +++++- 13 files changed, 532 insertions(+), 353 deletions(-) diff --git a/package.json b/package.json index fe3fbac..4521da8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "react-csv": "^2.0.3", "react-dom": "^16.13.1", "react-helmet": "^6.1.0", + "react-hot-toast": "^2.1.1", "react-intersection-observer": "^8.28.5", "react-lazy-load-image-component": "^1.5.1", "react-lottie-player": "^1.3.1", diff --git a/src/App.js b/src/App.js index a91187e..ecac1bf 100644 --- a/src/App.js +++ b/src/App.js @@ -8,6 +8,7 @@ import Tokens from './pages/event' import Header from './components/header' import Footer from './components/footer' +import {Toaster} from "react-hot-toast"; function App() { @@ -16,6 +17,13 @@ function App() { return ( +
diff --git a/src/components/activityTable.js b/src/components/activityTable.js index 9cdc5b2..b50386b 100644 --- a/src/components/activityTable.js +++ b/src/components/activityTable.js @@ -64,8 +64,8 @@ export default function ActivityTable() { }, [daitransfers, mainnetTransfers]) return ( -
+
POAP Gallery
@@ -73,7 +73,7 @@ export default function ActivityTable() {
-
+
{' '}View more activity
diff --git a/src/components/eventCard.js b/src/components/eventCard.js index e72d6e3..03481c9 100644 --- a/src/components/eventCard.js +++ b/src/components/eventCard.js @@ -66,8 +66,8 @@ function Content({type, width, size, event, power}) { } }, [event, transferCount]) - const nl2br = (text) => (text.split('\n').map((item, key) => { - return <>{item}
+ const nl2br = (text) => (text.split('\n').map(item => { + return

{item}

})); return ( diff --git a/src/pages/event.js b/src/pages/event.js index 78e71e7..ca964e9 100644 --- a/src/pages/event.js +++ b/src/pages/event.js @@ -7,7 +7,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' import {faAngleLeft, faAngleRight, faArrowDown, faArrowUp, faDotCircle, faQuestionCircle} from '@fortawesome/free-solid-svg-icons' import {Helmet} from 'react-helmet' import {useDispatch, useSelector} from 'react-redux'; -import {fetchEventPageData} from '../store'; +import {FETCH_EVENT_PAGE_INFO_STATUS, fetchEventPageData} from '../store'; import {CSVLink} from "react-csv"; import {getEnsData} from '../store/mutations'; import Loader from '../components/loader' @@ -17,8 +17,15 @@ import { Foliage } from '../components/foliage'; import {dateCell, shrinkAddress, utcDateFormatted, utcDateFull} from '../utilities/utilities'; import { useWindowWidth } from '@react-hook/window-size/throttled'; import OpenLink from '../assets/images/openLink.svg' +import {toast} from "react-hot-toast"; const GRAPH_LIMIT = 1000; +const CSV_STATUS = { + DownloadingData: 'DownloadingData', + DownloadingLastDataChunk: 'DownloadingLastDataChunk', + ReadyWithoutEns: 'ReadyWithoutEns', + Ready: 'Ready', +} export default function Events() { let match = useRouteMatch(); @@ -29,7 +36,7 @@ export default function Events() { -

No event Selected

+

No event Selected

); @@ -45,151 +52,94 @@ export function Event() { const event = useSelector(state => state.events.event) const [pageIndex, setPageIndex] = useState(0); - const [data, setData] = useState([]); - const [mobileData, setMobileData] = useState([]); const [csv_data, setCsv_data] = useState([]); const [ensNames, setEnsNames] = useState([]); - const width = useWindowWidth(); + const [canDownloadCsv, setCanDownloadCsv] = useState(CSV_STATUS.DownloadingData); + const [tableIsLoading, setTableIsLoading] = useState(true) const pageCount = useMemo( () => event.tokenCount % 50 !== 0 ? Math.floor(event.tokenCount / 50) + 1 : event.tokenCount, [event]) const power = calculatePower(csv_data); + const csvDownloadIsOnLastStep = () => canDownloadCsv === CSV_STATUS.DownloadingLastDataChunk + const csvReadyOrAlmostReady = () => canDownloadCsv === CSV_STATUS.Ready || canDownloadCsv === CSV_STATUS.ReadyWithoutEns + const csvOnlyMissingEns = () => canDownloadCsv === CSV_STATUS.ReadyWithoutEns + const succeededLoadingEvent = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.SUCCEEDED + const isLoadingEvent = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.LOADING + const failedLoadingEvent = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.FAILED + const isIdle = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.IDLE + useEffect(() => { window.scrollTo(0, 0) }, []) - - const MobileRow = ({token, address}) => ( -
- POAP ID#{token.id} - Address - {shrinkAddress(address, 15)} - Claim Date{utcDateFormatted(token.created * 1000)} - Transaction Count{token.transferCount} - Power{token.owner.tokensOwned} -
- ) + useEffect(() => { + // Get new batch of tokens if (eventId) { dispatch(fetchEventPageData({ eventId, first: GRAPH_LIMIT, skip: GRAPH_LIMIT*pageIndex })) } }, [dispatch, eventId, pageIndex]) useEffect(() => { - let ownerIds = tokens.map(t => t.owner.id) - if (event && event.tokenCount > GRAPH_LIMIT && tokens && tokens.length > 0) { - const totalPages = Math.ceil(event.tokenCount / GRAPH_LIMIT); - if (pageIndex + 1 < totalPages) { - setPageIndex(pageIndex + 1); + // Call next batch of tokens (if there is more), then load the new tokens data + const totalPages = Math.ceil(event.tokenCount / GRAPH_LIMIT); + const hasMorePages = pageIndex < totalPages + const hasTokens = tokens && tokens.length > 0 + if (event && hasTokens && hasMorePages) { + if (pageIndex + 1 === totalPages) { + setCanDownloadCsv(CSV_STATUS.DownloadingLastDataChunk) } + setPageIndex(pageIndex + 1); } - let _data = [], _mobileData = [] let _csv_data = [] _csv_data.push(['ID', 'Collection', 'ENS', 'Minting Date', 'Tx Count', 'Power']); for (let i = 0; i < tokens.length; i++) { - _data.push({ - col1: () , - col2: (), - col3: tokens[i].created * 1000, - col4: tokens[i].transferCount, - col5: tokens[i].owner.tokensOwned, - }) - _mobileData.push({ - col1: - }) _csv_data.push([tokens[i].id, tokens[i].owner.id, null, utcDateFull(tokens[i].created * 1000), tokens[i].transferCount, tokens[i].owner.tokensOwned]) } - setData(_data) - setMobileData(_mobileData) setCsv_data(_csv_data) - getEnsData(ownerIds).then(allnames => { - if(allnames.length > 0){ - setEnsNames(allnames) - } - }) }, [event, tokens, pageIndex, setPageIndex]); /* eslint-disable-line react-hooks/exhaustive-deps */ useEffect(() => { + // Merge ens data if(ensNames.length > 0){ // TODO: probably there is a better way to merge - let _data = _.cloneDeep(width > 480 ? data : mobileData); let _csv_data = _.cloneDeep(csv_data); for (let i = 0; i < tokens.length; i++) { let validName = ensNames[i] if (validName) { - if (data[i]) { - if (width > 480) { - _data[i].col2 = ( {validName}) - } else { - _data[i].col1 = - } + if (_csv_data[i+1]) { //TODO: test _csv_data[i+1][2] = validName // i+1 is there to compensate for the first array which is just the csv titles } } } - if (width > 480) { - setData(_data) - } else { - setMobileData(_data) - } setCsv_data(_csv_data) } }, [ensNames]) /* eslint-disable-line react-hooks/exhaustive-deps */ - const columns = useMemo( - () => [ - { - Header: () => (<>POAP ID ), - accessor: 'col1', // accessor is the "key" in the data - }, - { - Header: 'Collection', - accessor: 'col2', - }, - { - Header: 'Minting Date', - accessor: 'col3', - }, - { - Header: (<>TX Count ), - accessor: 'col4', - }, - { - Header: () => (<>Power ), - accessor: 'col5', - }, - ], - [] - ) - - const mobileColumns = useMemo( - () => [ - { - Header: '', - accessor: 'col1', // accessor is the "key" in the data + const validationCSVDownload = () => { + setCanDownloadCsv(CSV_STATUS.ReadyWithoutEns) + let ownerIds = tokens.map(t => t.owner.id) + getEnsData(ownerIds).then(allnames => { + if(allnames.length > 0){ + setEnsNames(allnames) } - ], - [] - ) - - if (loadingEvent === 'loading' || loadingEvent === 'idle') { - return ( -
- - POAP Gallery - Event - - - - - -
- -
-
- ) + setCanDownloadCsv(CSV_STATUS.Ready) + }).catch(() => { + toast.error(`Could not get ENS data (You can download CSV without ENS resolution or try again later)`, { + duration: 10000 + }) + }) } - if (errorEvent || Object.keys(event).length === 0) { - return ( + useEffect(() => { + if (succeededLoadingEvent() && csvDownloadIsOnLastStep()) { + validationCSVDownload() + } + setTableIsLoading(!succeededLoadingEvent()) + }, [loadingEvent]) /* eslint-disable-line react-hooks/exhaustive-deps */ + + const defaultEventErrorMessage = 'Token not found' + + return (
POAP Gallery - Event @@ -198,77 +148,67 @@ export function Event() { -
-

{errorEvent || 'Token not found'}

-
- warning sign + {!errorEvent && (isLoadingEvent() || isIdle()) && +
+
-
-
- ) - } - - return ( -
- - POAP Gallery - Event - - - - - -
-
-
-
- {' Prev'} -

EVENT ID
#{eventId}

- {'Next '} -
-
- + } + {(errorEvent || Object.keys(event).length === 0) && !isLoadingEvent() && !isIdle() && +
+

{errorEvent || defaultEventErrorMessage}

+
+ warning sign
-
-
-
Collections ({tokens.length})
- 0 && +
+
- Download CSV - -
-
- { - width > 480 - ? - : - } -
-
-
- ); + flexWrap: 'wrap', + alignContent: 'space-around', + justifyContent: 'space-around', + marginBottom: 82, + }}> +
+
+ {' Prev'} +

EVENT ID
#{eventId}

+ {'Next '} +
+
+ +
+
+
+
+
Collections ({tokens.length})
+ {csvReadyOrAlmostReady() ? + + {`Download CSV${csvOnlyMissingEns() ? ' (without ens)' : ''}`} + + : + + } +
+
+ +
+
+ } + + ) } function ExternalLinkCell({url, tooltipText = null, content}) { @@ -312,181 +252,230 @@ function ExternalLinkCell({url, tooltipText = null, content}) { ); } -function CreateTable({loading, pageCount: pc, columns, data, event}) { - const [length, setLength] = useState(20); - const { - getTableProps, - getTableBodyProps, - headerGroups, - prepareRow, - page, - setPageSize, - } = useTable({ columns, data, pageCount: pc, - initialState: { pageSize: length, sortBy: [ +function TableContainer({tokens, ensNames, pageCount: pc, loading}) { + const [data, setData] = useState([]); + const [mobileData, setMobileData] = useState([]); + + const MobileRow = ({token, address}) => ( +
+ POAP ID#{token.id} + Address + {shrinkAddress(address, 15)} + Claim Date{utcDateFormatted(token.created * 1000)} + Transaction Count{token.transferCount} + Power{token.owner.tokensOwned} +
+ ) + + const columns = useMemo( + () => [ { - id: 'col3', - desc: true + Header: () => (<>POAP ID ), + accessor: 'col1', // accessor is the "key" in the data + }, + { + Header: 'Collection', + accessor: 'col2', + }, + { + Header: 'Minting Date', + accessor: 'col3', + }, + { + Header: (<>TX Count ), + accessor: 'col4', + }, + { + Header: () => (<>Power ), + accessor: 'col5', + }, + ], + [] + ) + + const mobileColumns = useMemo( + () => [ + { + Header: '', + accessor: 'col1', // accessor is the "key" in the data } - ] } }, useSortBy, usePagination) + ], + [] + ) + + useEffect(() => { + let _data = [], _mobileData = [] + for (let i = 0; i < tokens.length; i++) { + _data.push({ + col1: () , + col2: (), + col3: tokens[i].created * 1000, + col4: tokens[i].transferCount, + col5: tokens[i].owner.tokensOwned, + }) + _mobileData.push({ + col1: + }) + } + setData(_data) + setMobileData(_mobileData) + }, [tokens]); /* eslint-disable-line react-hooks/exhaustive-deps */ + + useEffect(() => { + // Merge ens data + if(ensNames.length > 0){ + // TODO: probably there is a better way to merge + let _data = _.cloneDeep(data); + let _mobileData = _.cloneDeep(mobileData); + for (let i = 0; i < tokens.length; i++) { + let validName = ensNames[i] + if (validName) { + if (data[i]) { + _data[i].col2 = ( {validName}) + _mobileData[i].col1 = + } + } + } + setData(_data) + setMobileData(_mobileData) + } + }, [ensNames]) /* eslint-disable-line react-hooks/exhaustive-deps */ const [dateFormat, setDateFormat] = useState('timeago') const toggleDateFormat = () => { dateFormat === 'timeago' ? setDateFormat('date') : setDateFormat('timeago') } - return ( -
- - - {// Loop over the header rows - headerGroups.map((headerGroup, i) => ( - // Apply the header row props - - {// Loop over the headers in each row - headerGroup.headers.map((column, idx) => ( - // Apply the header cell props - (idx === 0 || idx === 3 || idx === 4) - ? - : - idx === 2 - ? - : - ))} - - ))} - - {/* Apply the table body props */} - - {page.map((row, i) => { - prepareRow(row) - return ( - - {row.cells.map((cell, idx) => { - return ( - idx === 2 - ? - : - )})} - - ) - })} - - {loading ? ( - // Use our custom loading state to show a loading indicator - - ) : ( - - )} - - -
- {// Render the header - column.render('Header')}{' '} - {column.isSorted - ? column.isSortedDesc - ? - : - : ''} - - {// Render the header - column.render('Header')}{' '} - - - {// Render the header - column.render('Header')} -
{dateCell(cell.value, dateFormat)}{cell.render('Cell')}
Loading... - - {/* Showing {page.length} of {page.length}{' '} - results */} -
-
- { - if (inView) { - setLength(length + 20) - setPageSize(length + 20) - } - }} - > - {({ inView, ref, entry }) =>
} -
-
-
- ) -} + const width = useWindowWidth() + const [isMobile, setIsMobile] = useState(false) + + useEffect(() => { + const _isMobile = width <= 480 + if (_isMobile !== isMobile) { + setIsMobile(_isMobile) + } + }, [width]) /* eslint-disable-line react-hooks/exhaustive-deps */ -function CreateMobileTable({loading, pageCount: pc, columns, data, event}) { const [length, setLength] = useState(20); const { - getTableProps, - getTableBodyProps, - headerGroups, - prepareRow, - page, - setPageSize - } = useTable({ columns, data, pageCount: pc, initialState: { pageSize: length } }, useSortBy, usePagination ) + getTableProps: getDesktopTableProps, + getTableBodyProps: getDesktopTableBodyProps, + headerGroups: desktopHeaderGroups, + prepareRow: desktopPrepareRow, + page: desktopPage, + setPageSize: setDesktopPageSize, + } = useTable({ columns, data, pageCount: pc, + initialState: { pageSize: length, sortBy: [ + { + id: 'col3', + desc: true + } + ] } }, useSortBy, usePagination) + + const { + getTableProps: getMobileTableProps, + getTableBodyProps: getMobileTableBodyProps, + headerGroups: mobileHeaderGroups, + prepareRow: mobilePrepareRow, + page: mobilePage, + setPageSize: setMobilePageSize, + } = useTable({ columns: mobileColumns, data: mobileData, pageCount: pc, initialState: { pageSize: length } }, useSortBy, usePagination ) return ( -
- - - {// Loop over the header rows - headerGroups.map((headerGroup, i) => ( - // Apply the header row props - - {// Loop over the headers in each row - headerGroup.headers.map((column, idx) => ( - // Apply the header cell props - +
+
- {// Render the header - column.render('Header')} -
+ + {// Loop over the header rows + (isMobile ? mobileHeaderGroups : desktopHeaderGroups).map((headerGroup, i) => ( + // Apply the header row props + + {// Loop over the headers in each row + headerGroup.headers.map((column, idx) => { + // Apply the header cell props + if (isMobile) { + return + } else { + switch (idx) { + case 0: + case 3: + case 4: + return + case 2: + return + default: + return + } + } + })} + ))} - - ))} - - {/* Apply the table body props */} - - {page.map((row, i) => { - prepareRow(row) - return ( - + + {/* Apply the table body props */} + + {(isMobile ? mobilePage : desktopPage).map((row, i) => { + (isMobile ? mobilePrepareRow : desktopPrepareRow)(row) + if (isMobile) { + return {row.cells.map((cell, idx) => { return ( - - )})} + + )})} - ) + } else { + return ( + + {row.cells.map((cell, idx) => { + return ( + idx === 2 + ? + : + )})} + + ) + } })} {loading ? ( - // Use our custom loading state to show a loading indicator - + // Use our custom loading state to show a loading indicator + ) : ( - + - -
+ {// Render the header + column.render('Header')} + + {// Render the header + column.render('Header')}{' '} + {column.isSorted + ? column.isSortedDesc + ? + : + : ''} + + {// Render the header + column.render('Header')}{' '} + + + {// Render the header + column.render('Header')} +
{cell.render('Cell')}{cell.render('Cell')}
{dateCell(cell.value, dateFormat)}{cell.render('Cell')}
Loading...Loading... - - {/* Showing {page.length} of {page.length}{' '} - results */} - )}
-
- { - if (inView) { - setLength(length + 20) - setPageSize(length + 20) - } - }} - > - {({ inView, ref, entry }) =>
} -
-
-
+ + +
+ { + if (inView) { + (isMobile ? setMobilePageSize : setDesktopPageSize)(length + 20) + setLength(length + 20) + } + }} + > + {({ inView, ref, entry }) =>
} + +
+
) } diff --git a/src/pages/gallery.js b/src/pages/gallery.js index 4e0c8dc..be4025f 100644 --- a/src/pages/gallery.js +++ b/src/pages/gallery.js @@ -1,7 +1,12 @@ import React, {useCallback, useEffect, useState} from 'react'; import ActivityTable from '../components/activityTable' import {Helmet} from 'react-helmet'; -import {fetchIndexData, selectEventError, selectEventStatus, selectRecentEvents} from '../store'; +import { + FETCH_INDEX_PAGE_INFO_STATUS, + fetchIndexData, + selectIndexFetchStatus, + selectRecentEvents +} from '../store'; import {useDispatch, useSelector} from 'react-redux'; import {EventCard} from "../components/eventCard"; import Loader from '../components/loader' @@ -22,9 +27,7 @@ export default function Gallery() { }, []); /* eslint-disable-line react-hooks/exhaustive-deps */ const events = useSelector(selectRecentEvents) - const eventStatus = useSelector(selectEventStatus) - const eventError = useSelector(selectEventError) - + const indexFetchStatus = useSelector(selectIndexFetchStatus) const [items, setItems] = useState(events) const [search, setSearch] = useState(undefined); @@ -50,7 +53,7 @@ export default function Gallery() { debounceHandleSearch(value, items) }; const handleNewSearchValue = (value, items) => { - if (value && value.length > 1) { + if (value && value.length > 2) { const filteredItems = items.filter((item) => { return item.name.toLowerCase().indexOf(value.toLowerCase()) !== -1; }); @@ -254,7 +257,7 @@ export default function Gallery() {
- {eventError ? ( + {indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.FAILED && (
Could not load gallery, check your connection and try again
- ) : eventStatus === 'succeeded' ? ( + )} + {indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.SUCCEEDED && ( (search?.length === 0) ?
Failed search

No results for that search :(

: - ) : ( - )} + {(indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.IDLE || indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.LOADING) && }
diff --git a/src/scss/components/_poapapp.scss b/src/scss/components/_poapapp.scss index b2f88c2..84ccf53 100755 --- a/src/scss/components/_poapapp.scss +++ b/src/scss/components/_poapapp.scss @@ -1041,3 +1041,8 @@ table { width: 12px; } } + +div.center { + display: flex; + justify-content: center; +} \ No newline at end of file diff --git a/src/scss/globals/_boilerplate.scss b/src/scss/globals/_boilerplate.scss index b15a773..cabea7a 100755 --- a/src/scss/globals/_boilerplate.scss +++ b/src/scss/globals/_boilerplate.scss @@ -244,3 +244,30 @@ ol { .tdColumnImage{ height: 26px; } + +.button-disabled { + cursor: default; + background-color: lightgray; + &:hover, &:focus-visible, &:focus { + background-color: lightgray; + } + box-shadow: none!important; +} + +.token-not-found { + display: flex; + justify-content: center; + flex-direction: column; + margin: 0 auto; + text-align: center; +} + +.discreet-paragraph { + margin: 0; + color: $grayscale5; + word-break: break-word; +} + +.no-margin { + margin: 0; +} \ No newline at end of file diff --git a/src/scss/globals/_forms.scss b/src/scss/globals/_forms.scss index d1cb536..a22fc75 100755 --- a/src/scss/globals/_forms.scss +++ b/src/scss/globals/_forms.scss @@ -547,3 +547,15 @@ textarea { } } } + +.csv-button { + width: fit-content; + min-width: auto; + margin-bottom: 0; + margin-left: auto; + padding: 12px 32px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 6px 18px 0 #6534FF4D; +} \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index fae20c7..c92b8c6 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,6 +1,19 @@ import { createSlice, combineReducers, configureStore, createAsyncThunk, current } from '@reduxjs/toolkit'; import { getIndexPageData, getEventPageData } from './mutations'; +export const FETCH_INFO_STATUS = { + IDLE: 'IDLE', + LOADING: 'LOADING', + SUCCEEDED: 'SUCCEEDED', + FAILED: 'FAILED' +} +export const FETCH_EVENT_PAGE_INFO_STATUS = { + ...FETCH_INFO_STATUS +} +export const FETCH_INDEX_PAGE_INFO_STATUS = { + ...FETCH_INFO_STATUS +} + const initialEventsState = { events: [], event: {}, @@ -8,9 +21,8 @@ const initialEventsState = { mostClaimed: undefined, upcoming: undefined, mostRecent: undefined, - status: 'idle', - error: null, - eventStatus: 'idle', + status: FETCH_INDEX_PAGE_INFO_STATUS.IDLE, + eventStatus: FETCH_EVENT_PAGE_INFO_STATUS.IDLE, eventError: null, tokens: [], tokenId: null @@ -26,7 +38,7 @@ const eventsSlice = createSlice({ reducers: {}, extraReducers: { [fetchIndexData.pending]: (state, action) => { - state.status = 'loading' + state.status = FETCH_INDEX_PAGE_INFO_STATUS.LOADING }, [fetchIndexData.fulfilled]: (state, action) => { const { poapEvents, mostRecent, mostClaimed, upcoming } = action.payload @@ -34,15 +46,15 @@ const eventsSlice = createSlice({ state.mostRecent = mostRecent state.mostClaimed = mostClaimed state.upcoming = upcoming - state.status = 'succeeded' + state.status = FETCH_INDEX_PAGE_INFO_STATUS.SUCCEEDED }, [fetchIndexData.rejected]: (state, action) => { state.eventError = action.error.message - state.status = 'failed' + state.status = FETCH_INDEX_PAGE_INFO_STATUS.FAILED console.warn(action.error) }, [fetchEventPageData.pending]: (state, action) => { - state.eventStatus = 'loading' + state.eventStatus = FETCH_EVENT_PAGE_INFO_STATUS.LOADING }, [fetchEventPageData.fulfilled]: (state, action) => { if (state.tokenId === action.payload.id) { @@ -52,19 +64,17 @@ const eventsSlice = createSlice({ } state.tokenId = action.payload.id - state.eventStatus = 'succeeded' state.event = action.payload.event + state.eventStatus = FETCH_EVENT_PAGE_INFO_STATUS.SUCCEEDED }, [fetchEventPageData.rejected]: (state, action) => { state.eventError = action.error.message - state.eventStatus = 'failed' - console.warn(action.error) + state.eventStatus = FETCH_EVENT_PAGE_INFO_STATUS.FAILED } } }) -export const selectEventStatus = state => state.events.status -export const selectEventError = state => state.events.error +export const selectIndexFetchStatus = state => state.events.status export const selectRecentEvents = state => state.events.events.filter(event => { // don't show future events diff --git a/src/store/mutations.js b/src/store/mutations.js index a11edab..5a3d638 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -12,6 +12,8 @@ import {ensABI} from './abis'; import _, {uniqBy} from 'lodash' import {ethers} from 'ethers'; import namehash from 'eth-ens-namehash'; +import {toast} from "react-hot-toast"; +import {toastInfoOptions} from "../utilities/utilities"; const {REACT_APP_RPC_PROVIDER_URL, REACT_APP_ENS_CONTRACT} = process.env; const provider = new ethers.providers.StaticJsonRpcProvider(REACT_APP_RPC_PROVIDER_URL); @@ -29,11 +31,19 @@ export async function getEnsData(ownerIds){ names = await ReverseRecords.getNames(chunk) }catch(e){ // Fallback to null if problem fetching Reverse record - console.log(e) names = chunk.map(a => null) } - const validNames = names.map(name => (namehash.normalize(name) === name && name !== '') && name ) - allnames = _.concat(allnames, validNames); + const validNames = names.map(name => { + try { + return (namehash.normalize(name) === name && name !== '') && name + } catch (e) { + if (name && name.length) { + toast.error(`Couldn't parse ENS name '${name}'.`, toastInfoOptions) + } + return false + } + } ) + allnames = _.concat(allnames, validNames); } return allnames } diff --git a/src/utilities/utilities.js b/src/utilities/utilities.js index 2e14a6e..89c2c2a 100644 --- a/src/utilities/utilities.js +++ b/src/utilities/utilities.js @@ -52,3 +52,10 @@ export const dateCell = (cell, dateFormat) => { } return utcDateFromNow(cell) } + +export const toastInfoOptions = { + icon: '', + style: { + backgroundColor: '#fff8e0' + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index e110853..378011a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1491,6 +1491,11 @@ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz#01dd3d054da07a00b764d78748df20daf2b317e9" integrity sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw== +"@fortawesome/fontawesome-common-types@^0.2.36": + version "0.2.36" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz#b44e52db3b6b20523e0c57ef8c42d315532cb903" + integrity sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg== + "@fortawesome/fontawesome-svg-core@^1.2.30": version "1.2.35" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz#85aea8c25645fcec88d35f2eb1045c38d3e65cff" @@ -1498,6 +1503,13 @@ dependencies: "@fortawesome/fontawesome-common-types" "^0.2.35" +"@fortawesome/free-regular-svg-icons@^5.15.3": + version "5.15.4" + resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz#b97edab436954333bbeac09cfc40c6a951081a02" + integrity sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw== + dependencies: + "@fortawesome/fontawesome-common-types" "^0.2.36" + "@fortawesome/free-solid-svg-icons@^5.14.0": version "5.15.3" resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz#52eebe354f60dc77e0bde934ffc5c75ffd04f9d8" @@ -1726,6 +1738,39 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@react-hook/debounce@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@react-hook/debounce/-/debounce-3.0.0.tgz#9eea8b5d81d4cb67cd72dd8657b3ff724afc7cad" + integrity sha512-ir/kPrSfAzY12Gre0sOHkZ2rkEmM4fS5M5zFxCi4BnCeXh2nvx9Ujd+U4IGpKCuPA+EQD0pg1eK2NGLvfWejag== + dependencies: + "@react-hook/latest" "^1.0.2" + +"@react-hook/event@^1.2.1": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@react-hook/event/-/event-1.2.6.tgz#52f91578add934acc1203328ca09ab14fc7ee58e" + integrity sha512-JUL5IluaOdn5w5Afpe/puPa1rj8X6udMlQ9dt4hvMuKmTrBS1Ya6sb4sVgvfe2eU4yDuOfAhik8xhbcCekbg9Q== + +"@react-hook/latest@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@react-hook/latest/-/latest-1.0.3.tgz#c2d1d0b0af8b69ec6e2b3a2412ba0768ac82db80" + integrity sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg== + +"@react-hook/throttle@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@react-hook/throttle/-/throttle-2.2.0.tgz#d0402714a06e1ba0bc1da1fdf5c3c5cd0e08d45a" + integrity sha512-LJ5eg+yMV8lXtqK3lR+OtOZ2WH/EfWvuiEEu0M3bhR7dZRfTyEJKxH1oK9uyBxiXPtWXiQggWbZirMCXam51tg== + dependencies: + "@react-hook/latest" "^1.0.2" + +"@react-hook/window-size@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@react-hook/window-size/-/window-size-3.0.7.tgz#00d176e7a8eb55814e161eae34aae20afbcbe35d" + integrity sha512-bK5ed/jN+cxy0s1jt2CelCnUt7jZRseUvPQ22ZJkUl/QDOsD+7CA/6wcqC3c0QweM/fPBRP6uI56TJ48SnlVww== + dependencies: + "@react-hook/debounce" "^3.0.0" + "@react-hook/event" "^1.2.1" + "@react-hook/throttle" "^2.2.0" + "@reduxjs/toolkit@^1.4.0": version "1.5.1" resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.5.1.tgz#05daa2f6eebc70dc18cd98a90421fab7fa565dc5" @@ -2933,6 +2978,13 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -4266,6 +4318,11 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -5089,7 +5146,7 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -5637,6 +5694,11 @@ globule@^1.0.0: lodash "~4.17.10" minimatch "~3.0.2" +goober@^2.0.35: + version "2.0.41" + resolved "https://registry.yarnpkg.com/goober/-/goober-2.0.41.tgz#0a3d786ff9917bcf2a096eef703bf717838cbec9" + integrity sha512-kwjegMT5018zWydhOQlQneCgCtrKJaPsru7TaBWmTYV0nsMeUrM6L6O8JmNYb9UbPMgWcmtf+9p4Y3oJabIH1A== + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" @@ -7377,6 +7439,11 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + lodash.debounce@^4.0.6, lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -7407,7 +7474,7 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash.throttle@^4.0.1: +lodash.throttle@^4.0.1, lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= @@ -7434,6 +7501,11 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3 dependencies: js-tokens "^3.0.0 || ^4.0.0" +lottie-web@^5.7.6: + version "5.8.1" + resolved "https://registry.yarnpkg.com/lottie-web/-/lottie-web-5.8.1.tgz#807e0af0ad22b59bf867d964eb684cb3368da0ef" + integrity sha512-9gIizWADlaHC2GCt+D+yNpk5l2clZQFqnVWWIVdY0LnxC/uLa39dYltAe3fcmC/hrZ2IUQ8dLlY0O934Npjs7Q== + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -7776,6 +7848,17 @@ mixin-object@^2.0.1: dependencies: minimist "^1.2.5" +morgan@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.0.2" + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -7964,7 +8047,7 @@ node-releases@^1.1.52, node-releases@^1.1.70: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== -node-sass@^4.14.1: +node-sass@^4.0.0: version "4.14.1" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.1.tgz#99c87ec2efb7047ed638fb4c9db7f3a42e2217b5" integrity sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g== @@ -9658,6 +9741,13 @@ react-helmet@^6.1.0: react-fast-compare "^3.1.1" react-side-effect "^2.1.0" +react-hot-toast@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.1.1.tgz#56409ab406b534e9e58274cf98d80355ba0fdda0" + integrity sha512-Odrp4wue0fHh0pOfZt5H+9nWCMtqs3wdlFSzZPp7qsxfzmbE26QmGWIh6hG43CukiPeOjA8WQhBJU8JwtWvWbQ== + dependencies: + goober "^2.0.35" + react-intersection-observer@^8.28.5: version "8.31.0" resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-8.31.0.tgz#0ed21aaf93c4c0475b22b0ccaba6169076d01605" @@ -9673,11 +9763,28 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-lazy-load-image-component@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/react-lazy-load-image-component/-/react-lazy-load-image-component-1.5.1.tgz#59cc92326ba5604e9c4a86805a2cf1667fafbc71" + integrity sha512-grTEZzURLHPkq7JoipcBBQU44ijdF4fH3Cb+eSD5eSAaMsjugbXqTaVWm5ruPUNLduoNR9KKQF6bOR9h2WphEg== + dependencies: + lodash.debounce "^4.0.8" + lodash.throttle "^4.1.1" + react-lifecycles-compat@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-lottie-player@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/react-lottie-player/-/react-lottie-player-1.4.1.tgz#ddd448486b8e8a42f4ae149751c38a8539f80b77" + integrity sha512-8dw3Dt8h6iTIrA+Iz7N/rOZjY1ShEwoxsgFywW2hZPFf58mZL75NFFNXY6v95NNj/25kZQpMV/bDkNoxj4Sang== + dependencies: + fast-deep-equal "^3.1.3" + lodash.clonedeep "^4.5.0" + lottie-web "^5.7.6" + react-modal@^3.11.2: version "3.12.1" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.12.1.tgz#38c33f70d81c33d02ff1ed115530443a3dc2afd3" From 5ede46facc5a48b7d09627ec3bbbf7e0fd5aead3 Mon Sep 17 00:00:00 2001 From: sebasfavaron Date: Thu, 25 Nov 2021 18:02:48 -0300 Subject: [PATCH 2/3] =?UTF-8?q?Revert=20"=E2=9C=A8=20Add:=20disabling=20of?= =?UTF-8?q?=20csv=20button=20while=20data=20is=20incomplete=20(PCD-113)=20?= =?UTF-8?q?(#27)"=20(#36)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 7aedb50ca84b602d8343d6c69d6823125f6a34d1. --- package.json | 1 - src/App.js | 8 - src/components/activityTable.js | 6 +- src/components/eventCard.js | 4 +- src/pages/event.js | 631 +++++++++++++++-------------- src/pages/gallery.js | 21 +- src/scss/components/_poapapp.scss | 5 - src/scss/globals/_boilerplate.scss | 27 -- src/scss/globals/_forms.scss | 12 - src/store/index.js | 34 +- src/store/mutations.js | 16 +- src/utilities/utilities.js | 7 - yarn.lock | 113 +----- 13 files changed, 353 insertions(+), 532 deletions(-) diff --git a/package.json b/package.json index 4521da8..fe3fbac 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "react-csv": "^2.0.3", "react-dom": "^16.13.1", "react-helmet": "^6.1.0", - "react-hot-toast": "^2.1.1", "react-intersection-observer": "^8.28.5", "react-lazy-load-image-component": "^1.5.1", "react-lottie-player": "^1.3.1", diff --git a/src/App.js b/src/App.js index ecac1bf..a91187e 100644 --- a/src/App.js +++ b/src/App.js @@ -8,7 +8,6 @@ import Tokens from './pages/event' import Header from './components/header' import Footer from './components/footer' -import {Toaster} from "react-hot-toast"; function App() { @@ -17,13 +16,6 @@ function App() { return ( -
diff --git a/src/components/activityTable.js b/src/components/activityTable.js index b50386b..9cdc5b2 100644 --- a/src/components/activityTable.js +++ b/src/components/activityTable.js @@ -64,8 +64,8 @@ export default function ActivityTable() { }, [daitransfers, mainnetTransfers]) return ( -
+
POAP Gallery
@@ -73,7 +73,7 @@ export default function ActivityTable() {
-
+
{' '}View more activity
diff --git a/src/components/eventCard.js b/src/components/eventCard.js index 03481c9..e72d6e3 100644 --- a/src/components/eventCard.js +++ b/src/components/eventCard.js @@ -66,8 +66,8 @@ function Content({type, width, size, event, power}) { } }, [event, transferCount]) - const nl2br = (text) => (text.split('\n').map(item => { - return

{item}

+ const nl2br = (text) => (text.split('\n').map((item, key) => { + return <>{item}
})); return ( diff --git a/src/pages/event.js b/src/pages/event.js index ca964e9..78e71e7 100644 --- a/src/pages/event.js +++ b/src/pages/event.js @@ -7,7 +7,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' import {faAngleLeft, faAngleRight, faArrowDown, faArrowUp, faDotCircle, faQuestionCircle} from '@fortawesome/free-solid-svg-icons' import {Helmet} from 'react-helmet' import {useDispatch, useSelector} from 'react-redux'; -import {FETCH_EVENT_PAGE_INFO_STATUS, fetchEventPageData} from '../store'; +import {fetchEventPageData} from '../store'; import {CSVLink} from "react-csv"; import {getEnsData} from '../store/mutations'; import Loader from '../components/loader' @@ -17,15 +17,8 @@ import { Foliage } from '../components/foliage'; import {dateCell, shrinkAddress, utcDateFormatted, utcDateFull} from '../utilities/utilities'; import { useWindowWidth } from '@react-hook/window-size/throttled'; import OpenLink from '../assets/images/openLink.svg' -import {toast} from "react-hot-toast"; const GRAPH_LIMIT = 1000; -const CSV_STATUS = { - DownloadingData: 'DownloadingData', - DownloadingLastDataChunk: 'DownloadingLastDataChunk', - ReadyWithoutEns: 'ReadyWithoutEns', - Ready: 'Ready', -} export default function Events() { let match = useRouteMatch(); @@ -36,7 +29,7 @@ export default function Events() { -

No event Selected

+

No event Selected

); @@ -52,94 +45,151 @@ export function Event() { const event = useSelector(state => state.events.event) const [pageIndex, setPageIndex] = useState(0); + const [data, setData] = useState([]); + const [mobileData, setMobileData] = useState([]); const [csv_data, setCsv_data] = useState([]); const [ensNames, setEnsNames] = useState([]); - const [canDownloadCsv, setCanDownloadCsv] = useState(CSV_STATUS.DownloadingData); - const [tableIsLoading, setTableIsLoading] = useState(true) + const width = useWindowWidth(); const pageCount = useMemo( () => event.tokenCount % 50 !== 0 ? Math.floor(event.tokenCount / 50) + 1 : event.tokenCount, [event]) const power = calculatePower(csv_data); - const csvDownloadIsOnLastStep = () => canDownloadCsv === CSV_STATUS.DownloadingLastDataChunk - const csvReadyOrAlmostReady = () => canDownloadCsv === CSV_STATUS.Ready || canDownloadCsv === CSV_STATUS.ReadyWithoutEns - const csvOnlyMissingEns = () => canDownloadCsv === CSV_STATUS.ReadyWithoutEns - const succeededLoadingEvent = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.SUCCEEDED - const isLoadingEvent = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.LOADING - const failedLoadingEvent = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.FAILED - const isIdle = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.IDLE - useEffect(() => { window.scrollTo(0, 0) }, []) - + + const MobileRow = ({token, address}) => ( +
+ POAP ID#{token.id} + Address + {shrinkAddress(address, 15)} + Claim Date{utcDateFormatted(token.created * 1000)} + Transaction Count{token.transferCount} + Power{token.owner.tokensOwned} +
+ ) useEffect(() => { - // Get new batch of tokens if (eventId) { dispatch(fetchEventPageData({ eventId, first: GRAPH_LIMIT, skip: GRAPH_LIMIT*pageIndex })) } }, [dispatch, eventId, pageIndex]) useEffect(() => { - // Call next batch of tokens (if there is more), then load the new tokens data - const totalPages = Math.ceil(event.tokenCount / GRAPH_LIMIT); - const hasMorePages = pageIndex < totalPages - const hasTokens = tokens && tokens.length > 0 - if (event && hasTokens && hasMorePages) { - if (pageIndex + 1 === totalPages) { - setCanDownloadCsv(CSV_STATUS.DownloadingLastDataChunk) + let ownerIds = tokens.map(t => t.owner.id) + if (event && event.tokenCount > GRAPH_LIMIT && tokens && tokens.length > 0) { + const totalPages = Math.ceil(event.tokenCount / GRAPH_LIMIT); + if (pageIndex + 1 < totalPages) { + setPageIndex(pageIndex + 1); } - setPageIndex(pageIndex + 1); } + let _data = [], _mobileData = [] let _csv_data = [] _csv_data.push(['ID', 'Collection', 'ENS', 'Minting Date', 'Tx Count', 'Power']); for (let i = 0; i < tokens.length; i++) { + _data.push({ + col1: () , + col2: (), + col3: tokens[i].created * 1000, + col4: tokens[i].transferCount, + col5: tokens[i].owner.tokensOwned, + }) + _mobileData.push({ + col1: + }) _csv_data.push([tokens[i].id, tokens[i].owner.id, null, utcDateFull(tokens[i].created * 1000), tokens[i].transferCount, tokens[i].owner.tokensOwned]) } + setData(_data) + setMobileData(_mobileData) setCsv_data(_csv_data) + getEnsData(ownerIds).then(allnames => { + if(allnames.length > 0){ + setEnsNames(allnames) + } + }) }, [event, tokens, pageIndex, setPageIndex]); /* eslint-disable-line react-hooks/exhaustive-deps */ useEffect(() => { - // Merge ens data if(ensNames.length > 0){ // TODO: probably there is a better way to merge + let _data = _.cloneDeep(width > 480 ? data : mobileData); let _csv_data = _.cloneDeep(csv_data); for (let i = 0; i < tokens.length; i++) { let validName = ensNames[i] if (validName) { - if (_csv_data[i+1]) { //TODO: test + if (data[i]) { + if (width > 480) { + _data[i].col2 = ( {validName}) + } else { + _data[i].col1 = + } _csv_data[i+1][2] = validName // i+1 is there to compensate for the first array which is just the csv titles } } } + if (width > 480) { + setData(_data) + } else { + setMobileData(_data) + } setCsv_data(_csv_data) } }, [ensNames]) /* eslint-disable-line react-hooks/exhaustive-deps */ - const validationCSVDownload = () => { - setCanDownloadCsv(CSV_STATUS.ReadyWithoutEns) - let ownerIds = tokens.map(t => t.owner.id) - getEnsData(ownerIds).then(allnames => { - if(allnames.length > 0){ - setEnsNames(allnames) - } - setCanDownloadCsv(CSV_STATUS.Ready) - }).catch(() => { - toast.error(`Could not get ENS data (You can download CSV without ENS resolution or try again later)`, { - duration: 10000 - }) - }) - } + const columns = useMemo( + () => [ + { + Header: () => (<>POAP ID ), + accessor: 'col1', // accessor is the "key" in the data + }, + { + Header: 'Collection', + accessor: 'col2', + }, + { + Header: 'Minting Date', + accessor: 'col3', + }, + { + Header: (<>TX Count ), + accessor: 'col4', + }, + { + Header: () => (<>Power ), + accessor: 'col5', + }, + ], + [] + ) - useEffect(() => { - if (succeededLoadingEvent() && csvDownloadIsOnLastStep()) { - validationCSVDownload() - } - setTableIsLoading(!succeededLoadingEvent()) - }, [loadingEvent]) /* eslint-disable-line react-hooks/exhaustive-deps */ + const mobileColumns = useMemo( + () => [ + { + Header: '', + accessor: 'col1', // accessor is the "key" in the data + } + ], + [] + ) - const defaultEventErrorMessage = 'Token not found' + if (loadingEvent === 'loading' || loadingEvent === 'idle') { + return ( +
+ + POAP Gallery - Event + + + + + +
+ +
+
+ ) + } - return ( + if (errorEvent || Object.keys(event).length === 0) { + return (
POAP Gallery - Event @@ -148,67 +198,77 @@ export function Event() { - {!errorEvent && (isLoadingEvent() || isIdle()) && -
- +
+

{errorEvent || 'Token not found'}

+
+ warning sign
- } - {(errorEvent || Object.keys(event).length === 0) && !isLoadingEvent() && !isIdle() && -
-

{errorEvent || defaultEventErrorMessage}

-
- warning sign +
+
+ ) + } + + return ( +
+ + POAP Gallery - Event + + + + + +
+
+
+
+ {' Prev'} +

EVENT ID
#{eventId}

+ {'Next '} +
+
+
- } - {(succeededLoadingEvent() || failedLoadingEvent()) && !errorEvent && Object.keys(event).length > 0 && -
-
+
+
Collections ({tokens.length})
+ -
-
- {' Prev'} -

EVENT ID
#{eventId}

- {'Next '} -
-
- -
-
-
-
-
Collections ({tokens.length})
- {csvReadyOrAlmostReady() ? - - {`Download CSV${csvOnlyMissingEns() ? ' (without ens)' : ''}`} - - : - - } -
-
- -
-
- } -
- ) + justifyContent: 'center', + boxShadow: '0 6px 18px 0 #6534FF4D', + }} + data={csv_data} + > + Download CSV + +
+
+ { + width > 480 + ? + : + } +
+
+ + ); } function ExternalLinkCell({url, tooltipText = null, content}) { @@ -252,118 +312,15 @@ function ExternalLinkCell({url, tooltipText = null, content}) { ); } -function TableContainer({tokens, ensNames, pageCount: pc, loading}) { - const [data, setData] = useState([]); - const [mobileData, setMobileData] = useState([]); - - const MobileRow = ({token, address}) => ( -
- POAP ID#{token.id} - Address - {shrinkAddress(address, 15)} - Claim Date{utcDateFormatted(token.created * 1000)} - Transaction Count{token.transferCount} - Power{token.owner.tokensOwned} -
- ) - - const columns = useMemo( - () => [ - { - Header: () => (<>POAP ID ), - accessor: 'col1', // accessor is the "key" in the data - }, - { - Header: 'Collection', - accessor: 'col2', - }, - { - Header: 'Minting Date', - accessor: 'col3', - }, - { - Header: (<>TX Count ), - accessor: 'col4', - }, - { - Header: () => (<>Power ), - accessor: 'col5', - }, - ], - [] - ) - - const mobileColumns = useMemo( - () => [ - { - Header: '', - accessor: 'col1', // accessor is the "key" in the data - } - ], - [] - ) - - useEffect(() => { - let _data = [], _mobileData = [] - for (let i = 0; i < tokens.length; i++) { - _data.push({ - col1: () , - col2: (), - col3: tokens[i].created * 1000, - col4: tokens[i].transferCount, - col5: tokens[i].owner.tokensOwned, - }) - _mobileData.push({ - col1: - }) - } - setData(_data) - setMobileData(_mobileData) - }, [tokens]); /* eslint-disable-line react-hooks/exhaustive-deps */ - - useEffect(() => { - // Merge ens data - if(ensNames.length > 0){ - // TODO: probably there is a better way to merge - let _data = _.cloneDeep(data); - let _mobileData = _.cloneDeep(mobileData); - for (let i = 0; i < tokens.length; i++) { - let validName = ensNames[i] - if (validName) { - if (data[i]) { - _data[i].col2 = ( {validName}) - _mobileData[i].col1 = - } - } - } - setData(_data) - setMobileData(_mobileData) - } - }, [ensNames]) /* eslint-disable-line react-hooks/exhaustive-deps */ - - const [dateFormat, setDateFormat] = useState('timeago') - const toggleDateFormat = () => { - dateFormat === 'timeago' ? setDateFormat('date') : setDateFormat('timeago') - } - - const width = useWindowWidth() - const [isMobile, setIsMobile] = useState(false) - - useEffect(() => { - const _isMobile = width <= 480 - if (_isMobile !== isMobile) { - setIsMobile(_isMobile) - } - }, [width]) /* eslint-disable-line react-hooks/exhaustive-deps */ - +function CreateTable({loading, pageCount: pc, columns, data, event}) { const [length, setLength] = useState(20); const { - getTableProps: getDesktopTableProps, - getTableBodyProps: getDesktopTableBodyProps, - headerGroups: desktopHeaderGroups, - prepareRow: desktopPrepareRow, - page: desktopPage, - setPageSize: setDesktopPageSize, + getTableProps, + getTableBodyProps, + headerGroups, + prepareRow, + page, + setPageSize, } = useTable({ columns, data, pageCount: pc, initialState: { pageSize: length, sortBy: [ { @@ -372,110 +329,164 @@ function TableContainer({tokens, ensNames, pageCount: pc, loading}) { } ] } }, useSortBy, usePagination) + const [dateFormat, setDateFormat] = useState('timeago') + const toggleDateFormat = () => { + dateFormat === 'timeago' ? setDateFormat('date') : setDateFormat('timeago') + } + + return ( +
+ + + {// Loop over the header rows + headerGroups.map((headerGroup, i) => ( + // Apply the header row props + + {// Loop over the headers in each row + headerGroup.headers.map((column, idx) => ( + // Apply the header cell props + (idx === 0 || idx === 3 || idx === 4) + ? + : + idx === 2 + ? + : + ))} + + ))} + + {/* Apply the table body props */} + + {page.map((row, i) => { + prepareRow(row) + return ( + + {row.cells.map((cell, idx) => { + return ( + idx === 2 + ? + : + )})} + + ) + })} + + {loading ? ( + // Use our custom loading state to show a loading indicator + + ) : ( + + )} + + +
+ {// Render the header + column.render('Header')}{' '} + {column.isSorted + ? column.isSortedDesc + ? + : + : ''} + + {// Render the header + column.render('Header')}{' '} + + + {// Render the header + column.render('Header')} +
{dateCell(cell.value, dateFormat)}{cell.render('Cell')}
Loading... + + {/* Showing {page.length} of {page.length}{' '} + results */} +
+
+ { + if (inView) { + setLength(length + 20) + setPageSize(length + 20) + } + }} + > + {({ inView, ref, entry }) =>
} +
+
+
+ ) +} + +function CreateMobileTable({loading, pageCount: pc, columns, data, event}) { + const [length, setLength] = useState(20); const { - getTableProps: getMobileTableProps, - getTableBodyProps: getMobileTableBodyProps, - headerGroups: mobileHeaderGroups, - prepareRow: mobilePrepareRow, - page: mobilePage, - setPageSize: setMobilePageSize, - } = useTable({ columns: mobileColumns, data: mobileData, pageCount: pc, initialState: { pageSize: length } }, useSortBy, usePagination ) + getTableProps, + getTableBodyProps, + headerGroups, + prepareRow, + page, + setPageSize + } = useTable({ columns, data, pageCount: pc, initialState: { pageSize: length } }, useSortBy, usePagination ) return ( -
- - - {// Loop over the header rows - (isMobile ? mobileHeaderGroups : desktopHeaderGroups).map((headerGroup, i) => ( - // Apply the header row props - - {// Loop over the headers in each row - headerGroup.headers.map((column, idx) => { - // Apply the header cell props - if (isMobile) { - return - } else { - switch (idx) { - case 0: - case 3: - case 4: - return - case 2: - return - default: - return - } - } - })} - +
+
- {// Render the header - column.render('Header')} - - {// Render the header - column.render('Header')}{' '} - {column.isSorted - ? column.isSortedDesc - ? - : - : ''} - - {// Render the header - column.render('Header')}{' '} - - - {// Render the header - column.render('Header')} -
+ + {// Loop over the header rows + headerGroups.map((headerGroup, i) => ( + // Apply the header row props + + {// Loop over the headers in each row + headerGroup.headers.map((column, idx) => ( + // Apply the header cell props + ))} - - {/* Apply the table body props */} - - {(isMobile ? mobilePage : desktopPage).map((row, i) => { - (isMobile ? mobilePrepareRow : desktopPrepareRow)(row) - if (isMobile) { - return + + ))} + + {/* Apply the table body props */} + + {page.map((row, i) => { + prepareRow(row) + return ( + {row.cells.map((cell, idx) => { return ( - - )})} + + )})} - } else { - return ( - - {row.cells.map((cell, idx) => { - return ( - idx === 2 - ? - : - )})} - - ) - } + ) })} {loading ? ( - // Use our custom loading state to show a loading indicator - + // Use our custom loading state to show a loading indicator + ) : ( - )} - -
+ {// Render the header + column.render('Header')} +
{cell.render('Cell')}{cell.render('Cell')}
{dateCell(cell.value, dateFormat)}{cell.render('Cell')}
Loading...Loading... + + + {/* Showing {page.length} of {page.length}{' '} + results */} +
-
- { - if (inView) { - (isMobile ? setMobilePageSize : setDesktopPageSize)(length + 20) - setLength(length + 20) - } - }} - > - {({ inView, ref, entry }) =>
} - -
-
+ + +
+ { + if (inView) { + setLength(length + 20) + setPageSize(length + 20) + } + }} + > + {({ inView, ref, entry }) =>
} +
+
+
) } diff --git a/src/pages/gallery.js b/src/pages/gallery.js index be4025f..4e0c8dc 100644 --- a/src/pages/gallery.js +++ b/src/pages/gallery.js @@ -1,12 +1,7 @@ import React, {useCallback, useEffect, useState} from 'react'; import ActivityTable from '../components/activityTable' import {Helmet} from 'react-helmet'; -import { - FETCH_INDEX_PAGE_INFO_STATUS, - fetchIndexData, - selectIndexFetchStatus, - selectRecentEvents -} from '../store'; +import {fetchIndexData, selectEventError, selectEventStatus, selectRecentEvents} from '../store'; import {useDispatch, useSelector} from 'react-redux'; import {EventCard} from "../components/eventCard"; import Loader from '../components/loader' @@ -27,7 +22,9 @@ export default function Gallery() { }, []); /* eslint-disable-line react-hooks/exhaustive-deps */ const events = useSelector(selectRecentEvents) - const indexFetchStatus = useSelector(selectIndexFetchStatus) + const eventStatus = useSelector(selectEventStatus) + const eventError = useSelector(selectEventError) + const [items, setItems] = useState(events) const [search, setSearch] = useState(undefined); @@ -53,7 +50,7 @@ export default function Gallery() { debounceHandleSearch(value, items) }; const handleNewSearchValue = (value, items) => { - if (value && value.length > 2) { + if (value && value.length > 1) { const filteredItems = items.filter((item) => { return item.name.toLowerCase().indexOf(value.toLowerCase()) !== -1; }); @@ -257,7 +254,7 @@ export default function Gallery() {
- {indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.FAILED && ( + {eventError ? (
Could not load gallery, check your connection and try again
- )} - {indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.SUCCEEDED && ( + ) : eventStatus === 'succeeded' ? ( (search?.length === 0) ?
Failed search

No results for that search :(

: + ) : ( + )} - {(indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.IDLE || indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.LOADING) && }
diff --git a/src/scss/components/_poapapp.scss b/src/scss/components/_poapapp.scss index 84ccf53..b2f88c2 100755 --- a/src/scss/components/_poapapp.scss +++ b/src/scss/components/_poapapp.scss @@ -1041,8 +1041,3 @@ table { width: 12px; } } - -div.center { - display: flex; - justify-content: center; -} \ No newline at end of file diff --git a/src/scss/globals/_boilerplate.scss b/src/scss/globals/_boilerplate.scss index cabea7a..b15a773 100755 --- a/src/scss/globals/_boilerplate.scss +++ b/src/scss/globals/_boilerplate.scss @@ -244,30 +244,3 @@ ol { .tdColumnImage{ height: 26px; } - -.button-disabled { - cursor: default; - background-color: lightgray; - &:hover, &:focus-visible, &:focus { - background-color: lightgray; - } - box-shadow: none!important; -} - -.token-not-found { - display: flex; - justify-content: center; - flex-direction: column; - margin: 0 auto; - text-align: center; -} - -.discreet-paragraph { - margin: 0; - color: $grayscale5; - word-break: break-word; -} - -.no-margin { - margin: 0; -} \ No newline at end of file diff --git a/src/scss/globals/_forms.scss b/src/scss/globals/_forms.scss index a22fc75..d1cb536 100755 --- a/src/scss/globals/_forms.scss +++ b/src/scss/globals/_forms.scss @@ -547,15 +547,3 @@ textarea { } } } - -.csv-button { - width: fit-content; - min-width: auto; - margin-bottom: 0; - margin-left: auto; - padding: 12px 32px; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 6px 18px 0 #6534FF4D; -} \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index c92b8c6..fae20c7 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,19 +1,6 @@ import { createSlice, combineReducers, configureStore, createAsyncThunk, current } from '@reduxjs/toolkit'; import { getIndexPageData, getEventPageData } from './mutations'; -export const FETCH_INFO_STATUS = { - IDLE: 'IDLE', - LOADING: 'LOADING', - SUCCEEDED: 'SUCCEEDED', - FAILED: 'FAILED' -} -export const FETCH_EVENT_PAGE_INFO_STATUS = { - ...FETCH_INFO_STATUS -} -export const FETCH_INDEX_PAGE_INFO_STATUS = { - ...FETCH_INFO_STATUS -} - const initialEventsState = { events: [], event: {}, @@ -21,8 +8,9 @@ const initialEventsState = { mostClaimed: undefined, upcoming: undefined, mostRecent: undefined, - status: FETCH_INDEX_PAGE_INFO_STATUS.IDLE, - eventStatus: FETCH_EVENT_PAGE_INFO_STATUS.IDLE, + status: 'idle', + error: null, + eventStatus: 'idle', eventError: null, tokens: [], tokenId: null @@ -38,7 +26,7 @@ const eventsSlice = createSlice({ reducers: {}, extraReducers: { [fetchIndexData.pending]: (state, action) => { - state.status = FETCH_INDEX_PAGE_INFO_STATUS.LOADING + state.status = 'loading' }, [fetchIndexData.fulfilled]: (state, action) => { const { poapEvents, mostRecent, mostClaimed, upcoming } = action.payload @@ -46,15 +34,15 @@ const eventsSlice = createSlice({ state.mostRecent = mostRecent state.mostClaimed = mostClaimed state.upcoming = upcoming - state.status = FETCH_INDEX_PAGE_INFO_STATUS.SUCCEEDED + state.status = 'succeeded' }, [fetchIndexData.rejected]: (state, action) => { state.eventError = action.error.message - state.status = FETCH_INDEX_PAGE_INFO_STATUS.FAILED + state.status = 'failed' console.warn(action.error) }, [fetchEventPageData.pending]: (state, action) => { - state.eventStatus = FETCH_EVENT_PAGE_INFO_STATUS.LOADING + state.eventStatus = 'loading' }, [fetchEventPageData.fulfilled]: (state, action) => { if (state.tokenId === action.payload.id) { @@ -64,17 +52,19 @@ const eventsSlice = createSlice({ } state.tokenId = action.payload.id + state.eventStatus = 'succeeded' state.event = action.payload.event - state.eventStatus = FETCH_EVENT_PAGE_INFO_STATUS.SUCCEEDED }, [fetchEventPageData.rejected]: (state, action) => { state.eventError = action.error.message - state.eventStatus = FETCH_EVENT_PAGE_INFO_STATUS.FAILED + state.eventStatus = 'failed' + console.warn(action.error) } } }) -export const selectIndexFetchStatus = state => state.events.status +export const selectEventStatus = state => state.events.status +export const selectEventError = state => state.events.error export const selectRecentEvents = state => state.events.events.filter(event => { // don't show future events diff --git a/src/store/mutations.js b/src/store/mutations.js index 5a3d638..a11edab 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -12,8 +12,6 @@ import {ensABI} from './abis'; import _, {uniqBy} from 'lodash' import {ethers} from 'ethers'; import namehash from 'eth-ens-namehash'; -import {toast} from "react-hot-toast"; -import {toastInfoOptions} from "../utilities/utilities"; const {REACT_APP_RPC_PROVIDER_URL, REACT_APP_ENS_CONTRACT} = process.env; const provider = new ethers.providers.StaticJsonRpcProvider(REACT_APP_RPC_PROVIDER_URL); @@ -31,19 +29,11 @@ export async function getEnsData(ownerIds){ names = await ReverseRecords.getNames(chunk) }catch(e){ // Fallback to null if problem fetching Reverse record + console.log(e) names = chunk.map(a => null) } - const validNames = names.map(name => { - try { - return (namehash.normalize(name) === name && name !== '') && name - } catch (e) { - if (name && name.length) { - toast.error(`Couldn't parse ENS name '${name}'.`, toastInfoOptions) - } - return false - } - } ) - allnames = _.concat(allnames, validNames); + const validNames = names.map(name => (namehash.normalize(name) === name && name !== '') && name ) + allnames = _.concat(allnames, validNames); } return allnames } diff --git a/src/utilities/utilities.js b/src/utilities/utilities.js index 89c2c2a..2e14a6e 100644 --- a/src/utilities/utilities.js +++ b/src/utilities/utilities.js @@ -52,10 +52,3 @@ export const dateCell = (cell, dateFormat) => { } return utcDateFromNow(cell) } - -export const toastInfoOptions = { - icon: '', - style: { - backgroundColor: '#fff8e0' - } -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 378011a..e110853 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1491,11 +1491,6 @@ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz#01dd3d054da07a00b764d78748df20daf2b317e9" integrity sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw== -"@fortawesome/fontawesome-common-types@^0.2.36": - version "0.2.36" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz#b44e52db3b6b20523e0c57ef8c42d315532cb903" - integrity sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg== - "@fortawesome/fontawesome-svg-core@^1.2.30": version "1.2.35" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz#85aea8c25645fcec88d35f2eb1045c38d3e65cff" @@ -1503,13 +1498,6 @@ dependencies: "@fortawesome/fontawesome-common-types" "^0.2.35" -"@fortawesome/free-regular-svg-icons@^5.15.3": - version "5.15.4" - resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz#b97edab436954333bbeac09cfc40c6a951081a02" - integrity sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw== - dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.36" - "@fortawesome/free-solid-svg-icons@^5.14.0": version "5.15.3" resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz#52eebe354f60dc77e0bde934ffc5c75ffd04f9d8" @@ -1738,39 +1726,6 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== -"@react-hook/debounce@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@react-hook/debounce/-/debounce-3.0.0.tgz#9eea8b5d81d4cb67cd72dd8657b3ff724afc7cad" - integrity sha512-ir/kPrSfAzY12Gre0sOHkZ2rkEmM4fS5M5zFxCi4BnCeXh2nvx9Ujd+U4IGpKCuPA+EQD0pg1eK2NGLvfWejag== - dependencies: - "@react-hook/latest" "^1.0.2" - -"@react-hook/event@^1.2.1": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@react-hook/event/-/event-1.2.6.tgz#52f91578add934acc1203328ca09ab14fc7ee58e" - integrity sha512-JUL5IluaOdn5w5Afpe/puPa1rj8X6udMlQ9dt4hvMuKmTrBS1Ya6sb4sVgvfe2eU4yDuOfAhik8xhbcCekbg9Q== - -"@react-hook/latest@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@react-hook/latest/-/latest-1.0.3.tgz#c2d1d0b0af8b69ec6e2b3a2412ba0768ac82db80" - integrity sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg== - -"@react-hook/throttle@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@react-hook/throttle/-/throttle-2.2.0.tgz#d0402714a06e1ba0bc1da1fdf5c3c5cd0e08d45a" - integrity sha512-LJ5eg+yMV8lXtqK3lR+OtOZ2WH/EfWvuiEEu0M3bhR7dZRfTyEJKxH1oK9uyBxiXPtWXiQggWbZirMCXam51tg== - dependencies: - "@react-hook/latest" "^1.0.2" - -"@react-hook/window-size@^3.0.7": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@react-hook/window-size/-/window-size-3.0.7.tgz#00d176e7a8eb55814e161eae34aae20afbcbe35d" - integrity sha512-bK5ed/jN+cxy0s1jt2CelCnUt7jZRseUvPQ22ZJkUl/QDOsD+7CA/6wcqC3c0QweM/fPBRP6uI56TJ48SnlVww== - dependencies: - "@react-hook/debounce" "^3.0.0" - "@react-hook/event" "^1.2.1" - "@react-hook/throttle" "^2.2.0" - "@reduxjs/toolkit@^1.4.0": version "1.5.1" resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.5.1.tgz#05daa2f6eebc70dc18cd98a90421fab7fa565dc5" @@ -2978,13 +2933,6 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -4318,11 +4266,6 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -depd@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -5146,7 +5089,7 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: +fast-deep-equal@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -5694,11 +5637,6 @@ globule@^1.0.0: lodash "~4.17.10" minimatch "~3.0.2" -goober@^2.0.35: - version "2.0.41" - resolved "https://registry.yarnpkg.com/goober/-/goober-2.0.41.tgz#0a3d786ff9917bcf2a096eef703bf717838cbec9" - integrity sha512-kwjegMT5018zWydhOQlQneCgCtrKJaPsru7TaBWmTYV0nsMeUrM6L6O8JmNYb9UbPMgWcmtf+9p4Y3oJabIH1A== - graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" @@ -7439,11 +7377,6 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.debounce@^4.0.6, lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -7474,7 +7407,7 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash.throttle@^4.0.1, lodash.throttle@^4.1.1: +lodash.throttle@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= @@ -7501,11 +7434,6 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3 dependencies: js-tokens "^3.0.0 || ^4.0.0" -lottie-web@^5.7.6: - version "5.8.1" - resolved "https://registry.yarnpkg.com/lottie-web/-/lottie-web-5.8.1.tgz#807e0af0ad22b59bf867d964eb684cb3368da0ef" - integrity sha512-9gIizWADlaHC2GCt+D+yNpk5l2clZQFqnVWWIVdY0LnxC/uLa39dYltAe3fcmC/hrZ2IUQ8dLlY0O934Npjs7Q== - loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -7848,17 +7776,6 @@ mixin-object@^2.0.1: dependencies: minimist "^1.2.5" -morgan@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" - integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== - dependencies: - basic-auth "~2.0.1" - debug "2.6.9" - depd "~2.0.0" - on-finished "~2.3.0" - on-headers "~1.0.2" - move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -8047,7 +7964,7 @@ node-releases@^1.1.52, node-releases@^1.1.70: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== -node-sass@^4.0.0: +node-sass@^4.14.1: version "4.14.1" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.1.tgz#99c87ec2efb7047ed638fb4c9db7f3a42e2217b5" integrity sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g== @@ -9741,13 +9658,6 @@ react-helmet@^6.1.0: react-fast-compare "^3.1.1" react-side-effect "^2.1.0" -react-hot-toast@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.1.1.tgz#56409ab406b534e9e58274cf98d80355ba0fdda0" - integrity sha512-Odrp4wue0fHh0pOfZt5H+9nWCMtqs3wdlFSzZPp7qsxfzmbE26QmGWIh6hG43CukiPeOjA8WQhBJU8JwtWvWbQ== - dependencies: - goober "^2.0.35" - react-intersection-observer@^8.28.5: version "8.31.0" resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-8.31.0.tgz#0ed21aaf93c4c0475b22b0ccaba6169076d01605" @@ -9763,28 +9673,11 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-lazy-load-image-component@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/react-lazy-load-image-component/-/react-lazy-load-image-component-1.5.1.tgz#59cc92326ba5604e9c4a86805a2cf1667fafbc71" - integrity sha512-grTEZzURLHPkq7JoipcBBQU44ijdF4fH3Cb+eSD5eSAaMsjugbXqTaVWm5ruPUNLduoNR9KKQF6bOR9h2WphEg== - dependencies: - lodash.debounce "^4.0.8" - lodash.throttle "^4.1.1" - react-lifecycles-compat@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-lottie-player@^1.3.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/react-lottie-player/-/react-lottie-player-1.4.1.tgz#ddd448486b8e8a42f4ae149751c38a8539f80b77" - integrity sha512-8dw3Dt8h6iTIrA+Iz7N/rOZjY1ShEwoxsgFywW2hZPFf58mZL75NFFNXY6v95NNj/25kZQpMV/bDkNoxj4Sang== - dependencies: - fast-deep-equal "^3.1.3" - lodash.clonedeep "^4.5.0" - lottie-web "^5.7.6" - react-modal@^3.11.2: version "3.12.1" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.12.1.tgz#38c33f70d81c33d02ff1ed115530443a3dc2afd3" From 6eaff0d9bbec032900167d47061a766fcdc345ad Mon Sep 17 00:00:00 2001 From: sebasfavaron Date: Thu, 25 Nov 2021 18:46:19 -0300 Subject: [PATCH 3/3] Add: disabling of csv button while data is incomplete (PCD-113) (#37) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Add: disabling of csv button while data is incomplete + eliminated react warning (unique key on children) + search now starts with 3 or more chars (previously 2 or more) + ens names error (mainly caused by emojis) now don't cause all other ens names to fail * 🐛Fix: eliminate react dependency warnings on useEffect * 🐛Fix: issue where events with less than 1000 tokens failed to load + added option to download csv without ens data + fixed onhover style for csv download button * 🐛Fix: tooltip not showing on Download button without ens * ✨ Add: toast service and styled components * ✨ Add: nl2br that uses p tags instead of appending br tags * ✨ Add: toast notifications + made CSV_STATUS use easier to read + refactored duplicated JSX code in event.js render * 🐛Fix: unused import and unnecessary char escape * ✨ Add: constants + replaced ternary conditional with three simpler conditionals * ♻️ Refactor: event table mobile and desktop components joined +some inline styles replaced with css classes +functions simplified +status texts moved into constants +default error message moved into constant * ♻️ Refactor: simplified loading event checks +remove log +replace ternary condition with individual conditions * 🐛Fix: loading screen showing error message * ♻️ Refactor: stop using styled components * ♻️ Refactor: remove styled components * 🐛Fix: UI bug when events fetch failed --- package.json | 1 + src/App.js | 8 + src/components/activityTable.js | 6 +- src/components/eventCard.js | 4 +- src/pages/event.js | 631 ++++++++++++++--------------- src/pages/gallery.js | 32 +- src/scss/components/_poapapp.scss | 5 + src/scss/globals/_boilerplate.scss | 27 ++ src/scss/globals/_forms.scss | 12 + src/store/index.js | 34 +- src/store/mutations.js | 16 +- src/utilities/utilities.js | 7 + yarn.lock | 113 +++++- 13 files changed, 535 insertions(+), 361 deletions(-) diff --git a/package.json b/package.json index fe3fbac..4521da8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "react-csv": "^2.0.3", "react-dom": "^16.13.1", "react-helmet": "^6.1.0", + "react-hot-toast": "^2.1.1", "react-intersection-observer": "^8.28.5", "react-lazy-load-image-component": "^1.5.1", "react-lottie-player": "^1.3.1", diff --git a/src/App.js b/src/App.js index a91187e..ecac1bf 100644 --- a/src/App.js +++ b/src/App.js @@ -8,6 +8,7 @@ import Tokens from './pages/event' import Header from './components/header' import Footer from './components/footer' +import {Toaster} from "react-hot-toast"; function App() { @@ -16,6 +17,13 @@ function App() { return ( +
diff --git a/src/components/activityTable.js b/src/components/activityTable.js index 9cdc5b2..b50386b 100644 --- a/src/components/activityTable.js +++ b/src/components/activityTable.js @@ -64,8 +64,8 @@ export default function ActivityTable() { }, [daitransfers, mainnetTransfers]) return ( -
+
POAP Gallery
@@ -73,7 +73,7 @@ export default function ActivityTable() {
-
+
{' '}View more activity
diff --git a/src/components/eventCard.js b/src/components/eventCard.js index e72d6e3..03481c9 100644 --- a/src/components/eventCard.js +++ b/src/components/eventCard.js @@ -66,8 +66,8 @@ function Content({type, width, size, event, power}) { } }, [event, transferCount]) - const nl2br = (text) => (text.split('\n').map((item, key) => { - return <>{item}
+ const nl2br = (text) => (text.split('\n').map(item => { + return

{item}

})); return ( diff --git a/src/pages/event.js b/src/pages/event.js index 78e71e7..ca964e9 100644 --- a/src/pages/event.js +++ b/src/pages/event.js @@ -7,7 +7,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' import {faAngleLeft, faAngleRight, faArrowDown, faArrowUp, faDotCircle, faQuestionCircle} from '@fortawesome/free-solid-svg-icons' import {Helmet} from 'react-helmet' import {useDispatch, useSelector} from 'react-redux'; -import {fetchEventPageData} from '../store'; +import {FETCH_EVENT_PAGE_INFO_STATUS, fetchEventPageData} from '../store'; import {CSVLink} from "react-csv"; import {getEnsData} from '../store/mutations'; import Loader from '../components/loader' @@ -17,8 +17,15 @@ import { Foliage } from '../components/foliage'; import {dateCell, shrinkAddress, utcDateFormatted, utcDateFull} from '../utilities/utilities'; import { useWindowWidth } from '@react-hook/window-size/throttled'; import OpenLink from '../assets/images/openLink.svg' +import {toast} from "react-hot-toast"; const GRAPH_LIMIT = 1000; +const CSV_STATUS = { + DownloadingData: 'DownloadingData', + DownloadingLastDataChunk: 'DownloadingLastDataChunk', + ReadyWithoutEns: 'ReadyWithoutEns', + Ready: 'Ready', +} export default function Events() { let match = useRouteMatch(); @@ -29,7 +36,7 @@ export default function Events() { -

No event Selected

+

No event Selected

); @@ -45,151 +52,94 @@ export function Event() { const event = useSelector(state => state.events.event) const [pageIndex, setPageIndex] = useState(0); - const [data, setData] = useState([]); - const [mobileData, setMobileData] = useState([]); const [csv_data, setCsv_data] = useState([]); const [ensNames, setEnsNames] = useState([]); - const width = useWindowWidth(); + const [canDownloadCsv, setCanDownloadCsv] = useState(CSV_STATUS.DownloadingData); + const [tableIsLoading, setTableIsLoading] = useState(true) const pageCount = useMemo( () => event.tokenCount % 50 !== 0 ? Math.floor(event.tokenCount / 50) + 1 : event.tokenCount, [event]) const power = calculatePower(csv_data); + const csvDownloadIsOnLastStep = () => canDownloadCsv === CSV_STATUS.DownloadingLastDataChunk + const csvReadyOrAlmostReady = () => canDownloadCsv === CSV_STATUS.Ready || canDownloadCsv === CSV_STATUS.ReadyWithoutEns + const csvOnlyMissingEns = () => canDownloadCsv === CSV_STATUS.ReadyWithoutEns + const succeededLoadingEvent = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.SUCCEEDED + const isLoadingEvent = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.LOADING + const failedLoadingEvent = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.FAILED + const isIdle = () => loadingEvent === FETCH_EVENT_PAGE_INFO_STATUS.IDLE + useEffect(() => { window.scrollTo(0, 0) }, []) - - const MobileRow = ({token, address}) => ( -
- POAP ID#{token.id} - Address - {shrinkAddress(address, 15)} - Claim Date{utcDateFormatted(token.created * 1000)} - Transaction Count{token.transferCount} - Power{token.owner.tokensOwned} -
- ) + useEffect(() => { + // Get new batch of tokens if (eventId) { dispatch(fetchEventPageData({ eventId, first: GRAPH_LIMIT, skip: GRAPH_LIMIT*pageIndex })) } }, [dispatch, eventId, pageIndex]) useEffect(() => { - let ownerIds = tokens.map(t => t.owner.id) - if (event && event.tokenCount > GRAPH_LIMIT && tokens && tokens.length > 0) { - const totalPages = Math.ceil(event.tokenCount / GRAPH_LIMIT); - if (pageIndex + 1 < totalPages) { - setPageIndex(pageIndex + 1); + // Call next batch of tokens (if there is more), then load the new tokens data + const totalPages = Math.ceil(event.tokenCount / GRAPH_LIMIT); + const hasMorePages = pageIndex < totalPages + const hasTokens = tokens && tokens.length > 0 + if (event && hasTokens && hasMorePages) { + if (pageIndex + 1 === totalPages) { + setCanDownloadCsv(CSV_STATUS.DownloadingLastDataChunk) } + setPageIndex(pageIndex + 1); } - let _data = [], _mobileData = [] let _csv_data = [] _csv_data.push(['ID', 'Collection', 'ENS', 'Minting Date', 'Tx Count', 'Power']); for (let i = 0; i < tokens.length; i++) { - _data.push({ - col1: () , - col2: (), - col3: tokens[i].created * 1000, - col4: tokens[i].transferCount, - col5: tokens[i].owner.tokensOwned, - }) - _mobileData.push({ - col1: - }) _csv_data.push([tokens[i].id, tokens[i].owner.id, null, utcDateFull(tokens[i].created * 1000), tokens[i].transferCount, tokens[i].owner.tokensOwned]) } - setData(_data) - setMobileData(_mobileData) setCsv_data(_csv_data) - getEnsData(ownerIds).then(allnames => { - if(allnames.length > 0){ - setEnsNames(allnames) - } - }) }, [event, tokens, pageIndex, setPageIndex]); /* eslint-disable-line react-hooks/exhaustive-deps */ useEffect(() => { + // Merge ens data if(ensNames.length > 0){ // TODO: probably there is a better way to merge - let _data = _.cloneDeep(width > 480 ? data : mobileData); let _csv_data = _.cloneDeep(csv_data); for (let i = 0; i < tokens.length; i++) { let validName = ensNames[i] if (validName) { - if (data[i]) { - if (width > 480) { - _data[i].col2 = ( {validName}) - } else { - _data[i].col1 = - } + if (_csv_data[i+1]) { //TODO: test _csv_data[i+1][2] = validName // i+1 is there to compensate for the first array which is just the csv titles } } } - if (width > 480) { - setData(_data) - } else { - setMobileData(_data) - } setCsv_data(_csv_data) } }, [ensNames]) /* eslint-disable-line react-hooks/exhaustive-deps */ - const columns = useMemo( - () => [ - { - Header: () => (<>POAP ID ), - accessor: 'col1', // accessor is the "key" in the data - }, - { - Header: 'Collection', - accessor: 'col2', - }, - { - Header: 'Minting Date', - accessor: 'col3', - }, - { - Header: (<>TX Count ), - accessor: 'col4', - }, - { - Header: () => (<>Power ), - accessor: 'col5', - }, - ], - [] - ) - - const mobileColumns = useMemo( - () => [ - { - Header: '', - accessor: 'col1', // accessor is the "key" in the data + const validationCSVDownload = () => { + setCanDownloadCsv(CSV_STATUS.ReadyWithoutEns) + let ownerIds = tokens.map(t => t.owner.id) + getEnsData(ownerIds).then(allnames => { + if(allnames.length > 0){ + setEnsNames(allnames) } - ], - [] - ) - - if (loadingEvent === 'loading' || loadingEvent === 'idle') { - return ( -
- - POAP Gallery - Event - - - - - -
- -
-
- ) + setCanDownloadCsv(CSV_STATUS.Ready) + }).catch(() => { + toast.error(`Could not get ENS data (You can download CSV without ENS resolution or try again later)`, { + duration: 10000 + }) + }) } - if (errorEvent || Object.keys(event).length === 0) { - return ( + useEffect(() => { + if (succeededLoadingEvent() && csvDownloadIsOnLastStep()) { + validationCSVDownload() + } + setTableIsLoading(!succeededLoadingEvent()) + }, [loadingEvent]) /* eslint-disable-line react-hooks/exhaustive-deps */ + + const defaultEventErrorMessage = 'Token not found' + + return (
POAP Gallery - Event @@ -198,77 +148,67 @@ export function Event() { -
-

{errorEvent || 'Token not found'}

-
- warning sign + {!errorEvent && (isLoadingEvent() || isIdle()) && +
+
-
-
- ) - } - - return ( -
- - POAP Gallery - Event - - - - - -
-
-
-
- {' Prev'} -

EVENT ID
#{eventId}

- {'Next '} -
-
- + } + {(errorEvent || Object.keys(event).length === 0) && !isLoadingEvent() && !isIdle() && +
+

{errorEvent || defaultEventErrorMessage}

+
+ warning sign
-
-
-
Collections ({tokens.length})
- 0 && +
+
- Download CSV - -
-
- { - width > 480 - ? - : - } -
-
-
- ); + flexWrap: 'wrap', + alignContent: 'space-around', + justifyContent: 'space-around', + marginBottom: 82, + }}> +
+
+ {' Prev'} +

EVENT ID
#{eventId}

+ {'Next '} +
+
+ +
+
+
+
+
Collections ({tokens.length})
+ {csvReadyOrAlmostReady() ? + + {`Download CSV${csvOnlyMissingEns() ? ' (without ens)' : ''}`} + + : + + } +
+
+ +
+
+ } + + ) } function ExternalLinkCell({url, tooltipText = null, content}) { @@ -312,181 +252,230 @@ function ExternalLinkCell({url, tooltipText = null, content}) { ); } -function CreateTable({loading, pageCount: pc, columns, data, event}) { - const [length, setLength] = useState(20); - const { - getTableProps, - getTableBodyProps, - headerGroups, - prepareRow, - page, - setPageSize, - } = useTable({ columns, data, pageCount: pc, - initialState: { pageSize: length, sortBy: [ +function TableContainer({tokens, ensNames, pageCount: pc, loading}) { + const [data, setData] = useState([]); + const [mobileData, setMobileData] = useState([]); + + const MobileRow = ({token, address}) => ( +
+ POAP ID#{token.id} + Address + {shrinkAddress(address, 15)} + Claim Date{utcDateFormatted(token.created * 1000)} + Transaction Count{token.transferCount} + Power{token.owner.tokensOwned} +
+ ) + + const columns = useMemo( + () => [ { - id: 'col3', - desc: true + Header: () => (<>POAP ID ), + accessor: 'col1', // accessor is the "key" in the data + }, + { + Header: 'Collection', + accessor: 'col2', + }, + { + Header: 'Minting Date', + accessor: 'col3', + }, + { + Header: (<>TX Count ), + accessor: 'col4', + }, + { + Header: () => (<>Power ), + accessor: 'col5', + }, + ], + [] + ) + + const mobileColumns = useMemo( + () => [ + { + Header: '', + accessor: 'col1', // accessor is the "key" in the data } - ] } }, useSortBy, usePagination) + ], + [] + ) + + useEffect(() => { + let _data = [], _mobileData = [] + for (let i = 0; i < tokens.length; i++) { + _data.push({ + col1: () , + col2: (), + col3: tokens[i].created * 1000, + col4: tokens[i].transferCount, + col5: tokens[i].owner.tokensOwned, + }) + _mobileData.push({ + col1: + }) + } + setData(_data) + setMobileData(_mobileData) + }, [tokens]); /* eslint-disable-line react-hooks/exhaustive-deps */ + + useEffect(() => { + // Merge ens data + if(ensNames.length > 0){ + // TODO: probably there is a better way to merge + let _data = _.cloneDeep(data); + let _mobileData = _.cloneDeep(mobileData); + for (let i = 0; i < tokens.length; i++) { + let validName = ensNames[i] + if (validName) { + if (data[i]) { + _data[i].col2 = ( {validName}) + _mobileData[i].col1 = + } + } + } + setData(_data) + setMobileData(_mobileData) + } + }, [ensNames]) /* eslint-disable-line react-hooks/exhaustive-deps */ const [dateFormat, setDateFormat] = useState('timeago') const toggleDateFormat = () => { dateFormat === 'timeago' ? setDateFormat('date') : setDateFormat('timeago') } - return ( -
- - - {// Loop over the header rows - headerGroups.map((headerGroup, i) => ( - // Apply the header row props - - {// Loop over the headers in each row - headerGroup.headers.map((column, idx) => ( - // Apply the header cell props - (idx === 0 || idx === 3 || idx === 4) - ? - : - idx === 2 - ? - : - ))} - - ))} - - {/* Apply the table body props */} - - {page.map((row, i) => { - prepareRow(row) - return ( - - {row.cells.map((cell, idx) => { - return ( - idx === 2 - ? - : - )})} - - ) - })} - - {loading ? ( - // Use our custom loading state to show a loading indicator - - ) : ( - - )} - - -
- {// Render the header - column.render('Header')}{' '} - {column.isSorted - ? column.isSortedDesc - ? - : - : ''} - - {// Render the header - column.render('Header')}{' '} - - - {// Render the header - column.render('Header')} -
{dateCell(cell.value, dateFormat)}{cell.render('Cell')}
Loading... - - {/* Showing {page.length} of {page.length}{' '} - results */} -
-
- { - if (inView) { - setLength(length + 20) - setPageSize(length + 20) - } - }} - > - {({ inView, ref, entry }) =>
} -
-
-
- ) -} + const width = useWindowWidth() + const [isMobile, setIsMobile] = useState(false) + + useEffect(() => { + const _isMobile = width <= 480 + if (_isMobile !== isMobile) { + setIsMobile(_isMobile) + } + }, [width]) /* eslint-disable-line react-hooks/exhaustive-deps */ -function CreateMobileTable({loading, pageCount: pc, columns, data, event}) { const [length, setLength] = useState(20); const { - getTableProps, - getTableBodyProps, - headerGroups, - prepareRow, - page, - setPageSize - } = useTable({ columns, data, pageCount: pc, initialState: { pageSize: length } }, useSortBy, usePagination ) + getTableProps: getDesktopTableProps, + getTableBodyProps: getDesktopTableBodyProps, + headerGroups: desktopHeaderGroups, + prepareRow: desktopPrepareRow, + page: desktopPage, + setPageSize: setDesktopPageSize, + } = useTable({ columns, data, pageCount: pc, + initialState: { pageSize: length, sortBy: [ + { + id: 'col3', + desc: true + } + ] } }, useSortBy, usePagination) + + const { + getTableProps: getMobileTableProps, + getTableBodyProps: getMobileTableBodyProps, + headerGroups: mobileHeaderGroups, + prepareRow: mobilePrepareRow, + page: mobilePage, + setPageSize: setMobilePageSize, + } = useTable({ columns: mobileColumns, data: mobileData, pageCount: pc, initialState: { pageSize: length } }, useSortBy, usePagination ) return ( -
- - - {// Loop over the header rows - headerGroups.map((headerGroup, i) => ( - // Apply the header row props - - {// Loop over the headers in each row - headerGroup.headers.map((column, idx) => ( - // Apply the header cell props - +
+
- {// Render the header - column.render('Header')} -
+ + {// Loop over the header rows + (isMobile ? mobileHeaderGroups : desktopHeaderGroups).map((headerGroup, i) => ( + // Apply the header row props + + {// Loop over the headers in each row + headerGroup.headers.map((column, idx) => { + // Apply the header cell props + if (isMobile) { + return + } else { + switch (idx) { + case 0: + case 3: + case 4: + return + case 2: + return + default: + return + } + } + })} + ))} - - ))} - - {/* Apply the table body props */} - - {page.map((row, i) => { - prepareRow(row) - return ( - + + {/* Apply the table body props */} + + {(isMobile ? mobilePage : desktopPage).map((row, i) => { + (isMobile ? mobilePrepareRow : desktopPrepareRow)(row) + if (isMobile) { + return {row.cells.map((cell, idx) => { return ( - - )})} + + )})} - ) + } else { + return ( + + {row.cells.map((cell, idx) => { + return ( + idx === 2 + ? + : + )})} + + ) + } })} {loading ? ( - // Use our custom loading state to show a loading indicator - + // Use our custom loading state to show a loading indicator + ) : ( - + - -
+ {// Render the header + column.render('Header')} + + {// Render the header + column.render('Header')}{' '} + {column.isSorted + ? column.isSortedDesc + ? + : + : ''} + + {// Render the header + column.render('Header')}{' '} + + + {// Render the header + column.render('Header')} +
{cell.render('Cell')}{cell.render('Cell')}
{dateCell(cell.value, dateFormat)}{cell.render('Cell')}
Loading...Loading... - - {/* Showing {page.length} of {page.length}{' '} - results */} - )}
-
- { - if (inView) { - setLength(length + 20) - setPageSize(length + 20) - } - }} - > - {({ inView, ref, entry }) =>
} -
-
-
+ + +
+ { + if (inView) { + (isMobile ? setMobilePageSize : setDesktopPageSize)(length + 20) + setLength(length + 20) + } + }} + > + {({ inView, ref, entry }) =>
} + +
+
) } diff --git a/src/pages/gallery.js b/src/pages/gallery.js index 4e0c8dc..8f004ea 100644 --- a/src/pages/gallery.js +++ b/src/pages/gallery.js @@ -1,7 +1,12 @@ import React, {useCallback, useEffect, useState} from 'react'; import ActivityTable from '../components/activityTable' import {Helmet} from 'react-helmet'; -import {fetchIndexData, selectEventError, selectEventStatus, selectRecentEvents} from '../store'; +import { + FETCH_INDEX_PAGE_INFO_STATUS, + fetchIndexData, + selectIndexFetchStatus, + selectRecentEvents +} from '../store'; import {useDispatch, useSelector} from 'react-redux'; import {EventCard} from "../components/eventCard"; import Loader from '../components/loader' @@ -22,9 +27,7 @@ export default function Gallery() { }, []); /* eslint-disable-line react-hooks/exhaustive-deps */ const events = useSelector(selectRecentEvents) - const eventStatus = useSelector(selectEventStatus) - const eventError = useSelector(selectEventError) - + const indexFetchStatus = useSelector(selectIndexFetchStatus) const [items, setItems] = useState(events) const [search, setSearch] = useState(undefined); @@ -50,7 +53,7 @@ export default function Gallery() { debounceHandleSearch(value, items) }; const handleNewSearchValue = (value, items) => { - if (value && value.length > 1) { + if (value && value.length > 2) { const filteredItems = items.filter((item) => { return item.name.toLowerCase().indexOf(value.toLowerCase()) !== -1; }); @@ -254,27 +257,22 @@ export default function Gallery() {
- {eventError ? ( -
- Could not load gallery, check your connection and try again -
- - ) : eventStatus === 'succeeded' ? ( + {indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.SUCCEEDED && ( (search?.length === 0) ?
Failed search

No results for that search :(

: - ) : ( - )} + {(indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.IDLE || indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.LOADING) && }
+ {indexFetchStatus === FETCH_INDEX_PAGE_INFO_STATUS.FAILED && ( +
+ Could not load gallery, check your connection and try again +
+ )} {!search ?