2018-11-14 14:24:56 -08:00
|
|
|
|
import BN from 'bn.js';
|
2019-02-05 12:34:19 -08:00
|
|
|
|
import {
|
|
|
|
|
User,
|
|
|
|
|
Proposal,
|
|
|
|
|
ProposalPageParams,
|
|
|
|
|
PageParams,
|
|
|
|
|
UserProposal,
|
|
|
|
|
RFP,
|
|
|
|
|
ProposalPage,
|
|
|
|
|
} from 'types';
|
2018-11-30 15:52:00 -08:00
|
|
|
|
import { UserState } from 'modules/users/reducers';
|
2018-11-21 19:17:49 -08:00
|
|
|
|
import { AppState } from 'store/reducers';
|
2018-12-27 10:00:04 -08:00
|
|
|
|
import { toZat } from './units';
|
2018-09-27 13:39:37 -07:00
|
|
|
|
|
2018-11-16 15:05:17 -08:00
|
|
|
|
export function formatUserForPost(user: User) {
|
2018-09-27 13:39:37 -07:00
|
|
|
|
return {
|
2018-11-16 15:05:17 -08:00
|
|
|
|
...user,
|
2018-11-26 17:14:00 -08:00
|
|
|
|
avatar: user.avatar ? user.avatar.imageUrl : null,
|
2018-09-27 13:39:37 -07:00
|
|
|
|
};
|
|
|
|
|
}
|
2018-11-07 09:33:19 -08:00
|
|
|
|
|
2018-11-30 15:52:00 -08:00
|
|
|
|
export function formatUserFromGet(user: UserState) {
|
2019-01-09 10:23:08 -08:00
|
|
|
|
const bnUserProp = (p: any) => {
|
|
|
|
|
p.funded = toZat(p.funded);
|
|
|
|
|
p.target = toZat(p.target);
|
2018-11-30 15:52:00 -08:00
|
|
|
|
return p;
|
|
|
|
|
};
|
2019-01-09 10:23:08 -08:00
|
|
|
|
if (user.pendingProposals) {
|
|
|
|
|
user.pendingProposals = user.pendingProposals.map(bnUserProp);
|
|
|
|
|
}
|
2019-02-06 14:37:45 -08:00
|
|
|
|
if (user.arbitrated) {
|
2019-02-09 19:03:19 -08:00
|
|
|
|
user.arbitrated = user.arbitrated.map(a => {
|
|
|
|
|
a.proposal = bnUserProp(a.proposal);
|
|
|
|
|
return a;
|
|
|
|
|
});
|
2019-02-06 14:37:45 -08:00
|
|
|
|
}
|
2019-01-09 13:57:15 -08:00
|
|
|
|
user.proposals = user.proposals.map(bnUserProp);
|
2019-01-09 11:07:50 -08:00
|
|
|
|
user.contributions = user.contributions.map(c => {
|
2019-01-29 15:50:27 -08:00
|
|
|
|
c.amount = toZat((c.amount as any) as string);
|
2019-01-09 11:07:50 -08:00
|
|
|
|
return c;
|
|
|
|
|
});
|
2018-11-30 15:52:00 -08:00
|
|
|
|
return user;
|
|
|
|
|
}
|
2019-02-05 12:34:19 -08:00
|
|
|
|
// NOTE: sync with pagination.py ProposalPagination.SORT_MAP
|
|
|
|
|
const proposalsSortMap = {
|
|
|
|
|
NEWEST: 'PUBLISHED:DESC',
|
|
|
|
|
OLDEST: 'PUBLISHED:ASC',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function formatProposalPageParamsForGet(params: ProposalPageParams): PageParams {
|
|
|
|
|
return {
|
|
|
|
|
...params,
|
|
|
|
|
sort: proposalsSortMap[params.sort],
|
|
|
|
|
filters: [
|
|
|
|
|
...params.filters.category.map(c => 'CAT_' + c),
|
|
|
|
|
...params.filters.stage.map(s => 'STAGE_' + s),
|
|
|
|
|
],
|
|
|
|
|
} as PageParams;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function formatProposalPageFromGet(page: any): ProposalPage {
|
|
|
|
|
page.items = page.items.map(formatProposalFromGet);
|
|
|
|
|
const swf = (sw: string, a: string[]) =>
|
|
|
|
|
a.filter(x => x.startsWith(sw)).map(x => x.replace(sw, ''));
|
|
|
|
|
page.filters = {
|
|
|
|
|
category: swf('CAT_', page.filters),
|
|
|
|
|
stage: swf('STAGE_', page.filters),
|
|
|
|
|
};
|
|
|
|
|
// reverse map
|
|
|
|
|
const serverSortToClient = Object.entries(proposalsSortMap).find(
|
|
|
|
|
([_, v]) => v === page.sort,
|
|
|
|
|
);
|
|
|
|
|
if (!serverSortToClient) {
|
|
|
|
|
throw Error(
|
|
|
|
|
`formatProposalFromGet Unable to find mapping from server proposal sort: ${
|
|
|
|
|
page.sort
|
|
|
|
|
}`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
page.sort = serverSortToClient[0];
|
|
|
|
|
return page as ProposalPage;
|
|
|
|
|
}
|
2018-11-30 15:52:00 -08:00
|
|
|
|
|
2018-12-27 10:37:30 -08:00
|
|
|
|
export function formatProposalFromGet(p: any): Proposal {
|
|
|
|
|
const proposal = { ...p } as Proposal;
|
2019-01-30 09:59:15 -08:00
|
|
|
|
proposal.proposalUrlId = generateSlugUrl(proposal.proposalId, proposal.title);
|
2018-12-27 10:37:30 -08:00
|
|
|
|
proposal.target = toZat(p.target);
|
|
|
|
|
proposal.funded = toZat(p.funded);
|
2019-03-06 12:25:58 -08:00
|
|
|
|
proposal.contributionBounty = toZat(p.contributionBounty);
|
2019-01-09 11:07:50 -08:00
|
|
|
|
proposal.percentFunded = proposal.target.isZero()
|
|
|
|
|
? 0
|
|
|
|
|
: proposal.funded.div(proposal.target.divn(100)).toNumber();
|
|
|
|
|
if (proposal.milestones) {
|
2019-02-11 13:22:40 -08:00
|
|
|
|
const msToFe = (m: any) => ({
|
|
|
|
|
...m,
|
|
|
|
|
amount: proposal.target.mul(new BN(m.payoutPercent)).divn(100),
|
2019-01-09 11:07:50 -08:00
|
|
|
|
});
|
2019-02-11 13:22:40 -08:00
|
|
|
|
proposal.milestones = proposal.milestones.map(msToFe);
|
|
|
|
|
proposal.currentMilestone = msToFe(proposal.currentMilestone);
|
2019-01-09 11:07:50 -08:00
|
|
|
|
}
|
2019-02-16 09:32:26 -08:00
|
|
|
|
if (proposal.rfp) {
|
|
|
|
|
proposal.rfp = formatRFPFromGet(proposal.rfp);
|
|
|
|
|
}
|
2018-11-14 14:24:56 -08:00
|
|
|
|
return proposal;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-01 11:13:30 -08:00
|
|
|
|
export function formatRFPFromGet(rfp: RFP): RFP {
|
2019-03-13 22:06:02 -07:00
|
|
|
|
rfp.urlId = generateSlugUrl(rfp.id, rfp.title);
|
2019-02-08 11:02:34 -08:00
|
|
|
|
if (rfp.bounty) {
|
|
|
|
|
rfp.bounty = toZat(rfp.bounty as any);
|
|
|
|
|
}
|
2019-02-16 09:32:26 -08:00
|
|
|
|
if (rfp.acceptedProposals) {
|
|
|
|
|
rfp.acceptedProposals = rfp.acceptedProposals.map(formatProposalFromGet);
|
|
|
|
|
}
|
2019-01-30 09:59:15 -08:00
|
|
|
|
return rfp;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 14:39:50 -07:00
|
|
|
|
// NOTE: i18n on case-by-case basis
|
2019-01-30 09:59:15 -08:00
|
|
|
|
export function generateSlugUrl(id: number, title: string) {
|
2018-11-07 09:33:19 -08:00
|
|
|
|
const slug = title
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
.replace(/[\s_]+/g, '-')
|
|
|
|
|
.replace(/[’'"]+/g, '')
|
|
|
|
|
.replace(/[^\w\-]+/g, '-')
|
|
|
|
|
.replace(/\-{2,}/g, '-')
|
|
|
|
|
.replace(/^\-*|\-*$/g, '');
|
|
|
|
|
return `${id}-${slug}`;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-30 09:59:15 -08:00
|
|
|
|
export function extractIdFromSlug(slug: string) {
|
|
|
|
|
const id = parseInt(slug, 10);
|
|
|
|
|
if (isNaN(id)) {
|
|
|
|
|
console.error('extractIdFromSlug could not find id in : ' + slug);
|
2018-11-07 09:33:19 -08:00
|
|
|
|
}
|
2019-01-30 09:59:15 -08:00
|
|
|
|
return id;
|
2018-11-07 09:33:19 -08:00
|
|
|
|
}
|
2018-11-21 19:17:49 -08:00
|
|
|
|
|
|
|
|
|
// pre-hydration massage (BNify JSONed BNs)
|
|
|
|
|
export function massageSerializedState(state: AppState) {
|
2019-02-05 12:34:19 -08:00
|
|
|
|
// proposal detail
|
|
|
|
|
if (state.proposal.detail) {
|
|
|
|
|
state.proposal.detail.target = new BN(
|
|
|
|
|
(state.proposal.detail.target as any) as string,
|
|
|
|
|
16,
|
|
|
|
|
);
|
|
|
|
|
state.proposal.detail.funded = new BN(
|
|
|
|
|
(state.proposal.detail.funded as any) as string,
|
|
|
|
|
16,
|
|
|
|
|
);
|
2019-03-06 12:25:58 -08:00
|
|
|
|
state.proposal.detail.contributionBounty = new BN((state.proposal.detail
|
|
|
|
|
.contributionBounty as any) as string);
|
2019-03-18 09:43:30 -07:00
|
|
|
|
state.proposal.detail.milestones = state.proposal.detail.milestones.map(m => ({
|
|
|
|
|
...m,
|
|
|
|
|
amount: new BN((m.amount as any) as string, 16),
|
|
|
|
|
}));
|
2019-02-19 10:58:18 -08:00
|
|
|
|
if (state.proposal.detail.rfp && state.proposal.detail.rfp.bounty) {
|
|
|
|
|
state.proposal.detail.rfp.bounty = new BN(
|
|
|
|
|
(state.proposal.detail.rfp.bounty as any) as string,
|
|
|
|
|
16,
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-02-05 12:34:19 -08:00
|
|
|
|
}
|
2018-12-27 10:37:30 -08:00
|
|
|
|
// proposals
|
2019-02-05 12:34:19 -08:00
|
|
|
|
state.proposal.page.items = state.proposal.page.items.map(p => ({
|
2018-12-27 10:37:30 -08:00
|
|
|
|
...p,
|
2019-01-04 11:29:09 -08:00
|
|
|
|
target: new BN((p.target as any) as string, 16),
|
|
|
|
|
funded: new BN((p.funded as any) as string, 16),
|
2019-03-06 12:25:58 -08:00
|
|
|
|
contributionBounty: new BN((p.contributionMatching as any) as string, 16),
|
2018-12-27 10:37:30 -08:00
|
|
|
|
milestones: p.milestones.map(m => ({
|
|
|
|
|
...m,
|
2019-01-04 11:29:09 -08:00
|
|
|
|
amount: new BN((m.amount as any) as string, 16),
|
2018-12-27 10:37:30 -08:00
|
|
|
|
})),
|
|
|
|
|
}));
|
2018-11-21 19:17:49 -08:00
|
|
|
|
// users
|
|
|
|
|
const bnUserProp = (p: UserProposal) => {
|
|
|
|
|
p.funded = new BN(p.funded, 16);
|
|
|
|
|
p.target = new BN(p.target, 16);
|
|
|
|
|
return p;
|
|
|
|
|
};
|
|
|
|
|
Object.values(state.users.map).forEach(user => {
|
2019-01-09 11:07:50 -08:00
|
|
|
|
user.proposals = user.proposals.map(bnUserProp);
|
|
|
|
|
user.contributions = user.contributions.map(c => {
|
|
|
|
|
c.amount = new BN(c.amount, 16);
|
|
|
|
|
return c;
|
|
|
|
|
});
|
|
|
|
|
user.comments = user.comments.map(c => {
|
2018-11-21 19:17:49 -08:00
|
|
|
|
c.proposal = bnUserProp(c.proposal);
|
2019-01-09 11:07:50 -08:00
|
|
|
|
return c;
|
2018-11-21 19:17:49 -08:00
|
|
|
|
});
|
|
|
|
|
});
|
2019-03-06 15:21:12 -08:00
|
|
|
|
// RFPs
|
|
|
|
|
state.rfps.rfps = state.rfps.rfps.map(rfp => {
|
|
|
|
|
if (rfp.bounty) {
|
|
|
|
|
rfp.bounty = new BN(rfp.bounty, 16);
|
|
|
|
|
}
|
|
|
|
|
return rfp;
|
|
|
|
|
});
|
2018-11-21 19:17:49 -08:00
|
|
|
|
|
|
|
|
|
return state;
|
|
|
|
|
}
|