Merge branch 'develop' into tests-tests-tests

This commit is contained in:
William O'Beirne 2018-12-01 01:30:46 -05:00 committed by GitHub
commit 4d0a6f2cf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 540 additions and 450 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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();

View File

@ -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: {

View File

@ -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,
},
);

View File

@ -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,
},
];
};

View File

@ -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, {

View File

@ -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',

View File

@ -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

View File

@ -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));
}
},
},

View File

@ -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