diff --git a/admin/src/components/events/events-table-virtualized-lazy-loading.js b/admin/src/components/events/events-table-virtualized-lazy-loading.js
new file mode 100644
index 0000000..d5ff516
--- /dev/null
+++ b/admin/src/components/events/events-table-virtualized-lazy-loading.js
@@ -0,0 +1,181 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import {
+ fetchEventsWithPagination,
+ toggleSelection as handleSelect,
+ eventListSelector,
+ loadedSelector,
+ loadingSelector,
+ loadedRowCount,
+ loadingRowCount,
+ loadedRowsMap,
+ clearData,
+ changeLoadingInfo
+} from '../../ducks/events'
+import Loader from '../common/loader'
+import { InfiniteLoader, AutoSizer, List } from 'react-virtualized'
+import 'react-virtualized/styles.css'
+
+export const width = 900
+export const columnWidth = 300
+export const height = 420
+export const rowHeight = 40
+
+const STATUS_LOADING = 1
+const STATUS_LOADED = 2
+
+export class EventsTable extends Component {
+ // state = {
+ // // loadedRowCount: 0,
+ // loadedRowsMap: {}
+ // // loadingRowCount: 0
+ // }
+ static propTypes = {}
+
+ componentDidMount() {
+ this.props.fetchEventsWithPagination &&
+ this.props.fetchEventsWithPagination()
+ }
+
+ componentWillUnmount() {
+ Object.keys(this.timeoutIdMap).forEach((timeoutId) => {
+ clearTimeout(timeoutId)
+ })
+ }
+
+ render() {
+ const {
+ loading,
+ // loaded,
+ events,
+ clearData,
+ loadedRowCount,
+ loadingRowCount
+ } = this.props
+
+ return (
+
+
+
+
+
+
+
+ {loadingRowCount} loading, {loadedRowCount} loaded
+
+
+
+
+ {loading && !loadedRowCount ? (
+
+ ) : (
+
+ {({ onRowsRendered, registerChild }) => (
+
+ {({ width }) => (
+
+ )}
+
+ )}
+
+ )}
+
+ )
+ }
+
+ handleRowClick = (event) => () => {
+ this.props.handleSelect(event.uid)
+ }
+
+ timeoutIdMap = () => {}
+
+ isRowLoaded = ({ index }) => {
+ const { loadedRowsMap } = this.props
+ return !!loadedRowsMap[index] // STATUS_LOADING or STATUS_LOADED
+ }
+
+ loadMoreRows = ({ startIndex, stopIndex }) => {
+ const { loadedRowsMap, loadingRowCount } = this.props
+ const increment = stopIndex - startIndex + 1
+
+ for (var i = startIndex; i <= stopIndex; i++) {
+ loadedRowsMap[i] = STATUS_LOADING
+ }
+
+ this.props.changeLoadingInfo({
+ loadingRowCount: loadingRowCount + increment
+ })
+
+ const timeoutId = setTimeout(() => {
+ const { loadedRowCount, loadingRowCount } = this.props
+ delete this.timeoutIdMap[timeoutId]
+
+ for (var i = startIndex; i <= stopIndex; i++) {
+ loadedRowsMap[i] = STATUS_LOADED
+ }
+
+ this.props.changeLoadingInfo({
+ loadingRowCount: loadingRowCount - increment,
+ loadedRowCount: loadedRowCount + increment
+ })
+
+ promiseResolver({
+ size: loadingRowCount,
+ startAt: startIndex,
+ endAt: stopIndex
+ })
+ }, 1000 + Math.round(Math.random() * 2000))
+
+ this.timeoutIdMap[timeoutId] = true
+
+ let promiseResolver
+
+ return new Promise((resolve) => {
+ promiseResolver = (pagination) =>
+ resolve(this.props.fetchEventsWithPagination(pagination))
+ })
+ }
+
+ rowRenderer = ({ index, key, style }) => {
+ const { events, loadedRowsMap } = this.props
+
+ const row = events[index]
+ let content
+
+ if (loadedRowsMap[index] === STATUS_LOADED) {
+ content = row.title
+ } else {
+ content =
+ }
+
+ return (
+
+ {content}
+
+ )
+ }
+}
+
+export default connect(
+ (state) => ({
+ events: eventListSelector(state),
+ loading: loadingSelector(state),
+ loaded: loadedSelector(state),
+ loadedRowCount: loadedRowCount(state),
+ loadingRowCount: loadingRowCount(state),
+ loadedRowsMap: loadedRowsMap(state)
+ }),
+ { fetchEventsWithPagination, handleSelect, clearData, changeLoadingInfo }
+)(EventsTable)
diff --git a/admin/src/components/events/events-table-virtualized.js b/admin/src/components/events/events-table-virtualized.js
index eb5ba6c..aba42c5 100644
--- a/admin/src/components/events/events-table-virtualized.js
+++ b/admin/src/components/events/events-table-virtualized.js
@@ -11,6 +11,11 @@ import Loader from '../common/loader'
import { Table, Column } from 'react-virtualized'
import 'react-virtualized/styles.css'
+export const width = 900
+export const columnWidth = 300
+export const height = 420
+export const rowHeight = 40
+
export class EventsTable extends Component {
static propTypes = {}
@@ -24,20 +29,35 @@ export class EventsTable extends Component {
return (
-
-
-
+ (
+
+ {cellData}
+
+ )}
+ dataKey="title"
+ width={columnWidth}
+ />
+
+
)
}
rowGetter = ({ index }) => this.props.events[index]
+
+ handleRowClick = (event) => () => {
+ this.props.handleSelect(event.uid)
+ }
}
export default connect(
diff --git a/admin/src/components/events/events-table-virtualized.test.js b/admin/src/components/events/events-table-virtualized.test.js
new file mode 100644
index 0000000..1400434
--- /dev/null
+++ b/admin/src/components/events/events-table-virtualized.test.js
@@ -0,0 +1,75 @@
+import React from 'react'
+import {
+ EventsTable,
+ width,
+ height,
+ rowHeight,
+ columnWidth
+} from './events-table-virtualized'
+import { shallow, mount } from 'enzyme'
+import { Table, Column } from 'react-virtualized'
+
+import Loader from '../common/loader'
+import eventsMocks from '../../mocks/conferences'
+
+const defaultOverscanRowCount = 1
+
+const events = eventsMocks.map((event) => ({
+ ...event,
+ uid: Math.random().toString()
+}))
+
+describe('EventsVirtualizedTable', () => {
+ it('should render loader', () => {
+ const container = shallow()
+
+ expect(container.contains()).toBe(true)
+ })
+
+ it('should render visible events', () => {
+ const container = mount()
+
+ expect(container.find('.test--event-list_item').length).toEqual(
+ Math.round(height / rowHeight) + defaultOverscanRowCount
+ )
+ })
+
+ it('should render 3 colunms', () => {
+ const container = shallow()
+
+ expect(container.children().length).toEqual(3)
+ })
+
+ it('should render first column with key title', () => {
+ const container = shallow()
+
+ expect(
+ container
+ .children()
+ .find({ dataKey: 'title' })
+ .exists()
+ ).toEqual(true)
+ })
+
+ it('should fetch all events', (done) => {
+ shallow( done()} />)
+ })
+
+ it('should select an event', () => {
+ let selectedEventId = null
+
+ const container = mount(
+ (selectedEventId = id)}
+ />
+ )
+
+ container
+ .find('.test--event-list_item')
+ .first()
+ .simulate('click')
+
+ expect(selectedEventId).toEqual(events[0].uid)
+ })
+})
diff --git a/admin/src/config.js b/admin/src/config.js
deleted file mode 100644
index aa22b3b..0000000
--- a/admin/src/config.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import firebase from 'firebase/app'
-import 'firebase/auth'
-import 'firebase/database'
-
-export const appName = 'advreact-10-05'
-
-export const config = {
- apiKey: 'AIzaSyCbMQM0eQUSQ0SuLVAu9ZNPUcm4rdbiB8U',
- authDomain: `${appName}.firebaseapp.com`,
- databaseURL: `https://${appName}.firebaseio.com`,
- projectId: appName,
- storageBucket: '',
- messagingSenderId: '1094825197832'
-}
-
-firebase.initializeApp(config)
diff --git a/admin/src/ducks/events.js b/admin/src/ducks/events.js
index 74107b8..d5eb771 100644
--- a/admin/src/ducks/events.js
+++ b/admin/src/ducks/events.js
@@ -14,7 +14,12 @@ const prefix = `${appName}/${moduleName}`
export const FETCH_ALL_REQUEST = `${prefix}/FETCH_ALL_REQUEST`
export const FETCH_ALL_START = `${prefix}/FETCH_ALL_START`
export const FETCH_ALL_SUCCESS = `${prefix}/FETCH_ALL_SUCCESS`
+export const FETCH_PAGINATION_EVENTS_REQUEST = `${prefix}/FETCH_PAGINATION_EVENTS_REQUEST`
+export const FETCH_PAGINATION_EVENTS_START = `${prefix}/FETCH_PAGINATION_EVENTS_START`
+export const FETCH_PAGINATION_EVENTS_SUCCESS = `${prefix}/FETCH_PAGINATION_EVENTS_SUCCESS`
export const TOGGLE_SELECTION = `${prefix}/TOGGLE_SELECTION`
+export const CLEAR_DATA = `${prefix}/CLEAR_DATA`
+export const CHANGE_LOADING_INFO = `${prefix}/CHANGE_LOADING_INFO`
/**
* Reducer
@@ -23,7 +28,10 @@ export const ReducerRecord = Record({
loading: false,
loaded: false,
selected: new OrderedSet(),
- entities: new OrderedMap()
+ entities: new OrderedMap(),
+ loadedRowCount: 0,
+ loadingRowCount: 0,
+ loadedRowsMap: {}
})
export const EventRecord = Record({
@@ -41,9 +49,14 @@ export default function reducer(state = new ReducerRecord(), action) {
switch (type) {
case FETCH_ALL_START:
+ case FETCH_PAGINATION_EVENTS_START:
return state.set('loading', true)
+ case CHANGE_LOADING_INFO:
+ return state.merge(payload)
+
case FETCH_ALL_SUCCESS:
+ case FETCH_PAGINATION_EVENTS_SUCCESS:
return state
.set('loading', false)
.set('loaded', true)
@@ -58,6 +71,13 @@ export default function reducer(state = new ReducerRecord(), action) {
: selected.add(payload.uid)
)
+ case CLEAR_DATA:
+ return state
+ .set('loadedRowCount', 0)
+ .set('loadingRowCount', 0)
+ .set('loading', false)
+ .set('loaded', false)
+
default:
return state
}
@@ -80,6 +100,18 @@ export const loadedSelector = createSelector(
stateSelector,
(state) => state.loaded
)
+export const loadedRowCount = createSelector(
+ stateSelector,
+ (state) => state.loadedRowCount
+)
+export const loadingRowCount = createSelector(
+ stateSelector,
+ (state) => state.loadingRowCount
+)
+export const loadedRowsMap = createSelector(
+ stateSelector,
+ (state) => state.loadedRowsMap
+)
export const eventListSelector = createSelector(entitiesSelector, (entities) =>
entities.valueSeq().toArray()
)
@@ -103,6 +135,14 @@ export function fetchAllEvents() {
type: FETCH_ALL_REQUEST
}
}
+export function fetchEventsWithPagination(
+ { startAt, endAt } = { startAt: 0, endAt: 10 }
+) {
+ return {
+ type: FETCH_PAGINATION_EVENTS_REQUEST,
+ payload: { startAt, endAt }
+ }
+}
export function toggleSelection(uid) {
return {
@@ -111,6 +151,19 @@ export function toggleSelection(uid) {
}
}
+export function changeLoadingInfo(data) {
+ return {
+ type: CHANGE_LOADING_INFO,
+ payload: { ...data }
+ }
+}
+
+export function clearData() {
+ return {
+ type: CLEAR_DATA
+ }
+}
+
/**
* Sagas
* */
@@ -129,7 +182,32 @@ export function* fetchAllSaga() {
payload: snapshot.val()
})
}
+export function* fetchWithPaginationSaga({ payload }) {
+ const { startAt, endAt } = payload
+ console.log('pagination', startAt, endAt)
+ const ref = firebase
+ .database()
+ .ref('events')
+ .orderByKey()
+ // .startAt(startAt)
+ // .endAt(endAt)
+
+ yield put({
+ type: FETCH_ALL_START
+ })
+
+ const snapshot = yield call([ref, ref.once], 'value')
+ console.log('snapshot', snapshot.val())
+
+ yield put({
+ type: FETCH_ALL_SUCCESS,
+ payload: snapshot.val()
+ })
+}
export function* saga() {
- yield all([takeEvery(FETCH_ALL_REQUEST, fetchAllSaga)])
+ yield all([
+ takeEvery(FETCH_ALL_REQUEST, fetchAllSaga),
+ takeEvery(FETCH_PAGINATION_EVENTS_REQUEST, fetchWithPaginationSaga)
+ ])
}
diff --git a/admin/src/routes/events-page.js b/admin/src/routes/events-page.js
index 2472593..a74e7bb 100644
--- a/admin/src/routes/events-page.js
+++ b/admin/src/routes/events-page.js
@@ -1,5 +1,5 @@
import React, { Component } from 'react'
-import EventsTable from '../components/events/events-table-virtualized'
+import EventsTable from '../components/events/events-table-virtualized-lazy-loading'
import SelectedEvents from '../components/events/selected-events'
class EventsPage extends Component {