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
14 changes: 11 additions & 3 deletions src/components/newsletterSenderInfo/newsletterSenderInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const NewsletterSenderInfo = ({ username }: Props) => {

return (
<View style={styles.row}>
<Text style={styles.countText}>
<Text style={styles.countText} numberOfLines={1}>
{intl.formatMessage(
{ id: 'newsletter.subscriber_count' },
{ weekly: subscribers.weekly ?? 0, monthly: subscribers.monthly ?? 0 },
Expand All @@ -58,28 +58,36 @@ const NewsletterSenderInfo = ({ username }: Props) => {
onPress={_handleCopyLink}
/>
<TouchableOpacity onPress={() => navigation.navigate(ROUTES.SCREENS.EMAIL_DIGESTS)}>
<Text style={styles.manageText}>{intl.formatMessage({ id: 'newsletter.manage' })}</Text>
<Text style={styles.manageText} numberOfLines={1}>
{intl.formatMessage({ id: 'newsletter.manage' })}
</Text>
</TouchableOpacity>
</View>
);
};

const styles = EStyleSheet.create({
// Natural (unfixed) height ON PURPOSE: the row lives OUTSIDE the summary
// card's one-shot height measurement, so it may grow with accessibility
// font scaling and its 30pt icon without clipping anything
// (vision-mobile#3522).
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 16,
paddingTop: 6,
paddingBottom: 4,
},
countText: {
flexShrink: 1,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. manage width remains unconstrained 📎 Requirement gap ≡ Correctness

The PR makes the count text shrink, but the Manage action still has no fixed-width container or
width constraint. A long localized action label can therefore consume or exceed the available row
width on small devices, contrary to the required fixed action area.
Agent Prompt
## Issue description
The subscriber count now shrinks, but the `Manage` action remains content-sized rather than occupying a fixed or otherwise bounded action area.

## Issue Context
Compliance rule 1 requires fixed icon and action areas so localized labels cannot push controls off-screen on small devices. Add an explicit bounded wrapper style for the action while preserving a usable single-line label.

## Fix Focus Areas
- src/components/newsletterSenderInfo/newsletterSenderInfo.tsx[79-81]
- src/components/newsletterSenderInfo/newsletterSenderInfo.tsx[94-103]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c45ed63: the Manage label is single line with maxWidth 120, and the count text keeps flexShrink with one line, so a long localized action cannot push the row content off narrow screens.

fontSize: 13,
color: '$primaryDarkGray',
},
manageText: {
fontSize: 13,
color: '$primaryBlue',
marginLeft: 8,
maxWidth: 120,
},
});

Expand Down
94 changes: 53 additions & 41 deletions src/components/profile/profileView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { CollapsibleCard } from '../collapsibleCard';
import { Header } from '../header';
import { ProfileSummaryPlaceHolder, WalletDetailsPlaceHolder } from '../basicUIElements';
import { NewsletterSenderInfo } from '../newsletterSenderInfo';
import { ProfileSummary } from '../profileSummary';
import { Wallet } from '../wallet';
import { IconButton } from '../iconButton';
Expand Down Expand Up @@ -214,47 +215,58 @@ class ProfileView extends PureComponent<any, any> {
return !isReady ? (
<ProfileSummaryPlaceHolder />
) : (
<CollapsibleCard
title=""
defaultTitle=""
titleComponent={<View />}
isExpanded={isSummaryOpen}
handleOnExpanded={this._handleOnSummaryExpanded}
moreHeight={collapsibleMoreHeight}
fitContent
noBorder
>
<ProfileSummary
date={getFormatedCreatedDate(get(selectedUser, 'created'))}
about={about}
displayName={displayName}
reputation={reputation ? parseReputation(reputation) : ''}
followerCount={follows ? follows.follower_count : 0}
followingCount={follows ? follows.following_count : 0}
handleFollowUnfollowUser={handleFollowUnfollowUser}
handleMessage={handleMessage}
handleMuteUnmuteUser={handleMuteUnmuteUser}
handleOnFavoritePress={handleOnFavoritePress}
handleOnFollowsPress={handleOnFollowsPress}
handleReportUser={handleReportUser}
handleDelegateHp={handleDelegateHp}
handleUIChange={this._handleUIChange}
hoursRC={Math.ceil((100 - resourceCredits) * 0.833333) || null}
hoursVP={Math.ceil((100 - votingPower) * 0.833333) || null}
intl={intl}
isDarkTheme={isDarkTheme}
isFavorite={isFavorite}
isFollowing={isFollowing}
isLoggedIn={isLoggedIn}
isMuted={isMuted}
isOwnProfile={isOwnProfile}
isProfileLoading={isProfileLoading}
percentRC={resourceCredits}
percentVP={votingPower}
handleOnPressProfileEdit={handleOnPressProfileEdit}
username={username}
/>
</CollapsibleCard>
<>
<CollapsibleCard
title=""
defaultTitle=""
titleComponent={<View />}
isExpanded={isSummaryOpen}
handleOnExpanded={this._handleOnSummaryExpanded}
moreHeight={collapsibleMoreHeight}
fitContent
noBorder
>
<ProfileSummary
date={getFormatedCreatedDate(get(selectedUser, 'created'))}
about={about}
displayName={displayName}
reputation={reputation ? parseReputation(reputation) : ''}
followerCount={follows ? follows.follower_count : 0}
followingCount={follows ? follows.following_count : 0}
handleFollowUnfollowUser={handleFollowUnfollowUser}
handleMessage={handleMessage}
handleMuteUnmuteUser={handleMuteUnmuteUser}
handleOnFavoritePress={handleOnFavoritePress}
handleOnFollowsPress={handleOnFollowsPress}
handleReportUser={handleReportUser}
handleDelegateHp={handleDelegateHp}
handleUIChange={this._handleUIChange}
hoursRC={Math.ceil((100 - resourceCredits) * 0.833333) || null}
hoursVP={Math.ceil((100 - votingPower) * 0.833333) || null}
intl={intl}
isDarkTheme={isDarkTheme}
isFavorite={isFavorite}
isFollowing={isFollowing}
isLoggedIn={isLoggedIn}
isMuted={isMuted}
isOwnProfile={isOwnProfile}
isProfileLoading={isProfileLoading}
percentRC={resourceCredits}
percentVP={votingPower}
handleOnPressProfileEdit={handleOnPressProfileEdit}
username={username}
/>
</CollapsibleCard>
{/* Outside the CollapsibleCard ON PURPOSE: the card measures its content
ONCE, and this row appears only after the sender query resolves, so
inside it the row was clipped (and reserving a fixed height fought
font scaling and cached-data double counts). As a sibling it takes
natural height in normal flow; it follows the summary's collapse
state so a collapsed header hides it too. */}
{!!isOwnProfile && !!username && isSummaryOpen && (
<NewsletterSenderInfo username={username} />
)}
</>
);
};

Expand Down
3 changes: 3 additions & 0 deletions src/components/profileSummary/view/profileSummaryStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ export default EStyleSheet.create({
fontSize: 13,
color: '$primaryDarkText',
},
newsletterButton: {
marginLeft: 4,
},
messageButton: {
borderColor: '$iconColor',
borderWidth: 1,
Expand Down
29 changes: 18 additions & 11 deletions src/components/profileSummary/view/profileSummaryView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import DARK_COVER_IMAGE from '../../../assets/dark_cover_image.png';
import { TextWithIcon } from '../../basicUIElements';
import { PercentBar } from '../../percentBar';
import { DropdownButton } from '../../dropdownButton';
import { IconButton } from '../../iconButton';
import { UserAvatar } from '../../userAvatar';
import { ProBadge } from '../../proBadge';

Expand All @@ -24,7 +25,6 @@ import { makeCountFriendly } from '../../../utils/formatter';
import styles from './profileSummaryStyles';
import getWindowDimensions from '../../../utils/getWindowDimensions';
import { SheetNames } from '../../../navigation/sheets';
import { NewsletterSenderInfo } from '../../newsletterSenderInfo';

const DEVICE_WIDTH = getWindowDimensions().width;

Expand All @@ -42,6 +42,12 @@ class ProfileSummaryView extends PureComponent<any, any> {
}
};

_handleNewsletterPress = () => {
SheetManager.show(SheetNames.NEWSLETTER_DIGEST, {
payload: { type: 'creator', target: this.props.username },
});
};

_handleOnDropdownSelect = (index: any) => {
const {
isMuted,
Expand Down Expand Up @@ -73,13 +79,6 @@ class ProfileSummaryView extends PureComponent<any, any> {
handleReportUser();
}
break;
case 4:
// Appended LAST on purpose: the dropdown dispatches by index, so a
// middle insertion would silently reroute the actions below it.
SheetManager.show(SheetNames.NEWSLETTER_DIGEST, {
payload: { type: 'creator', target: this.props.username },
});
break;
default:
Alert.alert('Action not implemented');
break;
Expand Down Expand Up @@ -138,7 +137,6 @@ class ProfileSummaryView extends PureComponent<any, any> {
intl.formatMessage({ id: 'user.delegate' }),
intl.formatMessage({ id: !isMuted ? 'user.mute' : 'user.unmute' }),
intl.formatMessage({ id: 'user.report' }),
intl.formatMessage({ id: 'newsletter.profile_option' }),
];
}

Expand Down Expand Up @@ -169,6 +167,17 @@ class ProfileSummaryView extends PureComponent<any, any> {
</Text>
</TouchableOpacity>

<IconButton
style={styles.newsletterButton}
iconType="MaterialCommunityIcons"
name="email-outline"
size={26}
color={EStyleSheet.value('$primaryBlue')}
disabled={isProfileLoading}
onPress={this._handleNewsletterPress}
accessibilityLabel={intl.formatMessage({ id: 'newsletter.profile_option' })}
/>

{isProfileLoading ? (
<ActivityIndicator
color={EStyleSheet.value('$primaryBlue')}
Expand Down Expand Up @@ -315,15 +324,13 @@ class ProfileSummaryView extends PureComponent<any, any> {
};

render() {
const { isOwnProfile, username } = this.props;
return (
<Fragment>
{this._renderCoverImage()}
{this._renderAvatarAndActions()}
{this._renderIdentity()}
{this._renderMetadata()}
{this._renderFollowerStats()}
{!!isOwnProfile && !!username && <NewsletterSenderInfo username={username} />}
{this._renderBars()}
</Fragment>
);
Expand Down
Loading