Merge branch 'develop' into tests-tests-tests
This commit is contained in:
commit
4d0a6f2cf2
|
@ -1,4 +1,5 @@
|
|||
import datetime
|
||||
from sqlalchemy.orm import raiseload
|
||||
|
||||
from grant.extensions import ma, db
|
||||
from grant.utils.misc import dt_to_unix
|
||||
|
@ -25,6 +26,14 @@ class Comment(db.Model):
|
|||
self.content = content
|
||||
self.date_created = datetime.datetime.now()
|
||||
|
||||
@staticmethod
|
||||
def get_by_user(user):
|
||||
return Comment.query \
|
||||
.options(raiseload(Comment.replies)) \
|
||||
.filter(Comment.user_id == user.id) \
|
||||
.order_by(Comment.date_created.desc()) \
|
||||
.all()
|
||||
|
||||
|
||||
class CommentSchema(ma.Schema):
|
||||
class Meta:
|
||||
|
@ -50,3 +59,35 @@ class CommentSchema(ma.Schema):
|
|||
|
||||
comment_schema = CommentSchema()
|
||||
comments_schema = CommentSchema(many=True)
|
||||
|
||||
|
||||
class UserCommentSchema(ma.Schema):
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = (
|
||||
"id",
|
||||
"proposal",
|
||||
"content",
|
||||
"date_created",
|
||||
)
|
||||
proposal = ma.Nested(
|
||||
"ProposalSchema",
|
||||
exclude=[
|
||||
"comments",
|
||||
"contributions",
|
||||
"team",
|
||||
"milestones",
|
||||
"content",
|
||||
"invites",
|
||||
"trustees",
|
||||
"updates"
|
||||
]
|
||||
)
|
||||
date_created = ma.Method("get_date_created")
|
||||
|
||||
def get_date_created(self, obj):
|
||||
return dt_to_unix(obj.date_created) * 1000
|
||||
|
||||
|
||||
user_comment_schema = UserCommentSchema()
|
||||
user_comments_schema = UserCommentSchema(many=True)
|
||||
|
|
|
@ -32,6 +32,7 @@ proposal_team = db.Table(
|
|||
db.Column('proposal_id', db.Integer, db.ForeignKey('proposal.id'))
|
||||
)
|
||||
|
||||
|
||||
class ProposalTeamInvite(db.Model):
|
||||
__tablename__ = "proposal_team_invite"
|
||||
|
||||
|
@ -141,8 +142,8 @@ class Proposal(db.Model):
|
|||
target: str = '0',
|
||||
payout_address: str = '',
|
||||
trustees: List[str] = [],
|
||||
deadline_duration: int = 5184000, # 60 days
|
||||
vote_duration: int = 604800, # 7 days
|
||||
deadline_duration: int = 5184000, # 60 days
|
||||
vote_duration: int = 604800, # 7 days
|
||||
proposal_address: str = None,
|
||||
category: str = ''
|
||||
):
|
||||
|
@ -178,7 +179,22 @@ class Proposal(db.Model):
|
|||
return Proposal(
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_by_user(user):
|
||||
return Proposal.query \
|
||||
.join(proposal_team) \
|
||||
.filter(proposal_team.c.user_id == user.id) \
|
||||
.all()
|
||||
|
||||
@staticmethod
|
||||
def get_by_user_contribution(user):
|
||||
return Proposal.query \
|
||||
.join(ProposalContribution) \
|
||||
.filter(ProposalContribution.user_id == user.id) \
|
||||
.order_by(ProposalContribution.date_created.desc()) \
|
||||
.all()
|
||||
|
||||
def update(
|
||||
self,
|
||||
title: str = '',
|
||||
|
@ -188,8 +204,8 @@ class Proposal(db.Model):
|
|||
target: str = '0',
|
||||
payout_address: str = '',
|
||||
trustees: List[str] = [],
|
||||
deadline_duration: int = 5184000, # 60 days
|
||||
vote_duration: int = 604800 # 7 days
|
||||
deadline_duration: int = 5184000, # 60 days
|
||||
vote_duration: int = 604800 # 7 days
|
||||
):
|
||||
self.title = title
|
||||
self.brief = brief
|
||||
|
@ -202,7 +218,6 @@ class Proposal(db.Model):
|
|||
self.vote_duration = vote_duration
|
||||
Proposal.validate(vars(self))
|
||||
|
||||
|
||||
def publish(self):
|
||||
# Require certain fields
|
||||
if not self.title:
|
||||
|
@ -216,8 +231,6 @@ class Proposal(db.Model):
|
|||
Proposal.validate(vars(self))
|
||||
self.status = 'LIVE'
|
||||
|
||||
|
||||
|
||||
|
||||
class ProposalSchema(ma.Schema):
|
||||
class Meta:
|
||||
|
@ -315,10 +328,13 @@ class ProposalTeamInviteSchema(ma.Schema):
|
|||
def get_date_created(self, obj):
|
||||
return dt_to_unix(obj.date_created)
|
||||
|
||||
|
||||
proposal_team_invite_schema = ProposalTeamInviteSchema()
|
||||
proposal_team_invites_schema = ProposalTeamInviteSchema(many=True)
|
||||
|
||||
# TODO: Find a way to extend ProposalTeamInviteSchema instead of redefining
|
||||
|
||||
|
||||
class InviteWithProposalSchema(ma.Schema):
|
||||
class Meta:
|
||||
model = ProposalTeamInvite
|
||||
|
@ -336,6 +352,7 @@ class InviteWithProposalSchema(ma.Schema):
|
|||
def get_date_created(self, obj):
|
||||
return dt_to_unix(obj.date_created)
|
||||
|
||||
|
||||
invite_with_proposal_schema = InviteWithProposalSchema()
|
||||
invites_with_proposal_schema = InviteWithProposalSchema(many=True)
|
||||
|
||||
|
@ -362,5 +379,33 @@ class ProposalContributionSchema(ma.Schema):
|
|||
def get_date_created(self, obj):
|
||||
return dt_to_unix(obj.date_created)
|
||||
|
||||
|
||||
proposal_contribution_schema = ProposalContributionSchema()
|
||||
proposals_contribution_schema = ProposalContributionSchema(many=True)
|
||||
|
||||
|
||||
class UserProposalSchema(ma.Schema):
|
||||
class Meta:
|
||||
model = Proposal
|
||||
# Fields to expose
|
||||
fields = (
|
||||
"proposal_id",
|
||||
"proposal_address",
|
||||
"title",
|
||||
"brief",
|
||||
"date_created",
|
||||
"team",
|
||||
)
|
||||
date_created = ma.Method("get_date_created")
|
||||
proposal_id = ma.Method("get_proposal_id")
|
||||
team = ma.Nested("UserSchema", many=True)
|
||||
|
||||
def get_proposal_id(self, obj):
|
||||
return obj.id
|
||||
|
||||
def get_date_created(self, obj):
|
||||
return dt_to_unix(obj.date_created) * 1000
|
||||
|
||||
|
||||
user_proposal_schema = UserProposalSchema()
|
||||
user_proposals_schema = UserProposalSchema(many=True)
|
||||
|
|
|
@ -1,15 +1,36 @@
|
|||
from flask import Blueprint, g, request
|
||||
from flask_yoloapi import endpoint, parameter
|
||||
|
||||
from grant.proposal.models import Proposal, proposal_team, ProposalTeamInvite, invites_with_proposal_schema
|
||||
from grant.comment.models import Comment, user_comments_schema
|
||||
from grant.proposal.models import (
|
||||
Proposal,
|
||||
proposals_schema,
|
||||
proposal_team,
|
||||
ProposalTeamInvite,
|
||||
invites_with_proposal_schema,
|
||||
user_proposals_schema
|
||||
)
|
||||
from grant.utils.auth import requires_sm, requires_same_user_auth, verify_signed_auth, BadSignatureException
|
||||
from grant.utils.upload import save_avatar, send_upload, remove_avatar
|
||||
from grant.web3.proposal import read_user_proposal
|
||||
from grant.settings import UPLOAD_URL
|
||||
from .models import User, SocialMedia, Avatar, users_schema, user_schema, db
|
||||
|
||||
blueprint = Blueprint('user', __name__, url_prefix='/api/v1/users')
|
||||
|
||||
|
||||
def populate_user_proposals_cfs(proposals):
|
||||
for p in proposals:
|
||||
proposal_contract = read_user_proposal(p['proposal_address'])
|
||||
if proposal_contract:
|
||||
p['target'] = proposal_contract['target']
|
||||
p['funded'] = proposal_contract['funded']
|
||||
else:
|
||||
p['target'] = None
|
||||
filtered_proposals = list(filter(lambda p: p['target'] is not None, proposals))
|
||||
return filtered_proposals
|
||||
|
||||
|
||||
@blueprint.route("/", methods=["GET"])
|
||||
@endpoint.api(
|
||||
parameter('proposalId', type=str, required=False)
|
||||
|
@ -39,11 +60,27 @@ def get_me():
|
|||
|
||||
|
||||
@blueprint.route("/<user_identity>", methods=["GET"])
|
||||
@endpoint.api()
|
||||
def get_user(user_identity):
|
||||
@endpoint.api(
|
||||
parameter("withProposals", type=bool, required=False),
|
||||
parameter("withComments", type=bool, required=False),
|
||||
parameter("withFunded", type=bool, required=False)
|
||||
)
|
||||
def get_user(user_identity, with_proposals, with_comments, with_funded):
|
||||
user = User.get_by_identifier(email_address=user_identity, account_address=user_identity)
|
||||
if user:
|
||||
result = user_schema.dump(user)
|
||||
if with_proposals:
|
||||
proposals = Proposal.get_by_user(user)
|
||||
proposals_dump = user_proposals_schema.dump(proposals)
|
||||
result["createdProposals"] = populate_user_proposals_cfs(proposals_dump)
|
||||
if with_funded:
|
||||
contributions = Proposal.get_by_user_contribution(user)
|
||||
contributions_dump = user_proposals_schema.dump(contributions)
|
||||
result["fundedProposals"] = populate_user_proposals_cfs(contributions_dump)
|
||||
if with_comments:
|
||||
comments = Comment.get_by_user(user)
|
||||
comments_dump = user_comments_schema.dump(comments)
|
||||
result["comments"] = comments_dump
|
||||
return result
|
||||
else:
|
||||
message = "User with account_address or user_identity matching {} not found".format(user_identity)
|
||||
|
@ -194,6 +231,7 @@ def update_user(user_identity, display_name, title, social_medias, avatar):
|
|||
result = user_schema.dump(user)
|
||||
return result
|
||||
|
||||
|
||||
@blueprint.route("/<user_identity>/invites", methods=["GET"])
|
||||
@requires_same_user_auth
|
||||
@endpoint.api()
|
||||
|
@ -201,6 +239,7 @@ def get_user_invites(user_identity):
|
|||
invites = ProposalTeamInvite.get_pending_for_user(g.current_user)
|
||||
return invites_with_proposal_schema.dump(invites)
|
||||
|
||||
|
||||
@blueprint.route("/<user_identity>/invites/<invite_id>/respond", methods=["PUT"])
|
||||
@requires_same_user_auth
|
||||
@endpoint.api(
|
||||
|
@ -210,13 +249,13 @@ def respond_to_invite(user_identity, invite_id, response):
|
|||
invite = ProposalTeamInvite.query.filter_by(id=invite_id).first()
|
||||
if not invite:
|
||||
return {"message": "No invite found with id {}".format(invite_id)}, 404
|
||||
|
||||
|
||||
invite.accepted = response
|
||||
db.session.add(invite)
|
||||
|
||||
if invite.accepted:
|
||||
invite.proposal.team.append(g.current_user)
|
||||
db.session.add(invite)
|
||||
|
||||
|
||||
db.session.commit()
|
||||
return None, 200
|
||||
|
|
|
@ -21,7 +21,7 @@ def get_crowd_fund_abi():
|
|||
|
||||
def read_proposal(address):
|
||||
current_web3.eth.defaultAccount = '0x537680D921C000fC52Af9962ceEb4e359C50F424' if not current_web3.eth.accounts else \
|
||||
current_web3.eth.accounts[0]
|
||||
current_web3.eth.accounts[0]
|
||||
crowd_fund_abi = get_crowd_fund_abi()
|
||||
contract = current_web3.eth.contract(address=address, abi=crowd_fund_abi)
|
||||
|
||||
|
@ -134,6 +134,33 @@ def read_proposal(address):
|
|||
return crowd_fund
|
||||
|
||||
|
||||
def read_user_proposal(address):
|
||||
current_web3.eth.defaultAccount = '0x537680D921C000fC52Af9962ceEb4e359C50F424' if not current_web3.eth.accounts else \
|
||||
current_web3.eth.accounts[0]
|
||||
crowd_fund_abi = get_crowd_fund_abi()
|
||||
contract = current_web3.eth.contract(address=address, abi=crowd_fund_abi)
|
||||
|
||||
crowd_fund = {}
|
||||
methods = [
|
||||
"raiseGoal",
|
||||
]
|
||||
|
||||
# batched
|
||||
calls = list(map(lambda x: [x, None], methods))
|
||||
try:
|
||||
crowd_fund = batch_call(current_web3, address, crowd_fund_abi, calls, contract)
|
||||
# catch dead contracts here
|
||||
except RpcError:
|
||||
return None
|
||||
|
||||
# balance (sync)
|
||||
crowd_fund['balance'] = current_web3.eth.getBalance(address)
|
||||
crowd_fund['target'] = str(crowd_fund.pop('raiseGoal'))
|
||||
crowd_fund['funded'] = str(crowd_fund.pop('balance'))
|
||||
|
||||
return crowd_fund
|
||||
|
||||
|
||||
def validate_contribution_tx(tx_id, from_address, to_address, amount):
|
||||
amount_wei = current_web3.toWei(amount, 'ether')
|
||||
tx = current_web3.eth.getTransaction(tx_id)
|
||||
|
|
|
@ -91,6 +91,11 @@ const start = async () => {
|
|||
logMessage('An error occured attempting to run the server. Exiting', 'error');
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
console.log(' Dev exited, see you next time.');
|
||||
process.exit();
|
||||
});
|
||||
};
|
||||
|
||||
start();
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
TeamInviteWithProposal,
|
||||
Contribution,
|
||||
} from 'types';
|
||||
import { formatUserForPost, formatProposalFromGet } from 'utils/api';
|
||||
import { formatUserForPost, formatProposalFromGet, formatUserFromGet } from 'utils/api';
|
||||
|
||||
export function getProposals(): Promise<{ data: Proposal[] }> {
|
||||
return axios.get('/api/v1/proposals/').then(res => {
|
||||
|
@ -41,7 +41,18 @@ export function postProposal(payload: ProposalDraft) {
|
|||
}
|
||||
|
||||
export function getUser(address: string): Promise<{ data: User }> {
|
||||
return axios.get(`/api/v1/users/${address}`);
|
||||
return axios
|
||||
.get(`/api/v1/users/${address}`, {
|
||||
params: {
|
||||
withProposals: true,
|
||||
withComments: true,
|
||||
withFunded: true,
|
||||
},
|
||||
})
|
||||
.then(res => {
|
||||
res.data = formatUserFromGet(res.data);
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
export function createUser(payload: {
|
||||
|
|
|
@ -22,9 +22,6 @@ interface StateProps {
|
|||
|
||||
interface DispatchProps {
|
||||
fetchUser: typeof usersActions['fetchUser'];
|
||||
fetchUserCreated: typeof usersActions['fetchUserCreated'];
|
||||
fetchUserFunded: typeof usersActions['fetchUserFunded'];
|
||||
fetchUserComments: typeof usersActions['fetchUserComments'];
|
||||
fetchUserInvites: typeof usersActions['fetchUserInvites'];
|
||||
}
|
||||
|
||||
|
@ -68,14 +65,13 @@ class Profile extends React.Component<Props> {
|
|||
}
|
||||
|
||||
const { createdProposals, fundedProposals, comments, invites } = user;
|
||||
const noneCreated = user.hasFetchedCreated && createdProposals.length === 0;
|
||||
const noneFunded = user.hasFetchedFunded && fundedProposals.length === 0;
|
||||
const noneCommented = user.hasFetchedComments && comments.length === 0;
|
||||
const noneCreated = createdProposals.length === 0;
|
||||
const noneFunded = fundedProposals.length === 0;
|
||||
const noneCommented = comments.length === 0;
|
||||
const noneInvites = user.hasFetchedInvites && invites.length === 0;
|
||||
|
||||
return (
|
||||
<div className="Profile">
|
||||
{/* TODO: SSR fetch user details */}
|
||||
{/* TODO: customize details for funders/creators */}
|
||||
<HeaderDetails
|
||||
title={`${user.displayName} is funding projects on Grant.io`}
|
||||
|
@ -84,11 +80,7 @@ class Profile extends React.Component<Props> {
|
|||
/>
|
||||
<ProfileUser user={user} />
|
||||
<Tabs>
|
||||
<Tabs.TabPane
|
||||
tab={TabTitle('Created', createdProposals.length)}
|
||||
key="created"
|
||||
disabled={!user.hasFetchedCreated}
|
||||
>
|
||||
<Tabs.TabPane tab={TabTitle('Created', createdProposals.length)} key="created">
|
||||
<div>
|
||||
{noneCreated && (
|
||||
<Placeholder subtitle="Has not created any proposals yet" />
|
||||
|
@ -98,31 +90,19 @@ class Profile extends React.Component<Props> {
|
|||
))}
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={TabTitle('Funded', fundedProposals.length)}
|
||||
key="funded"
|
||||
disabled={!user.hasFetchedFunded}
|
||||
>
|
||||
<Tabs.TabPane tab={TabTitle('Funded', fundedProposals.length)} key="funded">
|
||||
<div>
|
||||
{noneFunded && <Placeholder subtitle="Has not funded any proposals yet" />}
|
||||
{createdProposals.map(p => (
|
||||
{fundedProposals.map(p => (
|
||||
<ProfileProposal key={p.proposalId} proposal={p} />
|
||||
))}
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={TabTitle('Comments', comments.length)}
|
||||
key="comments"
|
||||
disabled={!user.hasFetchedComments}
|
||||
>
|
||||
<Tabs.TabPane tab={TabTitle('Comments', comments.length)} key="comments">
|
||||
<div>
|
||||
{noneCommented && <Placeholder subtitle="Has not made any comments yet" />}
|
||||
{comments.map(c => (
|
||||
<ProfileComment
|
||||
key={c.commentId}
|
||||
userName={user.displayName}
|
||||
comment={c}
|
||||
/>
|
||||
<ProfileComment key={c.id} userName={user.displayName} comment={c} />
|
||||
))}
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
@ -158,9 +138,6 @@ class Profile extends React.Component<Props> {
|
|||
const userLookupId = match.params.id;
|
||||
if (userLookupId) {
|
||||
this.props.fetchUser(userLookupId);
|
||||
this.props.fetchUserCreated(userLookupId);
|
||||
this.props.fetchUserFunded(userLookupId);
|
||||
this.props.fetchUserComments(userLookupId);
|
||||
this.props.fetchUserInvites(userLookupId);
|
||||
}
|
||||
}
|
||||
|
@ -184,9 +161,6 @@ const withConnect = connect<StateProps, DispatchProps, {}, AppState>(
|
|||
}),
|
||||
{
|
||||
fetchUser: usersActions.fetchUser,
|
||||
fetchUserCreated: usersActions.fetchUserCreated,
|
||||
fetchUserFunded: usersActions.fetchUserFunded,
|
||||
fetchUserComments: usersActions.fetchUserComments,
|
||||
fetchUserInvites: usersActions.fetchUserInvites,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
import { UserProposal, UserComment, User } from 'types';
|
||||
import { User } from 'types';
|
||||
import types from './types';
|
||||
import {
|
||||
getUser,
|
||||
updateUser as apiUpdateUser,
|
||||
getProposals,
|
||||
fetchUserInvites as apiFetchUserInvites,
|
||||
putInviteResponse,
|
||||
} from 'api/api';
|
||||
import { Dispatch } from 'redux';
|
||||
import { Proposal } from 'types';
|
||||
import BN from 'bn.js';
|
||||
import { cleanClone } from 'utils/helpers';
|
||||
import { INITIAL_TEAM_MEMBER_STATE } from 'modules/users/reducers';
|
||||
|
||||
|
@ -44,68 +41,6 @@ export function updateUser(user: User) {
|
|||
};
|
||||
}
|
||||
|
||||
export function fetchUserCreated(userFetchId: string) {
|
||||
return async (dispatch: Dispatch<any>) => {
|
||||
dispatch({ type: types.FETCH_USER_CREATED_PENDING, payload: { userFetchId } });
|
||||
try {
|
||||
// temporary, grab all proposals
|
||||
const proposalsRes = await getProposals();
|
||||
const proposals = proposalsRes.data.map(mockModifyProposals);
|
||||
dispatch({
|
||||
type: types.FETCH_USER_CREATED_FULFILLED,
|
||||
payload: { userFetchId, proposals },
|
||||
});
|
||||
} catch (error) {
|
||||
dispatch({
|
||||
type: types.FETCH_USER_CREATED_REJECTED,
|
||||
payload: { userFetchId, error },
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchUserFunded(userFetchId: string) {
|
||||
return async (dispatch: Dispatch<any>) => {
|
||||
dispatch({ type: types.FETCH_USER_FUNDED_PENDING, payload: { userFetchId } });
|
||||
try {
|
||||
// temporary, grab all proposals
|
||||
const proposalsRes = await getProposals();
|
||||
const proposals = proposalsRes.data.map(mockModifyProposals);
|
||||
dispatch({
|
||||
type: types.FETCH_USER_FUNDED_FULFILLED,
|
||||
payload: { userFetchId, proposals },
|
||||
});
|
||||
} catch (error) {
|
||||
dispatch({
|
||||
type: types.FETCH_USER_FUNDED_REJECTED,
|
||||
payload: { userFetchId, error },
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchUserComments(userFetchId: string) {
|
||||
return async (dispatch: Dispatch<any>) => {
|
||||
dispatch({ type: types.FETCH_USER_COMMENTS_PENDING, payload: { userFetchId } });
|
||||
try {
|
||||
// temporary, grab all proposals, mock comments
|
||||
const proposalsRes = await getProposals();
|
||||
const proposals = proposalsRes.data.map(mockModifyProposals);
|
||||
const comments = mockComments(proposals);
|
||||
comments.sort((a, b) => (a.dateCreated > b.dateCreated ? -1 : 1));
|
||||
dispatch({
|
||||
type: types.FETCH_USER_COMMENTS_FULFILLED,
|
||||
payload: { userFetchId, comments },
|
||||
});
|
||||
} catch (error) {
|
||||
dispatch({
|
||||
type: types.FETCH_USER_COMMENTS_REJECTED,
|
||||
payload: { userFetchId, error },
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchUserInvites(userFetchId: string) {
|
||||
return async (dispatch: Dispatch<any>) => {
|
||||
dispatch({
|
||||
|
@ -154,72 +89,3 @@ export function respondToInvite(
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
const mockModifyProposals = (p: Proposal): UserProposal => {
|
||||
const { proposalId, title, team } = p;
|
||||
return {
|
||||
proposalId,
|
||||
title,
|
||||
team,
|
||||
funded: new BN('5000000000000000000'),
|
||||
target: new BN('10000000000000000000'),
|
||||
brief: genBrief(title),
|
||||
};
|
||||
};
|
||||
|
||||
const genBrief = (title: string) => {
|
||||
return title.indexOf('T-Shirts') > -1
|
||||
? 'Stylish, classy logo tees for Grant.io! Show everyone your love for the future of crowdfunding!'
|
||||
: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
|
||||
};
|
||||
|
||||
const mockComments = (ps: UserProposal[]): UserComment[] => {
|
||||
return ps.reduce((a: UserComment[], p) => a.concat(mockComment(p)), []);
|
||||
};
|
||||
|
||||
const mockComment = (p: UserProposal): UserComment[] => {
|
||||
return p.title.indexOf('T-Shirts') > -1
|
||||
? [
|
||||
{
|
||||
commentId: Math.random(),
|
||||
content: "I can't WAIT to get my t-shirt!",
|
||||
dateCreated: Date.now() - Math.floor(Math.random() * 1000 * 60 * 60 * 24 * 30),
|
||||
proposal: p,
|
||||
},
|
||||
{
|
||||
commentId: Math.random(),
|
||||
content: 'I love the new design. Will they still be available next month?',
|
||||
dateCreated: Date.now() - Math.floor(Math.random() * 1000 * 60 * 60 * 24 * 30),
|
||||
proposal: p,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
commentId: Math.random(),
|
||||
content: 'Ut labore et dolore magna aliqua.',
|
||||
dateCreated: Date.now() - Math.floor(Math.random() * 1000 * 60 * 60 * 24 * 30),
|
||||
proposal: p,
|
||||
},
|
||||
{
|
||||
commentId: Math.random(),
|
||||
content:
|
||||
'Adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
|
||||
dateCreated: Date.now() - Math.floor(Math.random() * 1000 * 60 * 60 * 24 * 30),
|
||||
proposal: p,
|
||||
},
|
||||
{
|
||||
commentId: Math.random(),
|
||||
content:
|
||||
'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
dateCreated: Date.now() - Math.floor(Math.random() * 1000 * 60 * 60 * 24 * 30),
|
||||
proposal: p,
|
||||
},
|
||||
{
|
||||
commentId: Math.random(),
|
||||
content:
|
||||
'Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.',
|
||||
dateCreated: Date.now() - Math.floor(Math.random() * 1000 * 60 * 60 * 24 * 30),
|
||||
proposal: p,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
|
|
@ -14,17 +14,8 @@ export interface UserState extends User {
|
|||
fetchError: number | null;
|
||||
isUpdating: boolean;
|
||||
updateError: number | null;
|
||||
isFetchingCreated: boolean;
|
||||
hasFetchedCreated: boolean;
|
||||
fetchErrorCreated: number | null;
|
||||
createdProposals: UserProposal[];
|
||||
isFetchingFunded: boolean;
|
||||
hasFetchedFunded: boolean;
|
||||
fetchErrorFunded: number | null;
|
||||
fundedProposals: UserProposal[];
|
||||
isFetchingComments: boolean;
|
||||
hasFetchedComments: boolean;
|
||||
fetchErrorComments: number | null;
|
||||
comments: UserComment[];
|
||||
isFetchingInvites: boolean;
|
||||
hasFetchedInvites: boolean;
|
||||
|
@ -53,17 +44,8 @@ export const INITIAL_USER_STATE: UserState = {
|
|||
fetchError: null,
|
||||
isUpdating: false,
|
||||
updateError: null,
|
||||
isFetchingCreated: false,
|
||||
hasFetchedCreated: false,
|
||||
fetchErrorCreated: null,
|
||||
createdProposals: [],
|
||||
isFetchingFunded: false,
|
||||
hasFetchedFunded: false,
|
||||
fetchErrorFunded: null,
|
||||
fundedProposals: [],
|
||||
isFetchingComments: false,
|
||||
hasFetchedComments: false,
|
||||
fetchErrorComments: null,
|
||||
comments: [],
|
||||
isFetchingInvites: false,
|
||||
hasFetchedInvites: false,
|
||||
|
@ -78,8 +60,6 @@ export const INITIAL_STATE: UsersState = {
|
|||
export default (state = INITIAL_STATE, action: any) => {
|
||||
const { payload } = action;
|
||||
const userFetchId = payload && payload.userFetchId;
|
||||
const proposals = payload && payload.proposals;
|
||||
const comments = payload && payload.comments;
|
||||
const invites = payload && payload.invites;
|
||||
const errorStatus =
|
||||
(payload &&
|
||||
|
@ -122,60 +102,6 @@ export default (state = INITIAL_STATE, action: any) => {
|
|||
isUpdating: false,
|
||||
updateError: errorStatus,
|
||||
});
|
||||
// created proposals
|
||||
case types.FETCH_USER_CREATED_PENDING:
|
||||
return updateUserState(state, userFetchId, {
|
||||
isFetchingCreated: true,
|
||||
fetchErrorCreated: null,
|
||||
});
|
||||
case types.FETCH_USER_CREATED_FULFILLED:
|
||||
return updateUserState(state, userFetchId, {
|
||||
isFetchingCreated: false,
|
||||
hasFetchedCreated: true,
|
||||
createdProposals: proposals,
|
||||
});
|
||||
case types.FETCH_USER_CREATED_REJECTED:
|
||||
return updateUserState(state, userFetchId, {
|
||||
isFetchingCreated: false,
|
||||
hasFetchedCreated: true,
|
||||
fetchErrorCreated: errorStatus,
|
||||
});
|
||||
// funded proposals
|
||||
case types.FETCH_USER_FUNDED_PENDING:
|
||||
return updateUserState(state, userFetchId, {
|
||||
isFetchingFunded: true,
|
||||
fetchErrorFunded: null,
|
||||
});
|
||||
case types.FETCH_USER_FUNDED_FULFILLED:
|
||||
return updateUserState(state, userFetchId, {
|
||||
isFetchingFunded: false,
|
||||
hasFetchedFunded: true,
|
||||
fundedProposals: proposals,
|
||||
});
|
||||
case types.FETCH_USER_FUNDED_REJECTED:
|
||||
return updateUserState(state, userFetchId, {
|
||||
isFetchingFunded: false,
|
||||
hasFetchedFunded: true,
|
||||
fetchErrorFunded: errorStatus,
|
||||
});
|
||||
// comments
|
||||
case types.FETCH_USER_COMMENTS_PENDING:
|
||||
return updateUserState(state, userFetchId, {
|
||||
isFetchingComments: true,
|
||||
fetchErrorComments: null,
|
||||
});
|
||||
case types.FETCH_USER_COMMENTS_FULFILLED:
|
||||
return updateUserState(state, userFetchId, {
|
||||
isFetchingComments: false,
|
||||
hasFetchedComments: true,
|
||||
comments,
|
||||
});
|
||||
case types.FETCH_USER_COMMENTS_REJECTED:
|
||||
return updateUserState(state, userFetchId, {
|
||||
isFetchingComments: false,
|
||||
hasFetchedComments: true,
|
||||
fetchErrorComments: errorStatus,
|
||||
});
|
||||
// invites
|
||||
case types.FETCH_USER_INVITES_PENDING:
|
||||
return updateUserState(state, userFetchId, {
|
||||
|
|
|
@ -9,21 +9,6 @@ enum UsersActions {
|
|||
UPDATE_USER_FULFILLED = 'UPDATE_USER_FULFILLED',
|
||||
UPDATE_USER_REJECTED = 'UPDATE_USER_REJECTED',
|
||||
|
||||
FETCH_USER_CREATED = 'FETCH_USER_CREATED',
|
||||
FETCH_USER_CREATED_PENDING = 'FETCH_USER_CREATED_PENDING',
|
||||
FETCH_USER_CREATED_FULFILLED = 'FETCH_USER_CREATED_FULFILLED',
|
||||
FETCH_USER_CREATED_REJECTED = 'FETCH_USER_CREATED_REJECTED',
|
||||
|
||||
FETCH_USER_FUNDED = 'FETCH_USER_FUNDED',
|
||||
FETCH_USER_FUNDED_PENDING = 'FETCH_USER_FUNDED_PENDING',
|
||||
FETCH_USER_FUNDED_FULFILLED = 'FETCH_USER_FUNDED_FULFILLED',
|
||||
FETCH_USER_FUNDED_REJECTED = 'FETCH_USER_FUNDED_REJECTED',
|
||||
|
||||
FETCH_USER_COMMENTS = 'FETCH_USER_COMMENTS',
|
||||
FETCH_USER_COMMENTS_PENDING = 'FETCH_USER_COMMENTS_PENDING',
|
||||
FETCH_USER_COMMENTS_FULFILLED = 'FETCH_USER_COMMENTS_FULFILLED',
|
||||
FETCH_USER_COMMENTS_REJECTED = 'FETCH_USER_COMMENTS_REJECTED',
|
||||
|
||||
FETCH_USER_INVITES = 'FETCH_USER_INVITES',
|
||||
FETCH_USER_INVITES_PENDING = 'FETCH_USER_INVITES_PENDING',
|
||||
FETCH_USER_INVITES_FULFILLED = 'FETCH_USER_INVITES_FULFILLED',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import BN from 'bn.js';
|
||||
import { socialMediaToUrl } from 'utils/social';
|
||||
import { User, CrowdFund, ProposalWithCrowdFund, UserProposal } from 'types';
|
||||
import { UserState } from 'modules/users/reducers';
|
||||
import { AppState } from 'store/reducers';
|
||||
|
||||
export function formatUserForPost(user: User) {
|
||||
|
@ -11,6 +12,17 @@ export function formatUserForPost(user: User) {
|
|||
};
|
||||
}
|
||||
|
||||
export function formatUserFromGet(user: UserState) {
|
||||
const bnUserProp = (p: UserProposal) => {
|
||||
p.funded = new BN(p.funded);
|
||||
p.target = new BN(p.target);
|
||||
return p;
|
||||
};
|
||||
user.createdProposals = user.createdProposals.map(bnUserProp);
|
||||
user.fundedProposals = user.fundedProposals.map(bnUserProp);
|
||||
return user;
|
||||
}
|
||||
|
||||
export function formatCrowdFundFromGet(crowdFund: CrowdFund, base = 10): CrowdFund {
|
||||
const bnKeys = ['amountVotingForRefund', 'balance', 'funded', 'target'] as Array<
|
||||
keyof CrowdFund
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import { Store } from 'redux';
|
||||
import { fetchUser } from 'modules/users/actions';
|
||||
import { fetchProposals, fetchProposal } from 'modules/proposals/actions';
|
||||
import {
|
||||
fetchUser,
|
||||
fetchUserCreated,
|
||||
fetchUserFunded,
|
||||
fetchUserComments,
|
||||
} from 'modules/users/actions';
|
||||
import { extractProposalIdFromUrl } from 'utils/api';
|
||||
|
||||
const pathActions = [
|
||||
|
@ -29,12 +24,7 @@ const pathActions = [
|
|||
action: (match: RegExpMatchArray, store: Store) => {
|
||||
const userId = match[1];
|
||||
if (userId) {
|
||||
return Promise.all([
|
||||
store.dispatch<any>(fetchUser(userId)),
|
||||
store.dispatch<any>(fetchUserCreated(userId)),
|
||||
store.dispatch<any>(fetchUserFunded(userId)),
|
||||
store.dispatch<any>(fetchUserComments(userId)),
|
||||
]);
|
||||
return store.dispatch<any>(fetchUser(userId));
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@ export interface Comment {
|
|||
}
|
||||
|
||||
export interface UserComment {
|
||||
commentId: number | string;
|
||||
id: number | string;
|
||||
content: string;
|
||||
dateCreated: number;
|
||||
proposal: UserProposal;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue