Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 93 additions & 19 deletions src/js/common/components/Position/PositionForBallotItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ import { Avatar, Typography } from '@mui/material';
import { withStyles } from '@mui/styles';
import HeartFavoriteToggleLoader from '../Widgets/HeartFavoriteToggle/HeartFavoriteToggleLoader';
import ThumbsUpDownToggle from '../Widgets/ThumbsUpDownToggle/ThumbsUpDownToggle';
import { timeFromDate } from '../../utils/dateFormat';
import DesignTokenColors from '../Style/DesignTokenColors';
import { CompactSecondaryText, CompactStatementText, SpeakerInfoWrapper, SpeakerName, SpeakerStatement, SpeakerStatementWrapper } from '../Style/PositionDisplayStyles';
import { CompactStatementText, SpeakerInfoWrapper, SpeakerName, SpeakerStatement, SpeakerStatementWrapper } from '../Style/PositionDisplayStyles';
import speakerDisplayNameToInitials from '../../utils/speakerDisplayNameToInitials';
import SpeakerEndorsedOrOpposedSnippet from './SpeakerEndorsedOrOpposedSnippet';
import AppObservableStore from '../../stores/AppObservableStore';
import stringContains from '../../utils/stringContains';
import lookupPageNameAndPageTypeDict from '../../../utils/lookupPageNameAndPageTypeDict';

const EndorsementDetailModal = React.lazy(() => import(/* webpackChunkName: 'EndorsementDetailModal' */ '../../../components/Ballot/EndorsementDetailModal'));

const OpenExternalWebSite = React.lazy(() => import(/* webpackChunkName: 'OpenExternalWebSite' */ '../Widgets/OpenExternalWebSite'));
const ReadMore = React.lazy(() => import(/* webpackChunkName: 'ReadMore' */ '../Widgets/ReadMore'));

function PositionForBallotItem ({ classes, compactMode, linksOpenExternalWebsite, position }) {
const [anchorEl, setAnchorEL] = useState(null);
const [detailModalOpen, setDetailModalOpen] = useState(false);

const onDotButtonClick = (e) => {
setAnchorEL(e.currentTarget);
Expand Down Expand Up @@ -111,7 +113,19 @@ function PositionForBallotItem ({ classes, compactMode, linksOpenExternalWebsite
</SpeakerInfoNameFavoritesWrapper>
)}
{statementText && compactMode && (
<CompactStatementText>{statementText}</CompactStatementText>
<CompactStatementClickable
onClick={() => setDetailModalOpen(true)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setDetailModalOpen(true);
}
}}
role="button"
tabIndex={0}
>
<CompactStatementText>{statementText}</CompactStatementText>
</CompactStatementClickable>
)}
{statementText && !compactMode && (
<SpeakerStatementWrapper>
Expand All @@ -126,13 +140,14 @@ function PositionForBallotItem ({ classes, compactMode, linksOpenExternalWebsite
</SpeakerStatementWrapper>
)}
{compactMode && (
<CompactTimestamp>
{speakerDisplayName}
{position.is_support && <CompactStanceText $support>{' endorsed '}</CompactStanceText>}
{position.is_oppose && <CompactStanceText $oppose>{' opposed '}</CompactStanceText>}
{!position.is_support && !position.is_oppose && ' commented '}
{timeFromDate(position.last_updated || position.date_entered)}
</CompactTimestamp>
<CompactSpeakerRow>
<CompactSpeakerName>{speakerDisplayName}</CompactSpeakerName>
{(campaignXWeVoteId || organizationWeVoteId) && (
<CompactHeartWrapper>
<HeartFavoriteToggleLoader campaignXWeVoteId={campaignXWeVoteId} organizationWeVoteId={organizationWeVoteId} />
</CompactHeartWrapper>
)}
</CompactSpeakerRow>
)}
{!compactMode && (
<SpeakerPositionLikesSourceWrapper>
Expand Down Expand Up @@ -195,6 +210,15 @@ function PositionForBallotItem ({ classes, compactMode, linksOpenExternalWebsite
</SpeakerPositionLikesSourceWrapper>
)}
</SpeakerInfoWrapper>
{compactMode && (
<Suspense fallback={<></>}>
<EndorsementDetailModal
isOpen={detailModalOpen}
onClose={() => setDetailModalOpen(false)}
position={position}
/>
</Suspense>
)}
</PositionForBallotItemWrapper>
);
}
Expand All @@ -213,17 +237,61 @@ const styles = () => ({
},
});

const CompactStanceText = styled('span', {
shouldForwardProp: (prop) => !['$support', '$oppose'].includes(prop),
})`
color: ${({ $support, $oppose }) => {
if ($support) return DesignTokenColors.confirmation700;
if ($oppose) return DesignTokenColors.alert700;
return 'inherit';
}};
const CompactHeartWrapper = styled('div')`
align-items: center;
display: flex;
flex-shrink: 0;
margin-left: 8px;
/* Strip the pill chrome on HeartFavoriteToggleContainer (3 wrappers deep:
Loader's container > Live's container > Base's pill container). */
& > div > div > div {
background: transparent;
border: none;
border-radius: 0;
height: auto;
padding: 0;
}
/* Center the icon and the count text on the same baseline inside each toggle button. */
& button {
align-items: center;
}
& svg {
height: 20px;
width: 20px;
}
& span {
font-size: 14px;
line-height: 1;
}
`;

const CompactSpeakerName = styled('div')`
color: ${DesignTokenColors.neutralUI900};
flex: 1;
font-size: 13px;
font-weight: 500;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;

const CompactSpeakerRow = styled('div')`
align-items: center;
display: flex;
margin-top: 4px;
`;

const CompactTimestamp = CompactSecondaryText;
const CompactStatementClickable = styled('div')`
cursor: pointer;
&:hover {
text-decoration: underline;
}
&:focus-visible {
outline: 2px solid ${DesignTokenColors.primary500};
border-radius: 4px;
}
`;

const FlexDiv = styled('div')`
display: flex;
Expand Down Expand Up @@ -264,6 +332,12 @@ const PositionForBallotItemWrapper = styled('div', {
}

${({ $compactMode }) => $compactMode && `
/* Let SpeakerInfoWrapper take the remaining row width AND allow it to shrink,
so the speaker name's ellipsis can kick in and the heart toggle stays in view. */
${SpeakerInfoWrapper} {
flex: 1;
min-width: 0;
}
h3 {
font-size: 14px;
font-weight: 600;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ const CheckOutlinedStyled = styled(CheckOutlined)`
`;

const SpeakerPosition = styled('div')`
align-items: center;
display: flex;
`;

Expand Down
2 changes: 1 addition & 1 deletion src/js/common/components/Style/PositionDisplayStyles.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const PositionText = styled('div')`

export const SpeakerInfoWrapper = styled('div')`
display: flex;
margin-bottom: 12px;
margin-bottom: 6px;
margin-left: 15px;
flex-direction: column;
// width: 500px;
Expand Down
12 changes: 11 additions & 1 deletion src/js/common/stores/AppObservableStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const nonFluxState = {
showEditPositionModal: false,
editPositionModalPoliticianWeVoteId: '',
editPositionModalSetPublic: false,
editPositionModalVisibilityOnly: false,
showClaimProfileWithOtherWaysModal: false,
functionToCallAfterProfileComplete: null,
showCompleteYourProfileModal: false,
Expand Down Expand Up @@ -219,6 +220,10 @@ export default {
return nonFluxState.editPositionModalSetPublic;
},

getEditPositionModalVisibilityOnly () {
return nonFluxState.editPositionModalVisibilityOnly;
},

getShowClaimProfileWithOtherWaysModal () {
return nonFluxState.showClaimProfileWithOtherWaysModal;
},
Expand Down Expand Up @@ -527,10 +532,15 @@ export default {
nonFluxState.editPositionModalSetPublic = value;
},

setShowEditPositionModal (show, politicianWeVoteId = '', setPublic = false) {
setEditPositionModalVisibilityOnly (value) {
nonFluxState.editPositionModalVisibilityOnly = value;
},

setShowEditPositionModal (show, politicianWeVoteId = '', setPublic = false, visibilityOnly = false) {
nonFluxState.showEditPositionModal = show;
nonFluxState.editPositionModalPoliticianWeVoteId = show ? politicianWeVoteId : '';
nonFluxState.editPositionModalSetPublic = show ? setPublic : false;
nonFluxState.editPositionModalVisibilityOnly = show ? visibilityOnly : false;
messageService.sendMessage('state updated showEditPositionModal');
},

Expand Down
17 changes: 12 additions & 5 deletions src/js/components/Ballot/CandidateOpinionsColumn.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import DesignTokenColors from '../../common/components/Style/DesignTokenColors';
import { renderLog } from '../../common/utils/logging';
import AppObservableStore from '../../common/stores/AppObservableStore';
import CandidateStore from '../../stores/CandidateStore';
import OrganizationStore from '../../stores/OrganizationStore';
import VoterStore from '../../stores/VoterStore';
import { getPositionFollowersCount } from './opinionsHelpers';
import PositionList from './PositionList';

const VoterPositionEntryAndDisplay = React.lazy(() => import(/* webpackChunkName: 'VoterPositionEntryAndDisplay' */ '../PositionItem/VoterPositionEntryAndDisplay'));
Expand All @@ -23,22 +25,27 @@ class CandidateOpinionsColumn extends Component {

componentDidMount () {
this.candidateStoreListener = CandidateStore.addListener(this.onStoreChange.bind(this));
this.organizationStoreListener = OrganizationStore.addListener(this.onStoreChange.bind(this));
this.onStoreChange();
}

componentWillUnmount () {
this.candidateStoreListener.remove();
this.organizationStoreListener.remove();
}

onStoreChange () {
const { candidateWeVoteId } = this.props;
const allPositions = CandidateStore.getAllCachedPositionsByCandidateWeVoteId(candidateWeVoteId);
const currentVoterWeVoteId = VoterStore.getLinkedOrganizationWeVoteId();
const opinions = allPositions.filter(
(position) => position.statement_text && position.statement_text.length > 0 &&
!(position.speaker_display_name && position.speaker_display_name.startsWith('Voter-')) &&
position.speaker_we_vote_id !== currentVoterWeVoteId,
);
const opinions = allPositions
.filter(
(position) => position.is_support &&
position.statement_text && position.statement_text.length > 0 &&
!(position.speaker_display_name && position.speaker_display_name.startsWith('Voter-')) &&
position.speaker_we_vote_id !== currentVoterWeVoteId,
)
.sort((a, b) => getPositionFollowersCount(b) - getPositionFollowersCount(a));
this.setState({
opinions,
opinionsCount: opinions.length,
Expand Down
Loading
Loading