Skip to content

Commit

Permalink
refact: fetch ContributorProfiles from the API
Browse files Browse the repository at this point in the history
  • Loading branch information
kewitz committed Jan 17, 2025
1 parent afdeeca commit 1c8e893
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 78 deletions.
22 changes: 14 additions & 8 deletions components/contribution-flow/ContributeProfilePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { Span } from '../Text';

import { canUseIncognitoForContribution } from './utils';

const { USER, ORGANIZATION, COLLECTIVE, FUND, EVENT, PROJECT } = CollectiveType;
const { ORGANIZATION, COLLECTIVE, FUND, EVENT, PROJECT, INDIVIDUAL } = CollectiveType;

const formatAccountName = (intl, account) => {
return account.isIncognito
Expand All @@ -21,22 +21,28 @@ const formatAccountName = (intl, account) => {
};

const getProfileOptions = (intl, profiles, tier) => {
const getOptionFromAccount = value => ({ [FLAG_COLLECTIVE_PICKER_COLLECTIVE]: true, value, label: value.name });
const getOptionFromAccount = value => ({
[FLAG_COLLECTIVE_PICKER_COLLECTIVE]: true,
value: value.account,
label: value.account.name,
});
const sortOptions = options => sortBy(options, 'value.name');
const profileOptions = profiles.map(getOptionFromAccount);
const profilesByType = groupBy(profileOptions, p => p.value.type);
const myself = profilesByType[USER] || [];
const myself = profilesByType[INDIVIDUAL] || [];
const myOrganizations = sortOptions(profilesByType[ORGANIZATION] || []);

// Add incognito profile entry if it doesn't exists
const hasIncognitoProfile = profiles.some(p => p.type === CollectiveType.USER && p.isIncognito);
const hasIncognitoProfile = profiles.some(p => p.account.type === CollectiveType.INDIVIDUAL && p.account.isIncognito);
if (!hasIncognitoProfile && canUseIncognitoForContribution(tier)) {
myself.push(
getOptionFromAccount({
id: 'incognito',
type: CollectiveType.USER,
isIncognito: true,
name: intl.formatMessage({ id: 'profile.incognito', defaultMessage: 'Incognito' }),
account: {
id: 'incognito',
type: CollectiveType.INDIVIDUAL,
isIncognito: true,
name: intl.formatMessage({ id: 'profile.incognito', defaultMessage: 'Incognito' }),
},
}),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ContributionFlowStepContainer extends React.Component {
intl: PropTypes.object,
LoggedInUser: PropTypes.object,
collective: PropTypes.object,
contributorProfiles: PropTypes.arrayOf(PropTypes.object),
tier: PropTypes.object,
onChange: PropTypes.func,
showPlatformTip: PropTypes.bool,
Expand Down Expand Up @@ -92,7 +93,7 @@ class ContributionFlowStepContainer extends React.Component {
case 'profile': {
return (
<StepProfile
profiles={this.props.contributeProfiles}
profiles={this.props.contributorProfiles}
collective={collective}
tier={tier}
stepDetails={stepDetails}
Expand Down
10 changes: 5 additions & 5 deletions components/contribution-flow/StepProfileGuestForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,6 @@ const StepProfileGuestForm = ({ stepDetails, onChange, data, isEmbed, onSignInCl
/>
)}
</StyledInputField>
{isCaptchaEnabled() && (
<Flex mt="18px" justifyContent="center">
<Captcha onVerify={result => dispatchChange('captcha', result)} />
</Flex>
)}
{contributionRequiresAddress(stepDetails, tier) && (
<React.Fragment>
<Flex alignItems="center" my="14px">
Expand All @@ -166,6 +161,11 @@ const StepProfileGuestForm = ({ stepDetails, onChange, data, isEmbed, onSignInCl
/>
</React.Fragment>
)}
{isCaptchaEnabled() && (
<Flex mt="18px" justifyContent="center">
<Captcha onVerify={result => dispatchChange('captcha', result)} />
</Flex>
)}
<StepProfileInfoMessage isGuest hasLegalNameField />
<P color="black.500" fontSize="12px" mt={4} data-cy="join-conditions">
<FormattedMessage
Expand Down
30 changes: 30 additions & 0 deletions components/contribution-flow/graphql/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,36 @@ export const contributionFlowAccountQuery = gql`
id
...ContributionFlowAccountFields
}
me {
contributorProfiles(forAccount: { slug: $collectiveSlug }) {

Check failure on line 12 in components/contribution-flow/graphql/queries.js

View workflow job for this annotation

GitHub Actions / lint

Cannot query field "contributorProfiles" on type "Individual"
account {
id
name
legalName
slug
type
imageUrl(height: 192)
isIncognito
... on Individual {
email
isGuest
}
location {
address
country
structured
}
... on AccountWithHost {
host {
id
slug
name
imageUrl(height: 64)
}
}
}
}
}
tier(tier: { legacyId: $tierId }, throwIfMissing: false) @include(if: $includeTier) {
id
legacyId
Expand Down
26 changes: 13 additions & 13 deletions components/contribution-flow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { formatErrorMessage, getErrorFromGraphqlException } from '../../lib/erro
import { isPastEvent } from '../../lib/events';
import { Experiment, isExperimentEnabled } from '../../lib/experiments/experiments';
import { API_V2_CONTEXT, gql } from '../../lib/graphql/helpers';
import { AccountType } from '../../lib/graphql/types/v2/schema';
import { addCreateCollectiveMutation } from '../../lib/graphql/v1/mutations';
import { setGuestToken } from '../../lib/guest-accounts';
import { getStripe, stripeTokenToPaymentMethod } from '../../lib/stripe';
Expand Down Expand Up @@ -64,7 +65,6 @@ import SignInToContributeAsAnOrganization from './SignInToContributeAsAnOrganiza
import { validateGuestProfile } from './StepProfileGuestForm';
import { NEW_ORGANIZATION_KEY } from './StepProfileLoggedInForm';
import {
getContributeProfiles,
getGQLV2AmountInput,
getGuestInfoFromStepProfile,
getTotalAmount,
Expand Down Expand Up @@ -136,6 +136,7 @@ class ContributionFlow extends React.Component {
slug: PropTypes.string,
}),
}).isRequired,
contributorProfiles: PropTypes.arrayOf(PropTypes.object),
host: PropTypes.object.isRequired,
tier: PropTypes.object,
intl: PropTypes.object,
Expand Down Expand Up @@ -391,8 +392,8 @@ class ContributionFlow extends React.Component {
await confirmPayment(stripeData?.stripe, stripeData?.paymentIntentClientSecret, {
returnUrl: returnUrl.href,
elements: stripeData?.elements,
type: stepPayment?.paymentMethod?.type,
paymentMethodId: stepPayment?.paymentMethod?.data?.stripePaymentMethodId,
type: stepPayment.paymentMethod?.type,
paymentMethodId: stepPayment.paymentMethod?.data?.stripePaymentMethodId,
});
this.setState({ isSubmitted: true, isSubmitting: false });
return this.handleSuccess(order);
Expand Down Expand Up @@ -463,12 +464,9 @@ class ContributionFlow extends React.Component {
};

// ---- Getters ----

getContributeProfiles = memoizeOne(getContributeProfiles);

getDefaultStepProfile() {
const { LoggedInUser, loadingLoggedInUser, collective, tier } = this.props;
const profiles = this.getContributeProfiles(LoggedInUser, collective, tier);
const { loadingLoggedInUser, contributorProfiles } = this.props;
const profiles = contributorProfiles;
const queryParams = this.getQueryParams();

// We want to wait for the user to be logged in before matching the profile
Expand All @@ -480,17 +478,19 @@ class ContributionFlow extends React.Component {
let contributorProfile;
if (queryParams.contributeAs && queryParams.contributeAs !== PERSONAL_PROFILE_ALIAS) {
if (queryParams.contributeAs === INCOGNITO_PROFILE_ALIAS) {
contributorProfile = profiles.find(({ isIncognito }) => isIncognito);
contributorProfile = profiles.find(({ account: { isIncognito } }) => isIncognito);
} else if (queryParams.contributeAs === 'me') {
contributorProfile = profiles.find(({ account: { type } }) => type === AccountType.INDIVIDUAL);
} else {
contributorProfile = profiles.find(({ slug }) => slug === queryParams.contributeAs);
contributorProfile = profiles.find(({ account: { slug } }) => slug === queryParams.contributeAs);
}
}

if (contributorProfile) {
return contributorProfile;
} else if (profiles[0]) {
} else if (profiles[0].account) {
// Otherwise to the logged-in user personal profile, if any
return profiles[0];
return profiles[0].account;
}

// Otherwise, it's a guest contribution
Expand Down Expand Up @@ -1012,7 +1012,7 @@ class ContributionFlow extends React.Component {
isSubmitting={isValidating || isLoading}
disabledPaymentMethodTypes={queryParams.disabledPaymentMethodTypes}
hideCreditCardPostalCode={queryParams.hideCreditCardPostalCode}
contributeProfiles={this.getContributeProfiles(LoggedInUser, collective, tier)}
contributorProfiles={this.props.contributorProfiles}
/>
<Box mt={40}>
<ContributionFlowButtons
Expand Down
52 changes: 4 additions & 48 deletions components/contribution-flow/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { CreditCard } from '@styled-icons/fa-solid/CreditCard';
import { find, get, isEmpty, pick, sortBy, uniqBy } from 'lodash';
import { find, get, pick, sortBy, uniqBy } from 'lodash';
import { defineMessages, FormattedMessage } from 'react-intl';

import { canContributeRecurring, getCollectivePageMetadata } from '../../lib/collective';
Expand All @@ -11,7 +11,6 @@ import {
PAYMENT_METHOD_SERVICE,
PAYMENT_METHOD_TYPE,
} from '../../lib/constants/payment-methods';
import roles from '../../lib/constants/roles';
import { TierTypes } from '../../lib/constants/tiers-types';
import { PaymentMethodService, PaymentMethodType } from '../../lib/graphql/types/v2/schema';
import { getPaymentMethodName } from '../../lib/payment_method_label';
Expand All @@ -29,56 +28,13 @@ export const NEW_CREDIT_CARD_KEY = 'newCreditCard';
export const STRIPE_PAYMENT_ELEMENT_KEY = 'stripe-payment-element';
const PAYPAL_MAX_AMOUNT = 999999999; // See MAX_VALUE_EXCEEDED https://developer.paypal.com/api/rest/reference/orders/v2/errors/#link-createorder

const memberCanBeUsedToContribute = (member, account, canUseIncognito) => {
if (member.role !== roles.ADMIN) {
return false;
} else if (!canUseIncognito && member.collective.isIncognito) {
// Incognito can't be used to contribute if not allowed
return false;
} else if (
[CollectiveType.COLLECTIVE, CollectiveType.FUND].includes(member.collective.type) &&
member.collective.host?.id !== account.host.legacyId
) {
// If the contributing account is fiscally hosted, the host must be the same as the one you're contributing to
return false;
} else {
return true;
}
};

/*
**Cannot use contributions for events and "Tickets" tiers, because we need the ticket holder's identity
*/
export const canUseIncognitoForContribution = tier => {
return !tier || tier.type !== 'TICKET';
};

export const getContributeProfiles = (loggedInUser, collective, tier) => {
if (!loggedInUser) {
return [];
} else {
const canUseIncognito = canUseIncognitoForContribution(tier);
const filteredMembers = loggedInUser.memberOf.filter(member =>
memberCanBeUsedToContribute(member, collective, canUseIncognito),
);
const personalProfile = { email: loggedInUser.email, image: loggedInUser.image, ...loggedInUser.collective };
const contributorProfiles = [personalProfile];
filteredMembers.forEach(member => {
// Account can't contribute to itself
if (member.collective.id !== collective.legacyId) {
contributorProfiles.push(member.collective);
}
if (!isEmpty(member.collective.children)) {
const childrenOfSameHost = member.collective.children.filter(
child => child.host && child.host.id === collective.host.legacyId,
);
contributorProfiles.push(...childrenOfSameHost);
}
});
return uniqBy([personalProfile, ...contributorProfiles], 'id');
}
};

export const generatePaymentMethodOptions = (
intl,
paymentMethods,
Expand Down Expand Up @@ -111,7 +67,7 @@ export const generatePaymentMethodOptions = (

uniquePMs = uniquePMs.filter(
({ paymentMethod }) =>
paymentMethod.type !== PAYMENT_METHOD_TYPE.COLLECTIVE || collective.host.legacyId === stepProfile.host?.id,
paymentMethod.type !== PAYMENT_METHOD_TYPE.COLLECTIVE || collective.host.id === stepProfile.host?.id,
);

if (paymentIntent) {
Expand All @@ -127,7 +83,7 @@ export const generatePaymentMethodOptions = (

return (
allowedStripeTypes.includes(paymentMethod.type.toLowerCase()) &&
(!paymentMethod?.data?.stripeAccount || paymentMethod?.data?.stripeAccount === paymentIntent.stripeAccount)
(!paymentMethod.data?.stripeAccount || paymentMethod.data?.stripeAccount === paymentIntent.stripeAccount)
);
});
} else {
Expand All @@ -136,7 +92,7 @@ export const generatePaymentMethodOptions = (
return true;
}

return paymentMethod.type === PaymentMethodType.CREDITCARD && !paymentMethod?.data?.stripeAccount;
return paymentMethod.type === PaymentMethodType.CREDITCARD && !paymentMethod.data?.stripeAccount;
});
}

Expand Down
12 changes: 10 additions & 2 deletions pages/contribution-flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class NewContributionFlowPage extends React.Component {

renderPageContent() {
const { data = {}, LoggedInUser, error } = this.props;
const { account, tier } = data;
const { account, tier, me } = data;

if (data.loading) {
return (
Expand Down Expand Up @@ -116,7 +116,15 @@ class NewContributionFlowPage extends React.Component {
</React.Fragment>
);
} else {
return <ContributionFlowContainer collective={account} host={account.host} tier={tier} error={error} />;
return (
<ContributionFlowContainer
collective={account}
host={account.host}
tier={tier}
contributorProfiles={me?.contributorProfiles || []}
error={error}
/>
);
}
}

Expand Down
3 changes: 2 additions & 1 deletion pages/embed/contribution-flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class EmbedContributionFlowPage extends React.Component<{

renderPageContent() {
const { data, LoggedInUser } = this.props;
const { account, tier, loading } = data || {};
const { account, tier, loading, me } = data || {};

if (loading) {
return (
Expand All @@ -131,6 +131,7 @@ class EmbedContributionFlowPage extends React.Component<{
host={account.host}
tier={tier}
error={this.props.error}
contributorProfiles={me?.contributorProfiles || []}
onStepChange={step =>
this.postMessage('stepChange', {
step,
Expand Down

0 comments on commit 1c8e893

Please sign in to comment.