Skip to content
This repository has been archived by the owner on Feb 6, 2019. It is now read-only.

Add latest route #103

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2a28039
Avoid error thrown by marshamllow_jsonapi
ezmiller Jul 20, 2016
b90a9df
Revert "Avoid error thrown by marshamllow_jsonapi"
ezmiller Jul 22, 2016
d45a5cf
Avoid args order error
ezmiller Jul 24, 2016
0fc50b7
Add attempt to fix pagination schema
ezmiller Jul 25, 2016
8872ac9
Set key to entities for latest route
ezmiller Aug 2, 2016
6583f7c
Fix paging schema move to meta
ezmiller Aug 4, 2016
ca35e52
Adds prototype of latest view
ezmiller Aug 4, 2016
2e7808a
Rig latest view for infinite list
ezmiller Aug 18, 2016
39fd0a1
Update InfiniteList component to use ReactDOM
ezmiller Aug 18, 2016
e038be5
Fix incorrect lifecycle method name
ezmiller Aug 18, 2016
4ef37c7
Update property desecrated prop name
ezmiller Aug 18, 2016
479f46b
Add key to repeated components
ezmiller Aug 18, 2016
c885907
Revert preloadBatchSize to default
ezmiller Aug 18, 2016
67cea29
Use isEqual in shouldComponentUpdate
ezmiller Aug 19, 2016
c4379ef
Use usewindowAsScrollContainer feature of react-infinite
ezmiller Aug 19, 2016
899619e
Prevent load more action when isFetching or no next pages
ezmiller Aug 19, 2016
838286c
Add isFetching prop to LatestView
ezmiller Aug 21, 2016
8f7fe91
Fix reference to meta has_next
ezmiller Aug 21, 2016
37224e2
Clean up
ezmiller Aug 21, 2016
6baaac3
Fix long line
ezmiller Aug 21, 2016
ac20bc9
Add explanation for confusing react-infinite disable procedure
ezmiller Aug 21, 2016
509b2e4
Please eslint
ezmiller Aug 19, 2016
353dbab
Fix python style problems
ezmiller Aug 21, 2016
62878f4
Revert to setting containerHeight explicitly
ezmiller Aug 28, 2016
c3207ca
Change default size of LIs to 20px
ezmiller Aug 28, 2016
f585241
Upgrade the react-infinite module
ezmiller Aug 28, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion beavy/common/morphing_schema.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from marshmallow import pre_dump
from marshmallow import pre_dump, fields


class MorphingSchema():
Expand All @@ -16,3 +16,16 @@ def _obj_to_name(self, obj):
def _get_serializer(self, obj):
name = self._obj_to_name(obj)
return self.registry.get(name, self.FALLBACK)()


class MorphingNested(fields.Nested, MorphingSchema):

def _get_serializer(self, obj):
name = self._obj_to_name(obj)
rv = self.nested.registry.get(name, self.nested.FALLBACK)()
return rv

def _serialize(self, nested_obj, attr, obj):
rv = [self._get_serializer(obj).dump(obj).data
for obj in nested_obj]
return rv
7 changes: 4 additions & 3 deletions beavy/common/paging_schema.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from marshmallow import Schema, fields, post_dump
from beavy.common.morphing_schema import MorphingNested


class BasePaging(Schema):
Expand All @@ -17,11 +18,11 @@ def move_to_meta(self, data):
# FIXME: add support for paging links
return {
"meta": data,
"data": items.get("data", []),
"links": items.get("links", [])
"data": [item.get("data") for item in items],
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is links supposed to hold?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's supposed to hold paging links, but since we are using infinite pagination it's not implemented yet. This will also be a bit challenging because it's going to be hard for the pagination object to know, from the front-end, the details of the type of pagination.

"links": []
}


def makePaginationSchema(itemsCls, field_cls=fields.Nested):
def makePaginationSchema(itemsCls, field_cls=MorphingNested):
return type("{}Paging".format(itemsCls.__class__.__name__),
(BasePaging, ), dict(items=field_cls(itemsCls, many=True)))
23 changes: 23 additions & 0 deletions beavy/jsbeavy/actions/latest.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { CALL_API } from 'middleware/api'
import { LATEST } from 'reducers/latest'

export const LATEST_REQUEST = 'LATEST_REQUEST'
export const LATEST_SUCCESS = 'LATEST_SUCCESS'
export const LATEST_FAILURE = 'LATEST_FAILURE'

const fetchLatest = (page = 1) => {
return {
[CALL_API]: {
types: [LATEST_REQUEST, LATEST_SUCCESS, LATEST_FAILURE],
endpoint: '/latest?page=' + page,
key: LATEST
}
}
}

export function loadLatest (page = 1) {
return (dispatch, getState) => {
return dispatch(fetchLatest(page = page))
}
}

43 changes: 17 additions & 26 deletions beavy/jsbeavy/components/InfiniteList.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
/*eslint-disable react/no-multi-comp*/
import React, { Component, PropTypes} from 'react'
import ReactDOM from 'react-dom'
import { FormattedMessage } from 'react-intl'
import Infinite from 'react-infinite'
import map from 'lodash/collection/map'
import fill from 'lodash/array/fill'
import { fill, isEqual } from 'lodash'

class SizeReportWrapper extends Component {
propTypes: {
reportHeight: PropTypes.func.isRequired,
element: PropTypes.Component.isRequired,
}
componentDidMount () {
console.log('mounted')
var el = React.findDOMNode(this.refs.child)
var el = ReactDOM.findDOMNode(this.refs.child)
this.props.reportHeight(el.offsetHeight)
}
render () {
console.log('rendering')
return React.cloneElement(this.props.element, {ref: 'child'})
}
}
Expand Down Expand Up @@ -45,63 +44,55 @@ export default class InfiniteList extends Component {
}

shouldComponentUpdate (nextProps, nextState) {
var propsChanged = this.props !== nextProps
var propsChanged = !isEqual(this.props, nextProps)
var stateChanged = this.state !== nextState
return propsChanged || stateChanged
}

render () {
console.log(this.state)
console.log(map(this.props.children, (c, i) => <SizeReportWrapper element={c} reportHeight={(height) => this.reportHeight(i, height)} />))
// Note that the slightly confusing way of disabling infinite loading
// on the Infinite component, is to set the infiniteLoadBeginEdgeOffset
// prop to undefined, or not to set it all. We are setting it ot undefined
// when has_next == false.
return <Infinite elementHeight={this.state.elementHeights}
containerHeight={this.state.containerHeight}
infiniteLoadBeginBottomOffset={this.props.meta.has_next && 400}
infiniteLoadBeginEdgeOffset={!this.props.meta.has_next ? undefined : 200}
onInfiniteLoad={::this.handleInfiniteLoad}
loadingSpinnerDelegate={this.elementInfiniteLoad()}
isInfiniteLoading={this.props.isFetching}
preloadBatchSize={20}
className='infinite-list'
scrollNumberCallback={this.scrollCallback}
selectedItem={this.props.selectedItem}
>
{map(this.props.children, (c, i) => <SizeReportWrapper element={c} reportHeight={(height) => this.reportHeight(i, height)} />
{map(this.props.children, (c, i) =>
<SizeReportWrapper
key={i}
element={c}
reportHeight={(height) => this.reportHeight(i, height)} />
)}
</Infinite>
}

reportHeight (i, height) {
console.log(i, height)
let curHeights = this.state.elementHeights
curHeights[i] = height
this.setState({elementHeights: curHeights})
}

handleResize (e) {
this.setState({containerHeight: window.innerHeight - 60})
}

componentDidMount () {
window.addEventListener('resize', this.handleResize)
}

componentDidUnmount () {
window.removeEventListener('resize', this.handleResize)
}

componentWillReceiveProps (newProps) {
let newState = {elements: newProps.children}
if (this.props.children.length < newProps.children.length) {
const minimalItemHeight = newProps.minimalItemHeight || this.props.minimalItemHeight || 100
newState.elementHeights = this.state.elementHeights.concat(
fill(Array(newProps.children.length - this.props.children.length), minimalItemHeight))
}
console.log(newState)
this.setState(newState)
}

handleInfiniteLoad () {
console.log('LOAD MOA!')
this.props.loader(this.props.meta.page + 1)
if (this.props.meta.has_next && !this.props.isFetching) {
this.props.loader(this.props.meta.page + 1)
}
}

elementInfiniteLoad () {
Expand Down
11 changes: 11 additions & 0 deletions beavy/jsbeavy/reducers/latest.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { addNamedExtension } from 'config/extensions'
import paginate from 'reducers/paginate'
import { LATEST_SUCCESS, LATEST_FAILURE, LATEST_REQUEST } from 'actions/latest'

export const LATEST = 'latest'

addNamedExtension('reducers', LATEST, paginate({
mapActionToKey: x => LATEST,
types: [LATEST_REQUEST, LATEST_SUCCESS, LATEST_FAILURE]
}))

64 changes: 64 additions & 0 deletions beavy/jsbeavy/views/LatestView.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* eslint-disable react/no-multi-comp */
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { getStoreEntity } from 'utils'
import { LATEST } from 'reducers/latest'
import { loadLatest } from 'actions/latest'
import InfiniteList from 'components/InfiniteList'

// This should be a stateless component but if it's to be
// consumed by the InfiniteList later, we'll need to add
// a ref and you can't do that on stateless components.
class Link extends Component {
render () {
const props = this.props
return (
<li key={props.key} style={{height: 20}}>
<a target='_blank' href={props.url}>{props.title}</a>
</li>
)
}
}

const LinksList = ({
meta,
links,
isFetching,
loadMore
}) => (
<InfiniteList
meta={meta}
loader={loadMore}
minimalItemHeight={24}
isFetching={isFetching}
>
{links.map(l =>
<Link key={l.id} {...l} />
)}
</InfiniteList>
)

const mapStateToProps = (state) => {
return {
meta: state[LATEST].meta,
links: state[LATEST].data.map((item) => {
return getStoreEntity(state, item)
}),
isFetching: state[LATEST].isFetching
}
}

const mapDispatchToProps = (dispatch, props) => {
return {
loadMore: (page) => {
console.log('loadMore', {page: page})
dispatch(loadLatest(page))
}
}
}

export default connect(
mapStateToProps,
mapDispatchToProps
)(LinksList)
/* eslint-enable react/no-multi-comp */
4 changes: 4 additions & 0 deletions beavy/jsbeavy/views/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { make_url } from 'utils'

import UserView from 'views/UserView'
import HomeView from 'views/HomeView'
import LatestView from 'views/LatestView'

export default function setupViews (Application) {
addExtension('routes',
Expand All @@ -19,4 +20,7 @@ export default function setupViews (Application) {
<Route key='account' name='account' path={make_url.account('')}>
{getExtensions('accountRoutes')}
</Route>)

addExtension('routes',
<Route key='latest' name='latest' path='/latest' component={LatestView} />)
}
2 changes: 1 addition & 1 deletion beavy/models/object.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

class ObjectQuery(AccessQuery):

def by_capability(self, aborting=True, abort_code=404, *caps):
def by_capability(self, *caps, aborting=True, abort_code=404):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was causing an error because of the order of the args list. In the commit, I wrote:

Was getting error related to order of arguments: "got multiple
values for argument...", in this case the two keyword args. This
seems to fix it. See https://stackoverflow.com/questions/28336270/how-to-fix-got-multiple-values-for-argument-error-for-args-and-kwargs

caps = set(chain.from_iterable(map(lambda c:
getattr(Object.TypesForCapability,
getattr(c, 'value', c), []),
Expand Down
6 changes: 4 additions & 2 deletions beavy/views/lists/latest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

@lists_bp.route("latest/")
@rate_limit("2/second; 100/minute; 1000/hour; 1000/day")
@fallbackRender('home.html')
@fallbackRender('home.html', 'latest')
def latest():
query = Object.query.by_capability('listed', aborting=False # noqa
).with_my_activities(
).order_by(desc('objects.created_at'))
return objects_paged.dump(as_page(query, error_out=False)).data
rv = objects_paged.dump(as_page(query, per_page=5, error_out=False)).data
print("DEBUG: latest() rv: ", rv)
return rv
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"po2json": "^0.4.1",
"react": "^0.14.3",
"react-dom": "^0.14.3",
"react-infinite": "^0.7.1",
"react-infinite": "^0.9.2",
"react-prosemirror": "^0.1.4",
"react-intl": "^2.0.0-beta-2",
"react-proxy-loader": "^0.3.4",
Expand Down