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

Commit

Permalink
Add My Posts tool page (#613)
Browse files Browse the repository at this point in the history
  • Loading branch information
voidxnull committed Sep 1, 2016
1 parent 87fd8d2 commit 92dae7d
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 10 deletions.
21 changes: 18 additions & 3 deletions src/actions/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export const TOOLS__SET_USER_POST = 'TOOLS__SET_USER_POST';
export const TOOLS__ADD_USER_POST = 'TOOLS__ADD_USER_POST';

export const TOOLS__ADD_SCHOOLS_TO_RIVER = 'TOOLS__ADD_SCHOOLS_TO_RIVER';
export const TOOLS__SET_SCHOOLS_RIVER = 'TOOLS__SET_SCHOOLS_RIVER';

export const TOOLS__ADD_USER_POSTS_TO_RIVER = 'TOOLS__ADD_USER_POSTS_TO_RIVER';
export const TOOLS__SET_USER_POSTS_RIVER = 'TOOLS__SET_USER_POSTS_RIVER';


export function addSchoolsToRiver(schools) {
return {
Expand All @@ -35,3 +35,18 @@ export function setSchoolsRiver(schools) {
schools
};
}


export function addUserPostsToRiver(posts) {
return {
type: TOOLS__ADD_USER_POSTS_TO_RIVER,
posts
};
}

export function setUserPostsRiver(posts) {
return {
type: TOOLS__SET_USER_POSTS_RIVER,
posts
};
}
4 changes: 2 additions & 2 deletions src/api/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ export default class ApiClient
return await response.json();
}

async userPosts(username) {
const response = await this.get(`/api/v1/posts/user/${username}`);
async userPosts(username, query = {}) {
const response = await this.get(`/api/v1/posts/user/${username}`, query);
return await response.json();
}

Expand Down
4 changes: 3 additions & 1 deletion src/api/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ export default class ApiController {
.join('users', 'users.id', 'posts.user_id')
.where('users.username', '=', ctx.params.user)
.orderBy('posts.updated_at', 'desc')
.whereIn('posts.type', ['short_text', 'long_text']);
.whereIn('posts.type', ['short_text', 'long_text'])
.limit(ctx.query.limit)
.offset(ctx.query.offset);
});


Expand Down
134 changes: 134 additions & 0 deletions src/pages/tools/my-posts-tool.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
This file is a part of libertysoil.org website
Copyright (C) 2016 Loki Education (Social Enterprise)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Helmet from 'react-helmet';
import { Link } from 'react-router';
import { truncate } from 'grapheme-utils';

import { uuid4 } from '../../prop-types/common';
import { Post } from '../../prop-types/posts';
import { CurrentUser } from '../../prop-types/current-user.js';
import { User } from '../../prop-types/users.js';
import createSelector from '../../selectors/createSelector';
import currentUserSelector from '../../selectors/currentUser';
import { ActionsTrigger } from '../../triggers';
import ApiClient from '../../api/client';
import { API_HOST } from '../../config';
import Button from '../../components/button';
import VisibilitySensor from '../../components/visibility-sensor';


class MyPostsToolPage extends React.Component {
static displayName = 'SchoolsToolPage';

static propTypes = {
current_user: CurrentUser,
dispatch: PropTypes.func.isRequired,
posts: ImmutablePropTypes.mapOf(Post, uuid4).isRequired,
ui: ImmutablePropTypes.contains({
progress: ImmutablePropTypes.contains({
loadingUserPostsRiver: PropTypes.bool
}).isRequired
}),
user_posts_river: ImmutablePropTypes.listOf(uuid4).isRequired,
users: ImmutablePropTypes.mapOf(User, uuid4)
};

static async fetchData(params, store, client) {
const userId = store.getState().getIn(['current_user', 'id']);
const userName = store.getState().getIn(['users', userId, 'username']);
const trigger = new ActionsTrigger(client, store.dispatch);
await trigger.toolsLoadUserPostsRiver(userName, { limit: 25 });
}

state = {
displayLoadMore: true
}

handleLoadPosts = async () => {
const userId = this.props.current_user.get('id');
const userName = this.props.users.getIn([userId, 'username']);
const client = new ApiClient(API_HOST);
const trigger = new ActionsTrigger(client, this.props.dispatch);
const result = await trigger.toolsLoadUserPostsRiver(userName, {
limit: 25,
offset: this.props.user_posts_river.size
});

if (Array.isArray(result) && result.length === 0) {
this.setState({ displayLoadMore: false });
}
};

handleLoadOnSensor = async (isVisible) => {
if (isVisible && !this.props.ui.getIn(['progress', 'loadingUserPostsRiver'])) {
this.handleLoadPosts();
}
};

render() {
const {
posts,
ui,
user_posts_river
} = this.props;

const postsToDisplay = user_posts_river.map(postId => posts.get(postId));

return (
<div>
<Helmet title="My posts tool on " />
{postsToDisplay.map((post, index) =>
<div className="tools_page__item" key={index}>
<Link to={`/post/${post.get('id')}`}>{truncate(post.get('text'), { length: 70 })}</Link>
</div>
)}
<div className="layout layout-align_center layout__space layout__space-double">
{this.state.displayLoadMore &&
<VisibilitySensor onChange={this.handleLoadOnSensor}>
<Button
title="Load more..."
waiting={ui.getIn(['progress', 'loadingUserPostsRiver'])}
onClick={this.handleLoadPosts}
/>
</VisibilitySensor>
}
</div>
</div>
);
}
}

const selector = createSelector(
state => state.get('ui'),
state => state.get('posts'),
state => state.getIn(['tools', 'user_posts_river']),
state => state.get('users'),
currentUserSelector,
(ui, posts, user_posts_river, users, current_user) => ({
ui,
posts,
user_posts_river,
users,
...current_user
})
);

export default connect(selector)(MyPostsToolPage);
10 changes: 8 additions & 2 deletions src/routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import {
Route,
IndexRoute,
Redirect
Redirect,
IndexRedirect
} from 'react-router';
import React from 'react';

Expand Down Expand Up @@ -48,8 +49,9 @@ import SchoolCloudPage from './pages/school-cloud';
import GeotagCloudPage from './pages/geotag-cloud';
import GeotagPage from './pages/geotag';
import GeotagEditPage from './pages/geotag-edit';
import SchoolsToolPage from './pages/tools/schools-tool';
import BaseToolsPage from './pages/base/tools';
import SchoolsToolPage from './pages/tools/schools-tool';
import MyPostsToolPage from './pages/tools/my-posts-tool';

import List from './pages/list';
import Induction from './pages/induction';
Expand Down Expand Up @@ -112,6 +114,10 @@ export function getRoutes(authHandler, fetchHandler) {
<Route component={BaseToolsPage} path="/tools">
<Redirect from="tags" to="schools" />
<Route component={SchoolsToolPage} path="schools" onEnter={withAuth} />
<Route path="my">
<IndexRedirect to="posts" />
<Route component={MyPostsToolPage} path="posts" onEnter={withAuth} />
</Route>
</Route>
</Route>
);
Expand Down
4 changes: 3 additions & 1 deletion src/store/posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export default function reducer(state = initialState, action) {
case a.hashtags.SET_HASHTAG_POSTS:
case a.schools.SET_SCHOOL_POSTS:
case a.geotags.SET_GEOTAG_POSTS:
case a.posts.SET_RELATED_POSTS: {
case a.posts.SET_RELATED_POSTS:
case a.tools.TOOLS__ADD_USER_POSTS_TO_RIVER:
case a.tools.TOOLS__SET_USER_POSTS_RIVER: {
const postsWithoutUsers = _.keyBy(action.posts.map(post => {
const postCopy = _.cloneDeep(post);

Expand Down
14 changes: 13 additions & 1 deletion src/store/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import { tools } from '../actions';


const initialState = i.fromJS({
schools_river: []
schools_river: [],
user_posts_river: []
});

export default function reducer(state = initialState, action) {
Expand All @@ -36,6 +37,17 @@ export default function reducer(state = initialState, action) {
state = state.set('schools_river', i.List(action.schools.map(school => school.id)));
break;
}

case tools.TOOLS__ADD_USER_POSTS_TO_RIVER: {
const ids = i.List(action.posts.map(post => post.id));
state = state.update('user_posts_river', user_posts_river => user_posts_river.concat(ids));
break;
}

case tools.TOOLS__SET_USER_POSTS_RIVER: {
state = state.set('user_posts_river', i.List(action.posts.map(posts => posts.id)));
break;
}
}

return state;
Expand Down
21 changes: 21 additions & 0 deletions src/triggers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,27 @@ export class ActionsTrigger {
return result;
}

toolsLoadUserPostsRiver = async (userName, query = {}) => {
this.dispatch(a.ui.setProgress('loadingUserPostsRiver', true));

let result;
try {
result = await this.client.userPosts(userName, Object.assign(query));

if (!query.offset) {
this.dispatch(a.tools.setUserPostsRiver(result));
} else {
this.dispatch(a.tools.addUserPostsToRiver(result));
}
} catch (e) {
this.dispatch(a.messages.addError(e.message));
}

this.dispatch(a.ui.setProgress('loadingUserPostsRiver', false));

return result;
};

loadInitialSuggestions = async () => {
try {
const result = await this.client.initialSuggestions();
Expand Down
7 changes: 7 additions & 0 deletions src/utils/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,12 @@ export const toolsMenu = new MenuTree([
children: [
{ name: 'Schools', path: '/tools/schools' }
]
},
{
name: 'My',
path: '/tools/my',
children: [
{ name: 'Posts', path: '/tools/my/posts' }
]
}
]);

0 comments on commit 92dae7d

Please sign in to comment.