mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
feat(app): show current DM settings and keep them by default (#3357)
This commit is contained in:
1 parent
2f6201bcbd
commit
12d503d4c5
37 files changed
+3036
-89
No files matched your search
@@ -1,31 +1,54 @@
|
||||
.outliers {
|
||||
.current {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
border: 0.0625rem solid var(--background-header-secondary);
|
||||
border-radius: var(--radius-sm);
|
||||
background-color: var(--background-secondary);
|
||||
background-color: var(--background-tertiary);
|
||||
}
|
||||
|
||||
.outliersTitle {
|
||||
.sectionTitle {
|
||||
margin: 0;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.currentText {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.35;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.overrides {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.overridesTitle {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.outlierList {
|
||||
.guildList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
max-height: 9rem;
|
||||
max-height: 8rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.outlierItem {
|
||||
.guildItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
@@ -34,13 +57,54 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.outlierName {
|
||||
.guildName {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.guildState {
|
||||
flex-shrink: 0;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.optionName {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.currentTag {
|
||||
padding: 0.0625rem 0.375rem;
|
||||
border-radius: var(--radius-full);
|
||||
background-color: var(--background-modifier-accent);
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.apply {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
border: 0.0625rem solid var(--background-header-secondary);
|
||||
border-radius: var(--radius-sm);
|
||||
background-color: var(--background-tertiary);
|
||||
}
|
||||
|
||||
.applyLabel {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.summary {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import * as Modal from '@app/features/app/components/dialogs/Modal';
|
||||
import {GuildIcon} from '@app/features/guild/components/popouts/GuildIcon';
|
||||
import type {Guild} from '@app/features/guild/models/Guild';
|
||||
import Guilds from '@app/features/guild/state/Guilds';
|
||||
import {CANCEL_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
|
||||
import {Button} from '@app/features/ui/button/Button';
|
||||
@@ -23,11 +24,40 @@ const PRIVACY_SETUP_TITLE_DESCRIPTOR = msg({
|
||||
message: 'Who can message you?',
|
||||
comment: 'Title of the privacy setup modal that asks who can send the user direct messages.',
|
||||
});
|
||||
const PRIVACY_SETUP_DESCRIPTION_DESCRIPTOR = msg({
|
||||
message:
|
||||
'Friends can always send you direct messages. Choose whether people who share a community with you can message you without sending a friend request first.',
|
||||
const PRIVACY_SETUP_INTRO_DESCRIPTOR = msg({
|
||||
message: 'Take a moment to check who can message you. Nothing changes unless you choose to.',
|
||||
comment: 'Explanation at the top of the privacy setup modal.',
|
||||
});
|
||||
const PRIVACY_SETUP_CURRENT_HEADING_DESCRIPTOR = msg({
|
||||
message: 'Right now',
|
||||
comment: 'Heading of the box in the privacy setup modal that describes the current direct message setting.',
|
||||
});
|
||||
const PRIVACY_SETUP_CURRENT_OPEN_DESCRIPTOR = msg({
|
||||
message: 'People in your communities can message you without being friends.',
|
||||
comment: 'Describes the current setting in the privacy setup modal when community members can send direct messages.',
|
||||
});
|
||||
const PRIVACY_SETUP_CURRENT_FRIENDS_DESCRIPTOR = msg({
|
||||
message: 'People in your communities need to be your friend before they can message you.',
|
||||
comment: 'Describes the current setting in the privacy setup modal when only friends can send direct messages.',
|
||||
});
|
||||
const PRIVACY_SETUP_FRIENDS_ALWAYS_DESCRIPTOR = msg({
|
||||
message: 'Friends can always message you.',
|
||||
comment: 'Reassurance in the privacy setup modal that friends can always send direct messages.',
|
||||
});
|
||||
const PRIVACY_SETUP_OWN_SETTING_DESCRIPTOR = msg({
|
||||
message: '{count, plural, one {# community has its own setting} other {# communities have their own setting}}',
|
||||
comment:
|
||||
'Heading above the list of communities whose direct message setting differs from the default, in the privacy setup modal.',
|
||||
});
|
||||
const PRIVACY_SETUP_STATE_OPEN_DESCRIPTOR = msg({
|
||||
message: 'Can message you',
|
||||
comment: 'Shown next to a community in the privacy setup modal. Its members can send the user direct messages.',
|
||||
});
|
||||
const PRIVACY_SETUP_STATE_FRIENDS_DESCRIPTOR = msg({
|
||||
message: 'Friends only',
|
||||
comment:
|
||||
'Shown next to a community in the privacy setup modal. Only friends from it can send the user direct messages.',
|
||||
});
|
||||
const PRIVACY_SETUP_OPEN_DESCRIPTOR = msg({
|
||||
message: 'Anyone in my communities',
|
||||
comment: 'Privacy setup option. People who share a community with the user can send them direct messages.',
|
||||
@@ -44,28 +74,52 @@ const PRIVACY_SETUP_FRIENDS_DESC_DESCRIPTOR = msg({
|
||||
message: 'People need to be your friend before they can message you. They can still send you a friend request.',
|
||||
comment: 'Description under the "Friends only" privacy setup option.',
|
||||
});
|
||||
const PRIVACY_SETUP_FOOTNOTE_DESCRIPTOR = msg({
|
||||
message: 'You can change this anytime in your privacy settings, including for each community.',
|
||||
comment: 'Small note at the bottom of the privacy setup modal.',
|
||||
});
|
||||
const PRIVACY_SETUP_SAVE_DESCRIPTOR = msg({
|
||||
message: 'Save',
|
||||
comment: 'Primary button in the privacy setup modal. Saves the chosen direct message setting.',
|
||||
});
|
||||
const PRIVACY_SETUP_OUTLIERS_DESCRIPTOR = msg({
|
||||
message: '{count, plural, one {# community uses a different setting} other {# communities use a different setting}}',
|
||||
comment:
|
||||
'Heading above the list of communities whose direct message setting differs from the choice in the privacy setup modal.',
|
||||
});
|
||||
const PRIVACY_SETUP_APPLY_TO_OUTLIERS_DESCRIPTOR = msg({
|
||||
message: 'Apply to these communities too',
|
||||
comment:
|
||||
'Checkbox in the privacy setup modal. When checked, the chosen setting also replaces the setting of the listed communities.',
|
||||
const PRIVACY_SETUP_CURRENT_TAG_DESCRIPTOR = msg({
|
||||
message: 'Current',
|
||||
comment: 'Small tag next to the privacy setup option that matches the current setting.',
|
||||
});
|
||||
const PRIVACY_SETUP_OPTIONS_LABEL_DESCRIPTOR = msg({
|
||||
message: 'Who can message you',
|
||||
comment: 'Accessible label for the group of options in the privacy setup modal.',
|
||||
});
|
||||
const PRIVACY_SETUP_APPLY_DESCRIPTOR = msg({
|
||||
message: 'Also change the communities below to match',
|
||||
comment:
|
||||
'Unchecked checkbox in the privacy setup modal. When checked, the new setting also replaces the setting of the listed communities.',
|
||||
});
|
||||
const PRIVACY_SETUP_SUMMARY_NONE_DESCRIPTOR = msg({
|
||||
message: 'Nothing will change. This just confirms your settings.',
|
||||
comment: 'Summary in the privacy setup modal when the user keeps the current setting.',
|
||||
});
|
||||
const PRIVACY_SETUP_SUMMARY_FUTURE_DESCRIPTOR = msg({
|
||||
message: 'Communities you join from now on will use this. Your current communities keep their settings.',
|
||||
comment:
|
||||
'Summary in the privacy setup modal when the user changes the setting without changing their current communities.',
|
||||
});
|
||||
const PRIVACY_SETUP_SUMMARY_FUTURE_ONLY_DESCRIPTOR = msg({
|
||||
message: 'Communities you join from now on will use this.',
|
||||
comment: 'Summary in the privacy setup modal when the user changes the setting and no current community is affected.',
|
||||
});
|
||||
const PRIVACY_SETUP_SUMMARY_APPLY_DESCRIPTOR = msg({
|
||||
message: 'Communities you join from now on will use this, and the communities below change to match.',
|
||||
comment: 'Summary in the privacy setup modal when the new setting also applies to the listed current communities.',
|
||||
});
|
||||
const PRIVACY_SETUP_FOOTNOTE_DESCRIPTOR = msg({
|
||||
message: 'You can change this anytime in your privacy settings, including for each community.',
|
||||
comment: 'Small note at the bottom of the privacy setup modal.',
|
||||
});
|
||||
const PRIVACY_SETUP_KEEP_DESCRIPTOR = msg({
|
||||
message: 'Keep my current settings',
|
||||
comment: 'Primary button in the privacy setup modal when nothing is changed. Confirms the current settings.',
|
||||
});
|
||||
const PRIVACY_SETUP_SAVE_CHANGES_DESCRIPTOR = msg({
|
||||
message: 'Save changes',
|
||||
comment: 'Primary button in the privacy setup modal after the user picks a different setting.',
|
||||
});
|
||||
|
||||
function isRestricted(restricted: ReadonlySet<string>, guild: Guild): boolean {
|
||||
return restricted.has(guild.id);
|
||||
}
|
||||
|
||||
export const PrivacySetupModal = observer(() => {
|
||||
const {i18n} = useLingui();
|
||||
@@ -73,50 +127,74 @@ export const PrivacySetupModal = observer(() => {
|
||||
const currentRestrictedGuilds = UserSettings.restrictedGuilds;
|
||||
const currentChoice: CommunityDirectMessages = currentDefaultRestricted ? 'friends' : 'open';
|
||||
const [choice, setChoice] = useState<CommunityDirectMessages>(currentChoice);
|
||||
const [applyToOutliers, setApplyToOutliers] = useState(true);
|
||||
const [applyToCurrent, setApplyToCurrent] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const primaryRef = useRef<HTMLButtonElement | null>(null);
|
||||
const outliersTitleId = useId();
|
||||
const currentHeadingId = useId();
|
||||
const overridesHeadingId = useId();
|
||||
const guilds = Guilds.getGuilds();
|
||||
const restrictedSet = useMemo(() => new Set(currentRestrictedGuilds), [currentRestrictedGuilds]);
|
||||
const outliers = guilds.filter((guild) => restrictedSet.has(guild.id) === (choice === 'open'));
|
||||
const overrides = guilds.filter((guild) => isRestricted(restrictedSet, guild) !== currentDefaultRestricted);
|
||||
const changed = choice !== currentChoice;
|
||||
const nextDefaultRestricted = choice === 'friends';
|
||||
const affected = changed
|
||||
? guilds.filter((guild) => isRestricted(restrictedSet, guild) !== nextDefaultRestricted)
|
||||
: [];
|
||||
const applying = changed && applyToCurrent && affected.length > 0;
|
||||
const currentTag = (
|
||||
<span className={styles.currentTag} data-flx="user.privacy-setup-modal.current-tag">
|
||||
{i18n._(PRIVACY_SETUP_CURRENT_TAG_DESCRIPTOR)}
|
||||
</span>
|
||||
);
|
||||
const options = useMemo<ReadonlyArray<RadioOption<CommunityDirectMessages>>>(
|
||||
() => [
|
||||
{
|
||||
value: 'open',
|
||||
name: i18n._(PRIVACY_SETUP_OPEN_DESCRIPTOR),
|
||||
name: (
|
||||
<span className={styles.optionName} data-flx="user.privacy-setup-modal.option-name--open">
|
||||
{i18n._(PRIVACY_SETUP_OPEN_DESCRIPTOR)}
|
||||
{currentChoice === 'open' && currentTag}
|
||||
</span>
|
||||
),
|
||||
desc: i18n._(PRIVACY_SETUP_OPEN_DESC_DESCRIPTOR),
|
||||
},
|
||||
{
|
||||
value: 'friends',
|
||||
name: i18n._(PRIVACY_SETUP_FRIENDS_DESCRIPTOR),
|
||||
name: (
|
||||
<span className={styles.optionName} data-flx="user.privacy-setup-modal.option-name--friends">
|
||||
{i18n._(PRIVACY_SETUP_FRIENDS_DESCRIPTOR)}
|
||||
{currentChoice === 'friends' && currentTag}
|
||||
</span>
|
||||
),
|
||||
desc: i18n._(PRIVACY_SETUP_FRIENDS_DESC_DESCRIPTOR),
|
||||
},
|
||||
],
|
||||
[i18n],
|
||||
[i18n, currentChoice, currentTag],
|
||||
);
|
||||
const summary = !changed
|
||||
? i18n._(PRIVACY_SETUP_SUMMARY_NONE_DESCRIPTOR)
|
||||
: applying
|
||||
? i18n._(PRIVACY_SETUP_SUMMARY_APPLY_DESCRIPTOR)
|
||||
: affected.length > 0
|
||||
? i18n._(PRIVACY_SETUP_SUMMARY_FUTURE_DESCRIPTOR)
|
||||
: i18n._(PRIVACY_SETUP_SUMMARY_FUTURE_ONLY_DESCRIPTOR);
|
||||
const handleClose = useCallback(() => {
|
||||
ModalCommands.pop();
|
||||
}, []);
|
||||
const handleSave = useCallback(async () => {
|
||||
const nextDefaultRestricted = choice === 'friends';
|
||||
const nextRestrictedGuilds =
|
||||
applyToOutliers && outliers.length > 0
|
||||
? nextDefaultRestricted
|
||||
? guilds.map((guild) => guild.id)
|
||||
: []
|
||||
: currentRestrictedGuilds;
|
||||
const restrictedChanged =
|
||||
nextRestrictedGuilds.length !== currentRestrictedGuilds.length ||
|
||||
nextRestrictedGuilds.some((id) => !restrictedSet.has(id));
|
||||
setSubmitting(true);
|
||||
try {
|
||||
if (nextDefaultRestricted === currentDefaultRestricted && !restrictedChanged) {
|
||||
if (!changed) {
|
||||
await UserSettingsCommands.update({privacySetupVersion: PRIVACY_SETUP_VERSION});
|
||||
} else if (applying) {
|
||||
await UserSettingsCommands.update({
|
||||
defaultGuildsRestricted: nextDefaultRestricted,
|
||||
restrictedGuilds: nextDefaultRestricted ? guilds.map((guild) => guild.id) : [],
|
||||
privacySetupVersion: PRIVACY_SETUP_VERSION,
|
||||
});
|
||||
} else {
|
||||
await UserSettingsCommands.update({
|
||||
defaultGuildsRestricted: nextDefaultRestricted,
|
||||
restrictedGuilds: [...nextRestrictedGuilds],
|
||||
privacySetupVersion: PRIVACY_SETUP_VERSION,
|
||||
});
|
||||
}
|
||||
@@ -124,15 +202,26 @@ export const PrivacySetupModal = observer(() => {
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
}, [
|
||||
applyToOutliers,
|
||||
choice,
|
||||
currentDefaultRestricted,
|
||||
currentRestrictedGuilds,
|
||||
guilds,
|
||||
outliers.length,
|
||||
restrictedSet,
|
||||
]);
|
||||
}, [applying, changed, guilds, nextDefaultRestricted]);
|
||||
const renderGuildRow = (guild: Guild) => (
|
||||
<li key={guild.id} className={styles.guildItem} data-flx="user.privacy-setup-modal.guild-item">
|
||||
<GuildIcon
|
||||
id={guild.id}
|
||||
name={guild.name}
|
||||
icon={guild.icon}
|
||||
sizePx={20}
|
||||
data-flx="user.privacy-setup-modal.guild-icon"
|
||||
/>
|
||||
<span className={styles.guildName} data-flx="user.privacy-setup-modal.guild-name">
|
||||
{guild.name}
|
||||
</span>
|
||||
<span className={styles.guildState} data-flx="user.privacy-setup-modal.guild-state">
|
||||
{isRestricted(restrictedSet, guild)
|
||||
? i18n._(PRIVACY_SETUP_STATE_FRIENDS_DESCRIPTOR)
|
||||
: i18n._(PRIVACY_SETUP_STATE_OPEN_DESCRIPTOR)}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
return (
|
||||
<Modal.Root
|
||||
size="small"
|
||||
@@ -149,8 +238,45 @@ export const PrivacySetupModal = observer(() => {
|
||||
<Modal.Content data-flx="user.privacy-setup-modal.modal-content">
|
||||
<Modal.ContentLayout data-flx="user.privacy-setup-modal.modal-content-layout">
|
||||
<Modal.Description data-flx="user.privacy-setup-modal.modal-description">
|
||||
{i18n._(PRIVACY_SETUP_DESCRIPTION_DESCRIPTOR)}
|
||||
{i18n._(PRIVACY_SETUP_INTRO_DESCRIPTOR)}
|
||||
</Modal.Description>
|
||||
<section
|
||||
className={styles.current}
|
||||
aria-labelledby={currentHeadingId}
|
||||
data-flx="user.privacy-setup-modal.current"
|
||||
>
|
||||
<h3 id={currentHeadingId} className={styles.sectionTitle} data-flx="user.privacy-setup-modal.current-title">
|
||||
{i18n._(PRIVACY_SETUP_CURRENT_HEADING_DESCRIPTOR)}
|
||||
</h3>
|
||||
<p className={styles.currentText} data-flx="user.privacy-setup-modal.current-text">
|
||||
<span data-flx="user.privacy-setup-modal.current-state">
|
||||
{currentDefaultRestricted
|
||||
? i18n._(PRIVACY_SETUP_CURRENT_FRIENDS_DESCRIPTOR)
|
||||
: i18n._(PRIVACY_SETUP_CURRENT_OPEN_DESCRIPTOR)}
|
||||
</span>{' '}
|
||||
<span data-flx="user.privacy-setup-modal.friends-always">
|
||||
{i18n._(PRIVACY_SETUP_FRIENDS_ALWAYS_DESCRIPTOR)}
|
||||
</span>
|
||||
</p>
|
||||
{overrides.length > 0 && (
|
||||
<div className={styles.overrides} data-flx="user.privacy-setup-modal.overrides">
|
||||
<h4
|
||||
id={overridesHeadingId}
|
||||
className={styles.overridesTitle}
|
||||
data-flx="user.privacy-setup-modal.overrides-title"
|
||||
>
|
||||
{i18n._(PRIVACY_SETUP_OWN_SETTING_DESCRIPTOR, {count: overrides.length})}
|
||||
</h4>
|
||||
<ul
|
||||
className={styles.guildList}
|
||||
aria-labelledby={overridesHeadingId}
|
||||
data-flx="user.privacy-setup-modal.overrides-list"
|
||||
>
|
||||
{overrides.map(renderGuildRow)}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
<RadioGroup
|
||||
options={options}
|
||||
value={choice}
|
||||
@@ -158,47 +284,26 @@ export const PrivacySetupModal = observer(() => {
|
||||
aria-label={i18n._(PRIVACY_SETUP_OPTIONS_LABEL_DESCRIPTOR)}
|
||||
data-flx="user.privacy-setup-modal.radio-group.set-choice"
|
||||
/>
|
||||
{outliers.length > 0 && (
|
||||
<section
|
||||
className={styles.outliers}
|
||||
aria-labelledby={outliersTitleId}
|
||||
data-flx="user.privacy-setup-modal.outliers"
|
||||
>
|
||||
<h3
|
||||
id={outliersTitleId}
|
||||
className={styles.outliersTitle}
|
||||
data-flx="user.privacy-setup-modal.outliers-title"
|
||||
>
|
||||
{i18n._(PRIVACY_SETUP_OUTLIERS_DESCRIPTOR, {count: outliers.length})}
|
||||
</h3>
|
||||
<ul className={styles.outlierList} data-flx="user.privacy-setup-modal.outlier-list">
|
||||
{outliers.map((guild) => (
|
||||
<li key={guild.id} className={styles.outlierItem} data-flx="user.privacy-setup-modal.outlier-item">
|
||||
<GuildIcon
|
||||
id={guild.id}
|
||||
name={guild.name}
|
||||
icon={guild.icon}
|
||||
sizePx={20}
|
||||
data-flx="user.privacy-setup-modal.outlier-icon"
|
||||
/>
|
||||
<span className={styles.outlierName} data-flx="user.privacy-setup-modal.outlier-name">
|
||||
{guild.name}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{changed && affected.length > 0 && (
|
||||
<section className={styles.apply} data-flx="user.privacy-setup-modal.apply">
|
||||
<Checkbox
|
||||
checked={applyToOutliers}
|
||||
onChange={setApplyToOutliers}
|
||||
checked={applyToCurrent}
|
||||
onChange={setApplyToCurrent}
|
||||
size="small"
|
||||
data-flx="user.privacy-setup-modal.checkbox.apply-to-outliers"
|
||||
data-flx="user.privacy-setup-modal.checkbox.apply-to-current"
|
||||
>
|
||||
<span className={styles.applyLabel} data-flx="user.privacy-setup-modal.apply-label">
|
||||
{i18n._(PRIVACY_SETUP_APPLY_TO_OUTLIERS_DESCRIPTOR)}
|
||||
{i18n._(PRIVACY_SETUP_APPLY_DESCRIPTOR)}
|
||||
</span>
|
||||
</Checkbox>
|
||||
<ul className={styles.guildList} data-flx="user.privacy-setup-modal.affected-list">
|
||||
{affected.map(renderGuildRow)}
|
||||
</ul>
|
||||
</section>
|
||||
)}
|
||||
<p className={styles.summary} aria-live="polite" data-flx="user.privacy-setup-modal.summary">
|
||||
{summary}
|
||||
</p>
|
||||
<Modal.Description data-flx="user.privacy-setup-modal.modal-description--footnote">
|
||||
{i18n._(PRIVACY_SETUP_FOOTNOTE_DESCRIPTOR)}
|
||||
</Modal.Description>
|
||||
@@ -215,7 +320,7 @@ export const PrivacySetupModal = observer(() => {
|
||||
ref={primaryRef}
|
||||
data-flx="user.privacy-setup-modal.button.save"
|
||||
>
|
||||
{i18n._(PRIVACY_SETUP_SAVE_DESCRIPTOR)}
|
||||
{changed ? i18n._(PRIVACY_SETUP_SAVE_CHANGES_DESCRIPTOR) : i18n._(PRIVACY_SETUP_KEEP_DESCRIPTOR)}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal.Root>
|
||||
|
||||
Reference in new issue
Block a user