fix(app): keep slowmode reset visible and localize markers (#1586)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-13 22:44:54 +02:00
1 parent 27f459e6bd
commit ab46d16d12
4 files changed
+21 -25

No files matched your search

@@ -3,6 +3,7 @@
import styles from '@app/features/channel/components/modals/channel_tabs/ChannelOverviewTab.module.css'; import styles from '@app/features/channel/components/modals/channel_tabs/ChannelOverviewTab.module.css';
import {SettingsControlRow} from '@app/features/channel/components/modals/channel_tabs/channel_overview_tab/SettingsControlRow'; import {SettingsControlRow} from '@app/features/channel/components/modals/channel_tabs/channel_overview_tab/SettingsControlRow';
import type {FormInputs} from '@app/features/channel/components/modals/channel_tabs/channel_overview_tab/shared'; import type {FormInputs} from '@app/features/channel/components/modals/channel_tabs/channel_overview_tab/shared';
import {getCachedNumberFormat} from '@app/features/i18n/utils/IntlCache';
import {formatPermissionLabel} from '@app/features/permissions/utils/PermissionUtils'; import {formatPermissionLabel} from '@app/features/permissions/utils/PermissionUtils';
import {RESET_SLIDER_TO_DEFAULT_VALUE_DESCRIPTOR, Slider} from '@app/features/ui/components/Slider'; import {RESET_SLIDER_TO_DEFAULT_VALUE_DESCRIPTOR, Slider} from '@app/features/ui/components/Slider';
import {Permissions} from '@fluxer/constants/src/ChannelConstants'; import {Permissions} from '@fluxer/constants/src/ChannelConstants';
@@ -29,35 +30,27 @@ const SLOWMODE_DESCRIPTION_DESCRIPTOR = msg({
comment: comment:
'Description under the slowmode slider in channel settings. bypassSlowmodePermissionLabel is the localized Bypass Slowmode permission name.', 'Description under the slowmode slider in channel settings. bypassSlowmodePermissionLabel is the localized Bypass Slowmode permission name.',
}); });
const SLOWMODE_SECONDS_DESCRIPTOR = msg({
message: '{count, plural, one {# second} other {# seconds}}',
comment: 'Slowmode duration expressed in seconds, shown on the channel settings slowmode slider.',
});
const SLOWMODE_MINUTES_DESCRIPTOR = msg({
message: '{count, plural, one {# minute} other {# minutes}}',
comment: 'Slowmode duration expressed in minutes, shown on the channel settings slowmode slider.',
});
const SLOWMODE_HOURS_DESCRIPTOR = msg({
message: '{count, plural, one {# hour} other {# hours}}',
comment: 'Slowmode duration expressed in hours, shown on the channel settings slowmode slider.',
});
const SLOWMODE_STOP_SECONDS: ReadonlyArray<number> = [ const SLOWMODE_STOP_SECONDS: ReadonlyArray<number> = [
0, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 7200, 21600, 0, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 7200, 21600,
]; ];
const SLOWMODE_MARKER_SECONDS: ReadonlyArray<number> = [0, 60, 3600, 21600];
type SlowmodeDurationUnit = 'second' | 'minute' | 'hour';
function formatSlowmodeDuration(i18n: I18n, seconds: number): string { function formatSlowmodeDuration(i18n: I18n, seconds: number): string {
if (seconds === 0) { const roundedSeconds = Math.max(0, Math.round(seconds));
if (roundedSeconds === 0) {
return i18n._(OFF_DESCRIPTOR); return i18n._(OFF_DESCRIPTOR);
} }
if (seconds % SECONDS_PER_HOUR === 0) { let unit: SlowmodeDurationUnit = 'second';
return i18n._(SLOWMODE_HOURS_DESCRIPTOR, {count: seconds / SECONDS_PER_HOUR}); let value = roundedSeconds;
if (roundedSeconds % SECONDS_PER_HOUR === 0) {
unit = 'hour';
value = roundedSeconds / SECONDS_PER_HOUR;
} else if (roundedSeconds % SECONDS_PER_MINUTE === 0) {
unit = 'minute';
value = roundedSeconds / SECONDS_PER_MINUTE;
} }
if (seconds % SECONDS_PER_MINUTE === 0) { return getCachedNumberFormat(i18n.locale, {style: 'unit', unit, unitDisplay: 'long'}).format(value);
return i18n._(SLOWMODE_MINUTES_DESCRIPTOR, {count: seconds / SECONDS_PER_MINUTE});
}
return i18n._(SLOWMODE_SECONDS_DESCRIPTOR, {count: seconds});
} }
interface SlowmodeControlProps { interface SlowmodeControlProps {
@@ -99,9 +92,7 @@ export const SlowmodeControl: React.FC<SlowmodeControlProps> = ({form}) => {
markers={SLOWMODE_STOP_SECONDS} markers={SLOWMODE_STOP_SECONDS}
ariaLabel={slowmodeLabel} ariaLabel={slowmodeLabel}
ariaValueText={formatSlowmodeDuration(i18n, currentSeconds)} ariaValueText={formatSlowmodeDuration(i18n, currentSeconds)}
onMarkerRender={(seconds) => onMarkerRender={(seconds) => formatSlowmodeDuration(i18n, seconds)}
SLOWMODE_MARKER_SECONDS.includes(seconds) ? formatSlowmodeDuration(i18n, seconds) : null
}
onValueRender={(seconds) => formatSlowmodeDuration(i18n, Math.round(seconds))} onValueRender={(seconds) => formatSlowmodeDuration(i18n, Math.round(seconds))}
onValueChange={(seconds) => field.onChange(Math.round(seconds))} onValueChange={(seconds) => field.onChange(Math.round(seconds))}
showResetButton={true} showResetButton={true}
@@ -51,6 +51,7 @@
.resetButton:disabled { .resetButton:disabled {
cursor: not-allowed; cursor: not-allowed;
opacity: 0.5;
} }
.resetRow { .resetRow {
@@ -67,6 +67,7 @@ export function SliderResetControls({
canReset={canReset} canReset={canReset}
onReset={onReset} onReset={onReset}
ariaLabel={inlineLabel} ariaLabel={inlineLabel}
alwaysVisible={true}
dataFlx="ui.slider.slider-reset-controls.reset-button" dataFlx="ui.slider.slider-reset-controls.reset-button"
data-flx="ui.slider.slider-reset-controls.slider-reset-icon-button" data-flx="ui.slider.slider-reset-controls.slider-reset-icon-button"
/> />
@@ -13,6 +13,7 @@ interface SliderResetIconButtonProps {
className?: string; className?: string;
iconSize?: number; iconSize?: number;
dataFlx?: string; dataFlx?: string;
alwaysVisible?: boolean;
} }
export function canResetSliderValue(value: number, factoryDefaultValue: number, disabled = false): boolean { export function canResetSliderValue(value: number, factoryDefaultValue: number, disabled = false): boolean {
@@ -26,13 +27,15 @@ export function SliderResetIconButton({
className, className,
iconSize = 14, iconSize = 14,
dataFlx = 'ui.slider.slider-reset-icon-button.button', dataFlx = 'ui.slider.slider-reset-icon-button.button',
alwaysVisible = false,
}: SliderResetIconButtonProps): React.ReactElement | null { }: SliderResetIconButtonProps): React.ReactElement | null {
if (!canReset) return null; if (!canReset && !alwaysVisible) return null;
return ( return (
<button <button
type="button" type="button"
className={clsx(styles.resetButton, className)} className={clsx(styles.resetButton, className)}
onClick={onReset} onClick={onReset}
disabled={!canReset}
aria-label={ariaLabel} aria-label={ariaLabel}
data-flx={dataFlx} data-flx={dataFlx}
> >