diff --git a/src/App.jsx b/src/App.jsx index 8b90704d2..1e5e9d2a5 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -411,6 +411,7 @@ class App extends Component { } /> } /> + } /> } /> } /> } /> @@ -737,14 +738,14 @@ class App extends Component { } const WeVoteBody = styled('div')` - background-color: #fff; // rgb(235, 236, 238); // #fafafa; - color: #000; + // We rely on many of these from the body from main.css, including: + //background-color: #fff; // rgb(235, 236, 238); // #fafafa; + //color: #000; + //font-family: "Poppins", "Helvetica Neue Light", "Helvetica Neue", "Helvetica", "Arial", sans-serif; + //line-height: 1.4; + // margin: 0 auto; + display: block; - font-family: "Poppins", "Helvetica Neue Light", "Helvetica Neue", "Helvetica", "Arial", sans-serif; - line-height: 1.4; - margin: 0 auto; - // max-width: 960px; - //height: 100vw; position: relative; z-index: 0; // this debug technique works! ${() => console.log('-----------------------------')} diff --git a/src/css/main.css b/src/css/main.css index 24af97cba..df8233bf4 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -59,8 +59,8 @@ body { font-style:normal; font-weight:normal; letter-spacing:.15px; - line-height:24px; - margin:0; + line-height: 1.4; + margin:0 auto; text-align:left } diff --git a/src/img/global/svg-icons/issues/rocket-ship.svg b/src/img/global/svg-icons/rocket-ship.svg similarity index 100% rename from src/img/global/svg-icons/issues/rocket-ship.svg rename to src/img/global/svg-icons/rocket-ship.svg diff --git a/src/js/actions/ReadyActions.js b/src/js/actions/ReadyActions.js index 6f68edb45..b5b77a2e3 100644 --- a/src/js/actions/ReadyActions.js +++ b/src/js/actions/ReadyActions.js @@ -2,7 +2,7 @@ import Dispatcher from '../common/dispatcher/Dispatcher'; export default { voterPlansForVoterRetrieve (year = 0, month = 0, googleCivicElectionId = 0, stateCode = '') { - // Retrieve the click statistics for all of the items you have shared + // Retrieve the click statistics for all the items you have shared return Dispatcher.loadEndpoint('voterPlansForVoterRetrieve', { google_civic_election_id: googleCivicElectionId, month, diff --git a/src/js/common/actions/ChallengeInviteeActions.js b/src/js/common/actions/ChallengeInviteeActions.js index 0f768833c..faccac9ce 100644 --- a/src/js/common/actions/ChallengeInviteeActions.js +++ b/src/js/common/actions/ChallengeInviteeActions.js @@ -16,11 +16,13 @@ export default { }); }, - challengeInviteeSave (challengeWeVoteId, inviteeId = 0, inviteeName = '', inviteeNameChanged = false, inviteTextFromInviter = '', inviteTextFromInviterChanged = false, inviteeUrlCode = '', inviteeUrlCodeChanged = false) { + challengeInviteeSave (challengeWeVoteId, destinationFullURL = '', googleCivicElectionId = 0, inviteeId = 0, inviteeName = '', inviteeNameChanged = false, inviteTextFromInviter = '', inviteTextFromInviterChanged = false, inviteeUrlCode = '', inviteeUrlCodeChanged = false) { // console.log('challengeInviteeSave called with challengeWeVoteId: ', challengeWeVoteId, ' and inviteeName: ', inviteeName); Dispatcher.loadEndpoint('challengeInviteeSave', { challenge_we_vote_id: challengeWeVoteId, + destination_full_url: destinationFullURL, + google_civic_election_id: googleCivicElectionId, invitee_id: inviteeId, invitee_name: inviteeName, invitee_name_changed: inviteeNameChanged, diff --git a/src/js/common/actions/ShareActions.js b/src/js/common/actions/ShareActions.js index c8a1d72d1..19bd11c18 100644 --- a/src/js/common/actions/ShareActions.js +++ b/src/js/common/actions/ShareActions.js @@ -28,7 +28,7 @@ export default { }, sharedItemListRetrieve (year = 0, month = 0, googleCivicElectionId = 0, stateCode = '') { - // Retrieve the click statistics for all of the items you have shared + // Retrieve the click statistics for all the items you have shared return Dispatcher.loadEndpoint('sharedItemListRetrieve', { google_civic_election_id: googleCivicElectionId, month, diff --git a/src/js/common/components/Challenge/ThanksForViewingChallenge.jsx b/src/js/common/components/Challenge/ThanksForViewingChallenge.jsx index 441345393..73f866b2a 100644 --- a/src/js/common/components/Challenge/ThanksForViewingChallenge.jsx +++ b/src/js/common/components/Challenge/ThanksForViewingChallenge.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import Confetti from 'react-confetti'; import React, { useState, useEffect } from 'react'; -const ThanksForViewingChallenge = ({ challengeOwner }) => { +const ThanksForViewingChallenge = ({ sharedByDisplayName }) => { const [isClosing, setIsClosing] = useState(false); const [showConfetti, setShowConfetti] = useState(false); @@ -33,10 +33,18 @@ const ThanksForViewingChallenge = ({ challengeOwner }) => { {showConfetti && } - Thanks for confirming - the link from  - {challengeOwner} - ! + Thanks for checking out this challenge + {sharedByDisplayName && ( + <> + {' '} + your friend + {' '} + {sharedByDisplayName} + {' '} + has shared with you + + )} + ! Join the challenge below. { ); }; ThanksForViewingChallenge.propTypes = { - challengeOwner: PropTypes.string.isRequired, + sharedByDisplayName: PropTypes.string, }; const CloseMessageIconWrapper = styled.div` diff --git a/src/js/common/components/ChallengeInviteFriends/InviteFriendToChallengeInput.jsx b/src/js/common/components/ChallengeInviteFriends/InviteFriendToChallengeInput.jsx index 83330ac0c..7f31894e8 100755 --- a/src/js/common/components/ChallengeInviteFriends/InviteFriendToChallengeInput.jsx +++ b/src/js/common/components/ChallengeInviteFriends/InviteFriendToChallengeInput.jsx @@ -17,6 +17,8 @@ const InviteFriendToChallengeInput = ({ classes, challengeWeVoteId, externalUniq renderLog('InviteFriendToChallengeInputBox'); // Set LOG_RENDER_EVENTS to log all renders const [challengeInviteTextDefault, setChallengeInviteTextDefault] = React.useState(''); const [challengeTitle, setChallengeTitle] = React.useState(''); + const [destinationFullURL, setDestinationFullURL] = React.useState(''); + const [googleCivicElectionId, setGoogleCivicElectionId] = React.useState(0); const [inviteCopiedMessageOn, setInviteCopiedMessageOn] = React.useState(false); const [inviteeName, setInviteeName] = React.useState(''); const [inviterName, setInviterName] = React.useState(''); @@ -31,9 +33,11 @@ const InviteFriendToChallengeInput = ({ classes, challengeWeVoteId, externalUniq inviteTextToSendTemp1 += inviterFirstName ? `, this is ${inviterFirstName}. ` : ', '; const inviteTextToSendTemp2 = inviteTextForFriends || challengeInviteTextDefault; const inviteeUrlCode = ChallengeInviteeStore.getNextInviteeUrlCode(); - const urlToSendTemp = `${ChallengeStore.getSiteUrl(challengeWeVoteId)}/++/${inviteeUrlCode}`; + const urlToSendTemp = `${ChallengeStore.getSiteUrl(challengeWeVoteId)}/-${inviteeUrlCode}`; const inviteTextToSendTemp3 = `${inviteTextToSendTemp1}${inviteTextToSendTemp2} ${urlToSendTemp}`; setInviteTextToSend(inviteTextToSendTemp3); + const SEOFriendlyPath = ChallengeStore.getChallengeSEOFriendlyPathByWeVoteId(challengeWeVoteId); + setDestinationFullURL(`${ChallengeStore.getSiteUrl(challengeWeVoteId)}/${SEOFriendlyPath}/+/`); // setUrlToSend(urlToSendTemp); } @@ -46,13 +50,21 @@ const InviteFriendToChallengeInput = ({ classes, challengeWeVoteId, externalUniq const handleShare = async () => { const inviteeUrlCode = ChallengeInviteeStore.getNextInviteeUrlCode(); - ChallengeInviteeActions.challengeInviteeSave(challengeWeVoteId, 0, inviteeName, true, inviteTextToSend, true, inviteeUrlCode, true); + ChallengeInviteeActions.challengeInviteeSave( + challengeWeVoteId, + destinationFullURL, + googleCivicElectionId, + 0, + inviteeName, true, + inviteTextToSend, true, + inviteeUrlCode, true, + ); setInviteCopiedMessageOn(true); setTimeout(() => { console.log('handleShare setTimeout fired'); setInviteCopiedMessageOn(false); resetForm(); - }, 1000); + }, 2000); if (navigator.share) { try { await navigator.share({ @@ -84,18 +96,22 @@ const InviteFriendToChallengeInput = ({ classes, challengeWeVoteId, externalUniq }; const onChallengeParticipantStoreChange = () => { - setInviterName(VoterStore.getFirstName()); setInviteTextForFriends(ChallengeParticipantStore.getInviteTextForFriends(challengeWeVoteId)); prepareInviteTextToSend(); }; const onChallengeStoreChange = () => { - setInviterName(VoterStore.getFirstName()); setChallengeInviteTextDefault(ChallengeStore.getChallengeInviteTextDefaultByWeVoteId(challengeWeVoteId)); setChallengeTitle(ChallengeStore.getChallengeTitleByWeVoteId(challengeWeVoteId)); prepareInviteTextToSend(); }; + const onVoterStoreChange = () => { + setGoogleCivicElectionId(VoterStore.electionId()); + setInviterName(VoterStore.getFirstName()); + prepareInviteTextToSend(); + }; + // console.log('Fetching participants for:', challengeWeVoteId); const challengeInviteeStoreListener = ChallengeInviteeStore.addListener(onChallengeInviteeStoreChange); onChallengeParticipantStoreChange(); @@ -103,11 +119,14 @@ const InviteFriendToChallengeInput = ({ classes, challengeWeVoteId, externalUniq onChallengeParticipantStoreChange(); const challengeStoreListener = ChallengeStore.addListener(onChallengeStoreChange); onChallengeStoreChange(); + const voterStoreListener = VoterStore.addListener(onVoterStoreChange); + onVoterStoreChange(); return () => { challengeInviteeStoreListener.remove(); challengeParticipantStoreListener.remove(); challengeStoreListener.remove(); + voterStoreListener.remove(); }; }, [challengeWeVoteId]); diff --git a/src/js/common/components/ChallengeInviteeListRoot/ChallengeInviteeListRoot.jsx b/src/js/common/components/ChallengeInviteeListRoot/ChallengeInviteeListRoot.jsx index 233e93187..e300d155d 100644 --- a/src/js/common/components/ChallengeInviteeListRoot/ChallengeInviteeListRoot.jsx +++ b/src/js/common/components/ChallengeInviteeListRoot/ChallengeInviteeListRoot.jsx @@ -7,7 +7,7 @@ import FirstChallengeInviteeListController from './FirstChallengeInviteeListCont import AppObservableStore, { messageService } from '../../stores/AppObservableStore'; import ChallengeInviteeStore from '../../stores/ChallengeInviteeStore'; import DesignTokenColors from '../Style/DesignTokenColors'; -import ChallengeParticipantStore from "../../stores/ChallengeParticipantStore"; +import ChallengeParticipantStore from '../../stores/ChallengeParticipantStore'; const ChallengeParticipantFirstRetrieveController = React.lazy(() => import(/* webpackChunkName: 'ChallengeParticipantFirstRetrieveController' */ '../ChallengeParticipant/ChallengeParticipantFirstRetrieveController')); diff --git a/src/js/common/components/ChallengeInviteeListRoot/InviteAgainButton.jsx b/src/js/common/components/ChallengeInviteeListRoot/InviteAgainButton.jsx index 0651e46b2..6fbcf626e 100755 --- a/src/js/common/components/ChallengeInviteeListRoot/InviteAgainButton.jsx +++ b/src/js/common/components/ChallengeInviteeListRoot/InviteAgainButton.jsx @@ -107,7 +107,7 @@ const InviteAgainButton = ({ classes, challengeWeVoteId, challengeInviteeId }) = } // console.log('challengeInviteTextDefault: ', challengeInviteTextDefault); const inviteeUrlCode = ChallengeInviteeStore.getNextInviteeUrlCode(); - const urlToSendTemp = `${ChallengeStore.getSiteUrl(challengeWeVoteId)}/++/${inviteeUrlCode}`; + const urlToSendTemp = `${ChallengeStore.getSiteUrl(challengeWeVoteId)}/-${inviteeUrlCode}`; const inviteTextToSendTemp3 = `${inviteTextToSendTemp1}${inviteTextToSendTemp2} ${urlToSendTemp}`; setInviteTextToSend(inviteTextToSendTemp3); // setUrlToSend(urlToSendTemp); diff --git a/src/js/common/components/Navigation/ChallengeInviteSteps.jsx b/src/js/common/components/Navigation/ChallengeInviteSteps.jsx index e1e20dd65..812de0fc2 100644 --- a/src/js/common/components/Navigation/ChallengeInviteSteps.jsx +++ b/src/js/common/components/Navigation/ChallengeInviteSteps.jsx @@ -39,6 +39,12 @@ class ChallengeInviteSteps extends React.Component { return 1; }; + // Check if a step is active based on the current step number + // isStepActive = (stepNumber) => this.props.currentStep === stepNumber; + + // Set a step as active when clicked + isStepActive = (stepNumber) => this.state.activeStep === stepNumber; + // Get the path for the challenge getChallengeBasePath = () => { const { challengeSEOFriendlyPath, challengeWeVoteId } = this.props; @@ -51,9 +57,6 @@ class ChallengeInviteSteps extends React.Component { return challengeBasePath; }; - // Set a step as active when clicked - isStepActive = (stepNumber) => this.state.activeStep === stepNumber; - // Update the active step when the link is clicked handleStepClick = (stepNumber) => { this.setState({ activeStep: stepNumber }); @@ -146,6 +149,7 @@ class ChallengeInviteSteps extends React.Component { } ChallengeInviteSteps.propTypes = { + currentStep: PropTypes.number.isRequired, challengeSEOFriendlyPath: PropTypes.string, challengeWeVoteId: PropTypes.string, location: PropTypes.object.isRequired, diff --git a/src/js/common/components/PrivacyBody.jsx b/src/js/common/components/PrivacyBody.jsx index 568fc9e33..f410439ad 100755 --- a/src/js/common/components/PrivacyBody.jsx +++ b/src/js/common/components/PrivacyBody.jsx @@ -171,7 +171,7 @@ export default class PrivacyBody extends Component { Who you Follow on Twitter - When you sign in with Twitter, all of the Twitter accounts you follow on Twitter which have endorsements stored in WeVote, are displayed on your profile + When you sign in with Twitter, all the Twitter accounts you follow on Twitter which have endorsements stored in WeVote, are displayed on your profile Yes No diff --git a/src/js/common/components/Settings/CompleteYourProfile.jsx b/src/js/common/components/Settings/CompleteYourProfile.jsx index fc8f0dc0d..672ca865c 100644 --- a/src/js/common/components/Settings/CompleteYourProfile.jsx +++ b/src/js/common/components/Settings/CompleteYourProfile.jsx @@ -296,7 +296,7 @@ class CompleteYourProfile extends Component { } else { buttonText = 'Help them win'; } - introductionText = Leading up to election day, WeVote.US will remind you to vote for all of the candidates you support. We keep your email secure and confidential.; + introductionText = Leading up to election day, WeVote.US will remind you to vote for all the candidates you support. We keep your email secure and confidential.; } else if (supportCampaignOnCampaignHome) { if (voterCanVoteForPoliticianInCampaign) { buttonText = 'Support with my vote'; diff --git a/src/js/common/pages/Challenge/ChallengeHomePage.jsx b/src/js/common/pages/Challenge/ChallengeHomePage.jsx index 9c68e960b..e4cc94581 100644 --- a/src/js/common/pages/Challenge/ChallengeHomePage.jsx +++ b/src/js/common/pages/Challenge/ChallengeHomePage.jsx @@ -44,6 +44,7 @@ import ChallengeAbout from '../../components/Challenge/ChallengeAbout'; import ChallengeParticipantListRoot from '../../components/ChallengeParticipantListRoot/ChallengeParticipantListRoot'; import ChallengeInviteeListRoot from '../../components/ChallengeInviteeListRoot/ChallengeInviteeListRoot'; import ThanksForViewingChallenge from '../../components/Challenge/ThanksForViewingChallenge' +import ShareStore from '../../stores/ShareStore'; const ChallengeCardForList = React.lazy(() => import(/* webpackChunkName: 'ChallengeCardForList' */ '../../components/ChallengeListRoot/ChallengeCardForList')); // const ChallengeCommentsList = React.lazy(() => import(/* webpackChunkName: 'ChallengeCommentsList' */ '../../components/Challenge/ChallengeCommentsList')); @@ -108,6 +109,7 @@ class ChallengeHomePage extends Component { challengeWeVoteIdForDisplay: '', // Value for challenge already received sharingStepCompleted: false, step2Completed: false, + thanksForViewingChallengeOn: false, voterCanEditThisChallenge: false, }; // this.onScroll = this.onScroll.bind(this); @@ -116,7 +118,11 @@ class ChallengeHomePage extends Component { componentDidMount () { // console.log('ChallengeHomePage componentDidMount'); const { match: { params } } = this.props; - const { challengeSEOFriendlyPath: challengeSEOFriendlyPathFromUrl, challengeWeVoteId } = params; + const { + challengeSEOFriendlyPath: challengeSEOFriendlyPathFromUrl, + challengeWeVoteId, + shared_item_code: sharedItemCodeIncoming, + } = params; // console.log('ChallengeHomePage componentDidMount tabSelected: ', tabSelected); // console.log('componentDidMount challengeSEOFriendlyPathFromUrl: ', challengeSEOFriendlyPathFromUrl, ', challengeWeVoteId: ', challengeWeVoteId); this.onAppObservableStoreChange(); @@ -162,6 +168,20 @@ class ChallengeHomePage extends Component { } }, 5000); // April 19, 2021: Tuned to keep performance above 83. LCP at 597ms + // If we came in through a sharedItem link and then redirected to this page, fetch the shared item details + const sharedItem = ShareStore.getSharedItemByCode(sharedItemCodeIncoming); + // console.log('sharedItem:', sharedItem); + if (sharedItem && sharedItem.shared_by_display_name) { + const { + shared_by_display_name: sharedByDisplayName, + // shared_by_first_name: sharedByFirstName, + } = sharedItem; + this.setState({ + sharedByDisplayName, + thanksForViewingChallengeOn: true, + }); + } + // console.log('componentDidMount triggerSEOPathRedirect: ', triggerSEOPathRedirect, ', challengeSEOFriendlyPathFromObject: ', challengeSEOFriendlyPathFromObject); if (triggerSEOPathRedirect && challengeSEOFriendlyPathFromObject) { historyPush(`/${challengeSEOFriendlyPathFromObject}/+/`, true); @@ -453,12 +473,9 @@ class ChallengeHomePage extends Component { challengeDataFound, challengeDataNotFound, challengeDescription, challengeDescriptionLimited, challengeImageUrlLarge, challengeSEOFriendlyPath, challengeSEOFriendlyPathForDisplay, - challengeTitle, - challengeWeVoteIdForDisplay, - scrolledDown, - voterCanEditThisChallenge, - voterIsChallengeParticipant, - voterWeVoteId, + challengeTitle, challengeWeVoteIdForDisplay, + scrolledDown, sharedByDisplayName, thanksForViewingChallengeOn, + voterCanEditThisChallenge, voterIsChallengeParticipant, voterWeVoteId, } = this.state; // console.log('ChallengeHomePage render challengeSEOFriendlyPath: ', challengeSEOFriendlyPath, ', challengeSEOFriendlyPathForDisplay: ', challengeSEOFriendlyPathForDisplay); const challengeAdminEditUrl = `${webAppConfig.WE_VOTE_SERVER_ROOT_URL}challenge/${challengeWeVoteId}/summary`; @@ -520,10 +537,11 @@ class ChallengeHomePage extends Component { ); return ( - + {thanksForViewingChallengeOn && ( + + )}  }> @@ -200,7 +201,15 @@ class ChallengeInviteCustomizeMessage extends Component { - Hi [your friend's name], it's David. + Hi [your friend's name] + {voterFirstName && ( + <> + , it's + {' '} + {voterFirstName} + + )} + . diff --git a/src/js/common/pages/ChallengeInviteFriends/ChallengeInviteFriends.jsx b/src/js/common/pages/ChallengeInviteFriends/ChallengeInviteFriends.jsx index 987bc91b6..be43f6f00 100644 --- a/src/js/common/pages/ChallengeInviteFriends/ChallengeInviteFriends.jsx +++ b/src/js/common/pages/ChallengeInviteFriends/ChallengeInviteFriends.jsx @@ -18,10 +18,12 @@ import { renderLog } from '../../utils/logging'; import DesignTokenColors from '../../components/Style/DesignTokenColors'; import ChallengeInviteSteps from '../../components/Navigation/ChallengeInviteSteps'; import ChallengeInviteeListRoot from '../../components/ChallengeInviteeListRoot/ChallengeInviteeListRoot'; +import ChallengeInviteeStore from '../../stores/ChallengeInviteeStore'; import InviteFriendToChallengeInput from '../../components/ChallengeInviteFriends/InviteFriendToChallengeInput'; import YourRank from '../../components/Challenge/YourRank'; const ChallengeRetrieveController = React.lazy(() => import(/* webpackChunkName: 'ChallengeRetrieveController' */ '../../components/Challenge/ChallengeRetrieveController')); +const FirstChallengeInviteeListController = React.lazy(() => import(/* webpackChunkName: 'ChallengeRetrieveController' */ '../../components/ChallengeInviteeListRoot/FirstChallengeInviteeListController')); const VoterFirstRetrieveController = loadable(() => import(/* webpackChunkName: 'VoterFirstRetrieveController' */ '../../components/Settings/VoterFirstRetrieveController')); @@ -34,6 +36,7 @@ class ChallengeInviteFriends extends Component { challengeTitle: '', challengeWeVoteId: '', chosenWebsiteName: '', + inviteeList: [], }; } @@ -42,6 +45,8 @@ class ChallengeInviteFriends extends Component { this.props.setShowHeaderFooter(false); this.onAppObservableStoreChange(); this.appStateSubscription = messageService.getMessage().subscribe(() => this.onAppObservableStoreChange()); + this.onChallengeInviteeStoreChange(); + this.challengeInviteeStoreListener = ChallengeInviteeStore.addListener(this.onChallengeInviteeStoreChange.bind(this)); this.onChallengeStoreChange(); this.challengeStoreListener = ChallengeStore.addListener(this.onChallengeStoreChange.bind(this)); const { match: { params } } = this.props; @@ -85,6 +90,7 @@ class ChallengeInviteFriends extends Component { componentWillUnmount () { this.props.setShowHeaderFooter(true); this.appStateSubscription.unsubscribe(); + this.challengeInviteeStoreListener.remove(); this.challengeStoreListener.remove(); } @@ -96,6 +102,16 @@ class ChallengeInviteFriends extends Component { }); } + onChallengeInviteeStoreChange () { + const { challengeWeVoteId } = this.state; + if (challengeWeVoteId) { + const inviteeList = ChallengeInviteeStore.getChallengeInviteeList(challengeWeVoteId); + this.setState({ + inviteeList, + }); + } + } + onChallengeStoreChange () { const { match: { params } } = this.props; const { challengeSEOFriendlyPath: challengeSEOFriendlyPathFromParams, challengeWeVoteId: challengeWeVoteIdFromParams } = params; @@ -153,7 +169,7 @@ class ChallengeInviteFriends extends Component { renderLog('ChallengeInviteFriends'); // Set LOG_RENDER_EVENTS to log all renders const { challengePhotoLargeUrl, challengeSEOFriendlyPath, challengeTitle, - challengeWeVoteId, chosenWebsiteName, + challengeWeVoteId, chosenWebsiteName, inviteeList, } = this.state; const htmlTitle = `Invite your friends - ${chosenWebsiteName}`; return ( @@ -197,16 +213,21 @@ class ChallengeInviteFriends extends Component { - - - - + {inviteeList.length > 0 && ( + + + + + )}  }>  }> + }> + + ); } diff --git a/src/js/common/stores/ChallengeParticipantStore.js b/src/js/common/stores/ChallengeParticipantStore.js index 5673ca1cf..9871bec13 100644 --- a/src/js/common/stores/ChallengeParticipantStore.js +++ b/src/js/common/stores/ChallengeParticipantStore.js @@ -6,7 +6,7 @@ import AppObservableStore from './AppObservableStore'; const orderByDateJoined = (firstEntry, secondEntry) => new Date(secondEntry.date_joined) - new Date(firstEntry.date_joined); -const orderByParticipantsCount = (firstEntry, secondEntry) => secondEntry.points - firstEntry.points; +const orderByPoints = (firstEntry, secondEntry) => secondEntry.points - firstEntry.points; class ChallengeParticipantStore extends ReduceStore { getInitialState () { @@ -196,7 +196,7 @@ class ChallengeParticipantStore extends ReduceStore { if (!action.res || !action.res.success) return state; revisedState = state; challengeParticipantList = action.res.challenge_participant_list || []; - // A little filtering to keep data clean and avoid duplicates + // A little filtering to keep data clean and avoid duplicate participant entries tied to the same voter challengeParticipantList.forEach((oneParticipant) => { if (!(oneParticipant.voter_we_vote_id in voterWeVoteIdList) && oneParticipant.challenge_we_vote_id === action.res.challenge_we_vote_id) { challengeParticipantListModified.push(oneParticipant); @@ -204,7 +204,7 @@ class ChallengeParticipantStore extends ReduceStore { voterWeVoteIdList.push(oneParticipant.voter_we_vote_id); }); challengeParticipantListModified = challengeParticipantListModified.sort(orderByDateJoined); - challengeParticipantListModified = challengeParticipantListModified.sort(orderByParticipantsCount); + challengeParticipantListModified = challengeParticipantListModified.sort(orderByPoints); challengeParticipantListModified = challengeParticipantListModified.map((participant, index) => ({ ...participant, rank: index + 1 })); challengeParticipantListModified.forEach((participant, index) => { if (index === 0) { diff --git a/src/js/common/stores/ChallengeStore.js b/src/js/common/stores/ChallengeStore.js index 0d2162bfe..8a522c32c 100644 --- a/src/js/common/stores/ChallengeStore.js +++ b/src/js/common/stores/ChallengeStore.js @@ -2,6 +2,7 @@ import { ReduceStore } from 'flux/utils'; import { avatarGeneric } from '../../utils/applicationUtils'; import Dispatcher from '../dispatcher/Dispatcher'; import arrayContains from '../utils/arrayContains'; +import AppObservableStore from './AppObservableStore'; import VoterStore from '../../stores/VoterStore'; import daysUntil from '../utils/daysUntil'; // eslint-disable-line import/no-cycle @@ -249,6 +250,14 @@ class ChallengeStore extends ReduceStore { return SUPPORTERS_COUNT_NEXT_GOAL_DEFAULT; } + getChallengeSEOFriendlyPathByWeVoteId (challengeWeVoteId) { + const challenge = this.getState().allCachedChallengeDicts[challengeWeVoteId]; + if (challenge === undefined || challenge.seo_friendly_path === undefined) { + return ''; + } + return challenge.seo_friendly_path; + } + getChallengeTitleByWeVoteId (challengeWeVoteId) { const challenge = this.getState().allCachedChallengeDicts[challengeWeVoteId]; if (challenge === undefined || challenge.challenge_title === undefined) { @@ -340,7 +349,7 @@ class ChallengeStore extends ReduceStore { if (challenge && challenge.site_url) { return challenge.site_url; } else { - return 'https://wevote.us'; + return AppObservableStore.getWeVoteRootURL(); } } diff --git a/src/js/common/stores/ShareStore.js b/src/js/common/stores/ShareStore.js index 824442cba..109ac635e 100644 --- a/src/js/common/stores/ShareStore.js +++ b/src/js/common/stores/ShareStore.js @@ -226,6 +226,9 @@ class ShareStore extends ReduceStore { if (action.res.shared_item_code_all_opinions) { allCachedSharedItemsBySharedItemCode[action.res.shared_item_code_all_opinions] = sharedItem; } + if (action.res.shared_item_code_challenge) { + allCachedSharedItemsBySharedItemCode[action.res.shared_item_code_challenge] = sharedItem; + } if (action.res.shared_item_code_ready) { allCachedSharedItemsBySharedItemCode[action.res.shared_item_code_ready] = sharedItem; } diff --git a/src/js/components/Ballot/BallotScrollingContainer.jsx b/src/js/components/Ballot/BallotScrollingContainer.jsx index 4deffbfe9..05a77ae22 100644 --- a/src/js/components/Ballot/BallotScrollingContainer.jsx +++ b/src/js/components/Ballot/BallotScrollingContainer.jsx @@ -100,7 +100,7 @@ class BallotScrollingContainer extends Component { }; handleContainerClick = (e, weVoteId) => { - console.log(e.target); + // console.log(e.target); const candidateContainer = document.getElementById(`candidateContainer-${weVoteId}`); const positionRowListOuterWrapper = document.getElementById(`positionRowListOuterWrapper-${weVoteId}`); const candidateDiv = document.getElementById(`candidateDiv-${weVoteId}`); @@ -116,7 +116,7 @@ class BallotScrollingContainer extends Component { const buttonWrapper = document.getElementById(`buttonWrapper-${weVoteId}`); // EAW VERIFY - might not need this one const candidateParty = document.getElementById(`candidateParty-${weVoteId}`); - console.log(buttonWrapper); + // console.log(buttonWrapper); if (e.target === candidateDiv || e.target === candidateContainer || e.target === positionRowListOuterWrapper || diff --git a/src/js/components/Ballot/PositionDrawer.jsx b/src/js/components/Ballot/PositionDrawer.jsx index ffd660604..a5c8b9222 100644 --- a/src/js/components/Ballot/PositionDrawer.jsx +++ b/src/js/components/Ballot/PositionDrawer.jsx @@ -120,7 +120,7 @@ class PositionDrawer extends Component { } // 2022-04-28 This slows down rendering too much - // // We want to make sure we have all of the position information so that comments show up + // // We want to make sure we have all the position information so that comments show up // const voterGuidesForThisBallotItem = VoterGuideStore.getVoterGuidesToFollowForBallotItemId(ballotItemWeVoteId); // // if (voterGuidesForThisBallotItem) { diff --git a/src/js/components/Ballot/PositionItem.jsx b/src/js/components/Ballot/PositionItem.jsx index ff05e0fe4..db5ec88cb 100644 --- a/src/js/components/Ballot/PositionItem.jsx +++ b/src/js/components/Ballot/PositionItem.jsx @@ -47,7 +47,7 @@ class PositionItem extends Component { this.voterGuideStoreListener = VoterGuideStore.addListener(this.onVoterGuideStoreChange.bind(this)); // This creates too much load on the browser - // // We want to make sure we have all of the position information so that comments show up + // // We want to make sure we have all the position information so that comments show up // if (ballotItemWeVoteId) { // const voterGuidesForThisBallotItem = VoterGuideStore.getVoterGuidesToFollowForBallotItemId(ballotItemWeVoteId); // @@ -105,7 +105,7 @@ class PositionItem extends Component { // const { ballot_item_we_vote_id: ballotItemWeVoteId, speaker_we_vote_id: organizationWeVoteId } = position; // This puts too much strain on the browser since PositionItems are in a list - // // We want to make sure we have all of the position information so that comments show up + // // We want to make sure we have all the position information so that comments show up // if (ballotItemWeVoteId) { // const voterGuidesForThisBallotItem = VoterGuideStore.getVoterGuidesToFollowForBallotItemId(ballotItemWeVoteId); // diff --git a/src/js/components/Ballot/PositionItemSquare.jsx b/src/js/components/Ballot/PositionItemSquare.jsx index b9f068e9e..a0c567812 100644 --- a/src/js/components/Ballot/PositionItemSquare.jsx +++ b/src/js/components/Ballot/PositionItemSquare.jsx @@ -31,7 +31,7 @@ class PositionItemSquare extends Component { this.voterGuideStoreListener = VoterGuideStore.addListener(this.onVoterGuideStoreChange.bind(this)); // Creates too much load on the browser - // // We want to make sure we have all of the position information so that comments show up + // // We want to make sure we have all the position information so that comments show up // if (ballotItemWeVoteId) { // const voterGuidesForThisBallotItem = VoterGuideStore.getVoterGuidesToFollowForBallotItemId(ballotItemWeVoteId); // @@ -89,7 +89,7 @@ class PositionItemSquare extends Component { // const { position } = this.props; // const { ballot_item_we_vote_id: ballotItemWeVoteId, speaker_we_vote_id: organizationWeVoteId } = position; // - // // We want to make sure we have all of the position information so that comments show up + // // We want to make sure we have all the position information so that comments show up // if (ballotItemWeVoteId) { // const voterGuidesForThisBallotItem = VoterGuideStore.getVoterGuidesToFollowForBallotItemId(ballotItemWeVoteId); // diff --git a/src/js/components/Ballot/PositionRowItem.jsx b/src/js/components/Ballot/PositionRowItem.jsx index 2d7ba7033..513d8f34c 100644 --- a/src/js/components/Ballot/PositionRowItem.jsx +++ b/src/js/components/Ballot/PositionRowItem.jsx @@ -66,7 +66,7 @@ class PositionRowItem extends Component { const { position } = this.props; const { ballot_item_we_vote_id: ballotItemWeVoteId, speaker_we_vote_id: organizationWeVoteId } = position; - // We want to make sure we have all of the position information so that comments show up + // We want to make sure we have all the position information so that comments show up if (ballotItemWeVoteId) { const voterGuidesForThisBallotItem = VoterGuideStore.getVoterGuidesToFollowForBallotItemId(ballotItemWeVoteId); diff --git a/src/js/components/Ready/ReadyTaskBallot.jsx b/src/js/components/Ready/ReadyTaskBallot.jsx index 85852c13c..13d674e47 100644 --- a/src/js/components/Ready/ReadyTaskBallot.jsx +++ b/src/js/components/Ready/ReadyTaskBallot.jsx @@ -32,7 +32,7 @@ class ReadyTaskBallot extends React.Component { allCandidatesButtonNeeded: false, // Are there candidates on this ballot? allCandidatesAllCompleted: false, allCandidatesNumberCompleted: 0, - allCandidatesShowButton: false, // Given all of the buttons we need to show, should this one be "unfurled"? + allCandidatesShowButton: false, // Given all the buttons we need to show, should this one be "unfurled"? allCandidatesTotalNumber: 0, federalButtonNeeded: false, // Are there Federal candidates on this ballot? federalAllCompleted: false, @@ -256,7 +256,7 @@ class ReadyTaskBallot extends React.Component { // If there are measures, measureShowButton is always true measureShowButton = (measureButtonNeeded); // console.log('measureButtonNeeded:', measureButtonNeeded, ', measureShowButton:', measureShowButton); - // If all of the possible decisions have been made + // If all the possible decisions have been made allDecisionsMadeCount += federalAllCompleted ? 1 : 0; allDecisionsMadeCount += localAllCompleted ? 1 : 0; allDecisionsMadeCount += measureAllCompleted ? 1 : 0; diff --git a/src/js/components/Ready/VoterPlanModal.jsx b/src/js/components/Ready/VoterPlanModal.jsx index 78a91c667..431203e31 100644 --- a/src/js/components/Ready/VoterPlanModal.jsx +++ b/src/js/components/Ready/VoterPlanModal.jsx @@ -70,7 +70,7 @@ class VoterPlanModal extends Component { } componentDidUpdate (prevProps, prevState) { - // Update the Json that we save with all of the settings + // Update the Json that we save with all the settings const { approximateTime, electionDateMonthYear, locationToDeliverBallot, modeOfTransport, showToPublic, voterPlanDataSerializedCalculatedFirstTime, diff --git a/src/js/components/Values/IssuesByBallotItemDisplayList.jsx b/src/js/components/Values/IssuesByBallotItemDisplayList.jsx index faf50101d..991d4eac8 100644 --- a/src/js/components/Values/IssuesByBallotItemDisplayList.jsx +++ b/src/js/components/Values/IssuesByBallotItemDisplayList.jsx @@ -7,7 +7,7 @@ import VoterGuideStore from '../../stores/VoterGuideStore'; import signInModalGlobalState from '../../common/components/Widgets/signInModalGlobalState'; import ValueNameWithPopoverDisplay from './ValueNameWithPopoverDisplay'; -// Show a voter a horizontal list of all of the issues they are following that relate to this ballot item +// Show a voter a horizontal list of all the issues they are following that relate to this ballot item class IssuesByBallotItemDisplayList extends Component { static closePopover () { document.body.click(); diff --git a/src/js/components/Values/IssuesByOrganizationDisplayList.jsx b/src/js/components/Values/IssuesByOrganizationDisplayList.jsx index 4943d7315..f7d873532 100644 --- a/src/js/components/Values/IssuesByOrganizationDisplayList.jsx +++ b/src/js/components/Values/IssuesByOrganizationDisplayList.jsx @@ -14,7 +14,7 @@ import IssueFollowToggleButton from './IssueFollowToggleButton'; const ReadMore = React.lazy(() => import(/* webpackChunkName: 'ReadMore' */ '../../common/components/Widgets/ReadMore')); -// Show a voter a horizontal list of all of the issues they are following that relate to this ballot item +// Show a voter a horizontal list of all the issues they are following that relate to this ballot item class IssuesByOrganizationDisplayList extends Component { static closePopover () { document.body.click(); diff --git a/src/js/pages/SharedItemLanding.jsx b/src/js/pages/SharedItemLanding.jsx index 5b1aa3098..dfd8e2802 100644 --- a/src/js/pages/SharedItemLanding.jsx +++ b/src/js/pages/SharedItemLanding.jsx @@ -18,6 +18,8 @@ export default class SharedItemLanding extends Component { customLinkString: '', destinationFullUrl: '', destinationFullUrlOverride: '', + isBallotShare: false, + isChallengeShare: false, sharedItemCodeIncoming: '', sharedItemCodeRetrieved: false, waitForVoterDeviceId: false, @@ -59,41 +61,48 @@ export default class SharedItemLanding extends Component { // destinationFullUrl, // }); } + // console.log('sharedItemCodeIncoming:', sharedItemCodeIncoming); if (sharedItemCodeIncoming) { const sharedItem = ShareStore.getSharedItemByCode(sharedItemCodeIncoming); // console.log('sharedItem:', sharedItem); - const { - destination_full_url: destinationFullUrl, - destination_full_url_override: destinationFullUrlOverride, - email_secret_key: emailSecretKey, - is_ballot_share: isBallotShare, - shared_item_code_all_opinions: sharedItemCodeAllOpinions, - other_voter_display_name: voterDisplayName, - other_voter_first_name: voterFirstName, - other_voter_last_name: voterLastName, - } = sharedItem; - // console.log('SharedItemLanding emailSecretKey:', emailSecretKey); - let waitForVoterDeviceId = false; - if (emailSecretKey) { - if (VoterStore.voterDeviceId()) { - // We trigger this here (instead of on the API server with ShareActions.sharedItemRetrieveByCode) - // to reduce delay around first page display - // If the email hasn't been previously verified, this verifies it and attaches it to this account - // console.log('SharedItemLanding firstName:', voterFirstName, ', lastName:', voterLastName, ', fullName:', voterDisplayName); - VoterActions.voterEmailAddressVerify(emailSecretKey, voterFirstName, voterLastName, voterDisplayName); - // VoterActions.voterFullNameSoftSave(voterFirstName, voterLastName, voterDisplayName); - } else { - waitForVoterDeviceId = true; + if (sharedItem && sharedItem.destination_full_url) { + const { + destination_full_url: destinationFullUrl, + destination_full_url_override: destinationFullUrlOverride, + email_secret_key: emailSecretKey, + is_ballot_share: isBallotShare, + is_challenge_share: isChallengeShare, + shared_item_code_all_opinions: sharedItemCodeAllOpinions, + other_voter_display_name: voterDisplayName, + other_voter_first_name: voterFirstName, + other_voter_last_name: voterLastName, + } = sharedItem; + // console.log('SharedItemLanding emailSecretKey:', emailSecretKey); + let waitForVoterDeviceId = false; + if (emailSecretKey) { + if (VoterStore.voterDeviceId()) { + // We trigger this here (instead of on the API server with ShareActions.sharedItemRetrieveByCode) + // to reduce delay around first page display + // If the email hasn't been previously verified, this verifies it and attaches it to this account + // console.log('SharedItemLanding firstName:', voterFirstName, ', lastName:', voterLastName, ', fullName:', voterDisplayName); + VoterActions.voterEmailAddressVerify(emailSecretKey, voterFirstName, voterLastName, voterDisplayName); + // VoterActions.voterFullNameSoftSave(voterFirstName, voterLastName, voterDisplayName); + } else { + waitForVoterDeviceId = true; + } } + this.setState({ + destinationFullUrl, + destinationFullUrlOverride, + isBallotShare, + isChallengeShare, + sharedItemCodeAllOpinions, + sharedItemCodeRetrieved: true, + waitForVoterDeviceId, + }); + } else { + console.log('SharedItemLanding destination_full_url not found'); } - this.setState({ - destinationFullUrl, - destinationFullUrlOverride, - isBallotShare, - sharedItemCodeAllOpinions, - sharedItemCodeRetrieved: true, - waitForVoterDeviceId, - }); } } @@ -110,7 +119,11 @@ export default class SharedItemLanding extends Component { render () { renderLog('SharedItemLanding'); // Set LOG_RENDER_EVENTS to log all renders - const { componentDidMount, isBallotShare, destinationFullUrlOverride, sharedItemCodeAllOpinions, sharedItemCodeIncoming, sharedItemCodeRetrieved } = this.state; + const { + componentDidMount, destinationFullUrlOverride, + isBallotShare, isChallengeShare, + sharedItemCodeAllOpinions, sharedItemCodeIncoming, sharedItemCodeRetrieved, + } = this.state; let { destinationFullUrl } = this.state; // console.log('sharedItemCodeIncoming:', sharedItemCodeIncoming, 'sharedItemCodeAllOpinions:', sharedItemCodeAllOpinions); // console.log('destinationFullUrl:', destinationFullUrl, 'destinationFullUrlOverride:', destinationFullUrlOverride); @@ -124,7 +137,7 @@ export default class SharedItemLanding extends Component { // console.log('SharedItemLanding sharedItemCodeRetrieved not retrieved'); return LoadingWheel; } else if (sharedItemCodeRetrieved && (destinationFullUrl === undefined || destinationFullUrl === '')) { - // console.log('SharedItemLanding destinationFullUrl undefined'); + // console.log('SharedItemLanding destinationFullUrl undefined, directing to /ready'); this.localHistoryPush('/ready'); return LoadingWheel; } else { @@ -138,14 +151,20 @@ export default class SharedItemLanding extends Component { // console.log('Ballot Share AllOpinions'); return ; } else if (destinationFullUrl && destinationFullUrl.startsWith(hrefHostname)) { - let destinationLocalUrl = destinationFullUrl.replace(hrefHostname, ''); - destinationLocalUrl = destinationLocalUrl.replace(':3000', ''); // For local development machines - this.localHistoryPush(destinationLocalUrl); - // const destinationLocalUrlWithModal = `${destinationLocalUrl}/modal/sic/${sharedItemCodeIncoming}`; - // // console.log('*** WILL Direct to LOCAL destinationLocalUrlWithModal:', destinationLocalUrlWithModal); - // historyPush(destinationLocalUrlWithModal); + let destinationPath = destinationFullUrl.replace(hrefHostname, ''); + destinationPath = destinationPath.replace(':3000', ''); // For local development machines + if (isChallengeShare) { + // Add the sharedItemCode to the end of the URL so we have access to the sharedItem data on the next page + destinationPath += `-${sharedItemCodeIncoming}`; + } + // console.log('SharedItemLanding destinationPath:', destinationPath); + this.localHistoryPush(destinationPath); + // const destinationPathWithModal = `${destinationPath}/modal/sic/${sharedItemCodeIncoming}`; + // // console.log('*** WILL Direct to LOCAL destinationPathWithModal:', destinationPathWithModal); + // historyPush(destinationPathWithModal); return LoadingWheel; } else { + // console.log('SharedItemLanding destinationFullUrl:', destinationFullUrl); this.localHistoryPush(destinationFullUrl); // const destinationFullUrlWithModal = `${destinationFullUrl}/modal/sic/${sharedItemCodeIncoming}`; // // console.log('*** WILL Direct to EXTERNAL destinationFullUrlWithModal:', destinationFullUrlWithModal); diff --git a/src/js/stores/FriendStore.js b/src/js/stores/FriendStore.js index 685a55db1..5ba54e882 100644 --- a/src/js/stores/FriendStore.js +++ b/src/js/stores/FriendStore.js @@ -459,7 +459,7 @@ class FriendStore extends ReduceStore { if (!action.res.success) { return state; } else { - // Firing all of these "just in case" api queries is slow, and firing queries from Stores should bed avoidd + // Firing all these "just in case" api queries is slow, and firing queries from Stores should bed avoidd // console.log('resetting FriendStore from voterSignOut'); if (apiCalming('friendListsAll', 1500)) { FriendActions.friendListsAll(); diff --git a/src/js/stores/IssueStore.js b/src/js/stores/IssueStore.js index d92b81d7e..95cea01ff 100644 --- a/src/js/stores/IssueStore.js +++ b/src/js/stores/IssueStore.js @@ -237,7 +237,7 @@ class IssueStore extends ReduceStore { // if (!ballotItemWeVoteId) { // return 0; // } - // // These are scores based on all of the organizations under all of the issues a voter follows + // // These are scores based on all the organizations under all the issues a voter follows // const issueScore = this.getState().issueScoreForEachBallotItem[ballotItemWeVoteId]; // if (issueScore === undefined) { // return 0; @@ -876,7 +876,7 @@ class IssueStore extends ReduceStore { }; case 'voterGuidesUpcomingRetrieve': // List of all public voter guides from CDN - // Collect all of the issues an organization is tagged with + // Collect all the issues an organization is tagged with // console.log('IssueStore, case voterGuidesToFollowRetrieve'); voterGuides = action.res.voter_guides; if (!voterGuides || voterGuides.length === 0) {