Various fixes (#260)

* remove linkedin social media option

* fix ProfileEdit social media button white-space

* fix default profile tab for non-authed user

* filter out non live proposal contributions for GET user/<id>

* admin: remove 255 char limit on reject_reason
This commit is contained in:
AMStrix 2019-02-25 10:41:00 -06:00 committed by William O'Beirne
parent 4c3a2127a8
commit 77e25b1c7c
9 changed files with 53 additions and 67 deletions

View File

@ -10,7 +10,6 @@ import {
Button,
Collapse,
Popconfirm,
Modal,
Input,
Switch,
message,
@ -29,14 +28,13 @@ import Back from 'components/Back';
import Info from 'components/Info';
import Markdown from 'components/Markdown';
import ArbiterControl from 'components/ArbiterControl';
import './index.less';
import { toZat, fromZat } from 'src/util/units';
import FeedbackModal from '../FeedbackModal';
import './index.less';
type Props = RouteComponentProps<any>;
const STATE = {
showRejectModal: false,
rejectReason: '',
paidTxId: '',
};
@ -50,8 +48,7 @@ class ProposalDetailNaked extends React.Component<Props, State> {
}
render() {
const id = this.getIdFromQuery();
const { proposalDetail: p, proposalDetailFetching, proposalDetailApproving } = store;
const { rejectReason, showRejectModal } = this.state;
const { proposalDetail: p, proposalDetailFetching } = store;
if (!p || (p && p.proposalId !== id) || proposalDetailFetching) {
return 'loading proposal...';
@ -65,7 +62,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
return m.datePaid ? prev - parseFloat(m.payoutPercent) : prev;
}, 100);
const renderDeleteControl = () => (
<Popconfirm
onConfirm={this.handleDelete}
@ -81,13 +77,13 @@ class ProposalDetailNaked extends React.Component<Props, State> {
const renderCancelControl = () => (
<Popconfirm
title={(
title={
<p>
Are you sure you want to cancel proposal and begin
<br />
the refund process? This cannot be undone.
</p>
)}
}
placement="left"
cancelText="cancel"
okText="confirm"
@ -181,35 +177,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
/>
);
const rejectModal = (
<Modal
visible={showRejectModal}
title="Reject this proposal"
onOk={this.handleReject}
onCancel={() => this.setState({ showRejectModal: false })}
okButtonProps={{
disabled: rejectReason.length === 0,
loading: proposalDetailApproving,
}}
cancelButtonProps={{
loading: proposalDetailApproving,
}}
>
Please provide a reason ({!!rejectReason.length && `${rejectReason.length}/`}
250 chars max):
<Input.TextArea
ref={ta => (this.rejectInput = ta)}
rows={4}
maxLength={250}
required={true}
value={rejectReason}
onChange={e => {
this.setState({ rejectReason: e.target.value });
}}
/>
</Modal>
);
const renderReview = () =>
p.status === PROPOSAL_STATUS.PENDING && (
<Alert
@ -232,16 +199,16 @@ class ProposalDetailNaked extends React.Component<Props, State> {
icon="close"
type="danger"
onClick={() => {
this.setState({ showRejectModal: true });
// hacky way of waiting for modal to render in before focus
setTimeout(() => {
if (this.rejectInput) this.rejectInput.focus();
}, 200);
FeedbackModal.open({
title: 'Reject this proposal?',
label: 'Please provide a reason:',
okText: 'Reject',
onOk: this.handleReject,
});
}}
>
Reject
</Button>
{rejectModal}
</div>
}
/>
@ -357,21 +324,22 @@ class ProposalDetailNaked extends React.Component<Props, State> {
<Alert
showIcon
type="error"
message={p.stage === PROPOSAL_STAGE.FAILED ? 'Proposal failed' : 'Proposal canceled'}
message={
p.stage === PROPOSAL_STAGE.FAILED ? 'Proposal failed' : 'Proposal canceled'
}
description={
p.stage === PROPOSAL_STAGE.FAILED ? (
<>
This proposal failed to reach its funding goal of <b>{p.target} ZEC</b> by{' '}
<b>{formatDateSeconds(p.datePublished + p.deadlineDuration)}</b>. All contributors
will need to be refunded.
<b>{formatDateSeconds(p.datePublished + p.deadlineDuration)}</b>. All
contributors will need to be refunded.
</>
) : (
<>
This proposal was canceled by an admin, and will be refunding contributors
{' '}<b>{refundablePct}%</b> of their contributions.
This proposal was canceled by an admin, and will be refunding contributors{' '}
<b>{refundablePct}%</b> of their contributions.
</>
)
}
/>
);
@ -421,7 +389,6 @@ class ProposalDetailNaked extends React.Component<Props, State> {
{renderCancelControl()}
{renderArbiterControl()}
{renderMatchingControl()}
{/* TODO - other actions */}
</Card>
{/* DETAILS */}
@ -502,9 +469,9 @@ class ProposalDetailNaked extends React.Component<Props, State> {
store.approveProposal(true);
};
private handleReject = async () => {
await store.approveProposal(false, this.state.rejectReason);
this.setState({ showRejectModal: false });
private handleReject = async (reason: string) => {
await store.approveProposal(false, reason);
message.info('Proposal rejected');
};
private handleToggleMatching = async () => {

View File

@ -160,7 +160,7 @@ class ProposalContribution(db.Model):
self.status = ContributionStatus.CONFIRMED
self.tx_id = tx_id
self.amount = amount
@hybrid_property
def refund_address(self):
return self.user.settings.refund_address
@ -216,7 +216,7 @@ class Proposal(db.Model):
category = db.Column(db.String(255), nullable=False)
date_approved = db.Column(db.DateTime)
date_published = db.Column(db.DateTime)
reject_reason = db.Column(db.String(255))
reject_reason = db.Column(db.String())
# Payment info
target = db.Column(db.String(255), nullable=False)

View File

@ -83,6 +83,7 @@ def get_user(user_id, with_proposals, with_comments, with_funded, with_pending,
contributions = ProposalContribution.get_by_userid(user_id)
if not authed_user or user.id != authed_user.id:
contributions = [c for c in contributions if c.status == ContributionStatus.CONFIRMED]
contributions = [c for c in contributions if c.proposal.status == ProposalStatus.LIVE]
contributions_dump = user_proposal_contributions_schema.dump(contributions)
result["contributions"] = contributions_dump
if with_comments:

View File

@ -0,0 +1,26 @@
"""remove linkedin social_media items
Revision ID: 332a15eba9d8
Revises: 7c7cecfe5e6c
Create Date: 2019-02-23 19:51:16.284007
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '332a15eba9d8'
down_revision = '7c7cecfe5e6c'
branch_labels = None
depends_on = None
def upgrade():
connection = op.get_bind()
connection.execute("DELETE FROM social_media WHERE service = 'LINKEDIN'")
def downgrade():
# there is no going back, all your precious linkedin profiles are gone now
pass

View File

@ -28,7 +28,7 @@ def upgrade():
sa.Column('category', sa.String(length=255), nullable=False),
sa.Column('date_approved', sa.DateTime(), nullable=True),
sa.Column('date_published', sa.DateTime(), nullable=True),
sa.Column('reject_reason', sa.String(length=255), nullable=True),
sa.Column('reject_reason', sa.String(), nullable=True),
sa.Column('target', sa.String(length=255), nullable=False),
sa.Column('payout_address', sa.String(length=255), nullable=False),
sa.Column('deadline_duration', sa.Integer(), nullable=False),

View File

@ -164,8 +164,7 @@ class ProfileEdit extends React.PureComponent<Props, State> {
loading={loading}
block
>
{!loading && s.icon}
Connect to {s.name}
{!loading && s.icon} <>Connect to {s.name}</>
</Button>
)}
</Form.Item>

View File

@ -125,7 +125,7 @@ class Profile extends React.Component<Props, State> {
/>
</Switch>
<div className="Profile-tabs">
<LinkableTabs defaultActiveKey="pending">
<LinkableTabs defaultActiveKey={(isAuthedUser && 'pending') || 'created'}>
{isAuthedUser && (
<Tabs.TabPane
tab={TabTitle('Pending', pendingProposals.length)}

View File

@ -16,12 +16,6 @@ export const SOCIAL_INFO: { [key in SOCIAL_SERVICE]: SocialInfo } = {
format: `https://twitter.com/${accountNameRegex}`,
icon: <Icon type="twitter" />,
},
[SOCIAL_SERVICE.LINKEDIN]: {
service: SOCIAL_SERVICE.LINKEDIN,
name: 'LinkedIn',
format: `https://linkedin.com/in/${accountNameRegex}`,
icon: <Icon type="linkedin" />,
},
};
export function socialMediaToUrl(service: SOCIAL_SERVICE, username: string): string {

View File

@ -18,5 +18,4 @@ export interface SocialInfo {
export enum SOCIAL_SERVICE {
GITHUB = 'GITHUB',
TWITTER = 'TWITTER',
LINKEDIN = 'LINKEDIN',
}