Back

88.46% Statements 23/26
66.66% Branches 12/18
70% Functions 7/10
88.46% Lines 23/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161                                        1x 5x 5x 5x 5x 5x 5x 5x 5x 5x           5x 5x   3x                               5x   5x                                                                   1x                                                 1x                           1x               1x       5x   5x                   1x 5x            
import { ToggleControl } from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
import { Card, Stack } from '@wordpress/ui';
import DashboardLayout from '../../src/dashboard/components/dashboard-layout';
import FreeTierNotice, {
	FREE_TIER_AT_LIMIT_MESSAGE,
} from '../../src/dashboard/components/free-tier-notice';
import QueryClientWrapper from '../../src/dashboard/components/query-client-wrapper';
import { useFreeTier } from '../../src/dashboard/hooks/use-free-tier';
import {
	isPrivateForSiteServerControlled,
	useSettings,
	useUpdateSettings,
} from '../../src/dashboard/hooks/use-settings';
import './style.scss';
import type { SettingsPatch } from '../../src/dashboard/hooks/use-settings';
 
const SettingsForm = () => {
	const settings = useSettings();
	const update = useUpdateSettings();
	const { createErrorNotice } = useDispatch( noticesStore );
	const privateForSite = settings.data?.videoPressVideosPrivateForSite ?? false;
	const autoSubtitlesDisabled = settings.data?.videoPressAutoSubtitlesDisabled ?? false;
	const playerPreloadDisabled = settings.data?.videoPressPlayerPreloadDisabled ?? false;
	const inlinePlayerEnabled = settings.data?.videoPressInlinePlayerEnabled ?? false;
	const shareMenuDisabled = settings.data?.videoPressShareMenuDisabled ?? false;
	const disabled = settings.isLoading || update.isPending;
 
	// The mutation rolls the optimistic value back on failure; without a notice
	// the toggle would just silently snap back and read as if the save took.
	// Depend on `mutate` (stable across renders), not the per-render mutation
	// result object, so the memoization actually holds.
	const { mutate } = update;
	const save = useCallback(
		( patch: SettingsPatch ) =>
			mutate( patch, {
				onError: () =>
					createErrorNotice(
						__( 'Your setting couldn’t be saved. Please try again.', 'jetpack-videopress-pkg' ),
						{ type: 'snackbar' }
					),
			} ),
		[ mutate, createErrorNotice ]
	);
 
	// The "private videos for site" value is resolved server-side on WordPress.com
	// Simple (it follows the whole-site Privacy setting) and on private Atomic/WoA
	// sites (a private site forces every video private). In both cases the server
	// ignores writes to this setting, so reflect the effective value read-only
	// instead of offering a toggle whose write is a no-op. Deciding from the
	// settings response — not the ENV — keeps both hosts in sync with the server.
	const privateForSiteServerControlled = isPrivateForSiteServerControlled( settings.data );
 
	return (
		<Card.Root>
			<Card.Header>
				<Card.Title>{ __( 'Video settings', 'jetpack-videopress-pkg' ) }</Card.Title>
			</Card.Header>
			<Card.Content>
				<Stack direction="column" gap="lg">
					<ToggleControl
						__nextHasNoMarginBottom
						label={ __( 'Only logged-in users can play your videos', 'jetpack-videopress-pkg' ) }
						help={
							privateForSiteServerControlled
								? __(
										'This follows your site’s Privacy setting. To change who can view your videos, update your site’s visibility in Settings → General.',
										'jetpack-videopress-pkg'
									)
								: __(
										"Private videos won't play for signed-out visitors.",
										'jetpack-videopress-pkg'
									)
						}
						checked={ privateForSite }
						disabled={ disabled || privateForSiteServerControlled }
						onChange={ next => save( { videoPressVideosPrivateForSite: next } ) }
					/>
					<ToggleControl
						__nextHasNoMarginBottom
						label={ __( 'Allow sharing', 'jetpack-videopress-pkg' ) }
						help={ __(
							'When enabled, each video’s own Share setting decides whether viewers can share the video link. Turn it off to hide the share menu on every video and stop it from being turned on for individual videos.',
							'jetpack-videopress-pkg'
						) }
						checked={ ! shareMenuDisabled }
						disabled={ disabled }
						onChange={ next => save( { videoPressShareMenuDisabled: ! next } ) }
					/>
					<ToggleControl
						__nextHasNoMarginBottom
						label={ __(
							'Automatically generate subtitles for new videos',
							'jetpack-videopress-pkg'
						) }
						help={ __(
							'When enabled, subtitles are generated automatically for videos uploaded to this site. Existing subtitles are not affected.',
							'jetpack-videopress-pkg'
						) }
						checked={ ! autoSubtitlesDisabled }
						disabled={ disabled }
						onChange={ next => save( { videoPressAutoSubtitlesDisabled: ! next } ) }
					/>
					<ToggleControl
						__nextHasNoMarginBottom
						label={ __( 'Preload video data when pages load', 'jetpack-videopress-pkg' ) }
						help={ __(
							'When enabled, embedded videos fetch their metadata and seek-bar preview thumbnails as the page loads. Turn it off to reduce page weight on pages with many videos; each video then loads its data when playback starts.',
							'jetpack-videopress-pkg'
						) }
						checked={ ! playerPreloadDisabled }
						disabled={ disabled }
						onChange={ next => save( { videoPressPlayerPreloadDisabled: ! next } ) }
					/>
					<ToggleControl
						__nextHasNoMarginBottom
						label={ __(
							'Load the player once per page instead of once per video',
							'jetpack-videopress-pkg'
						) }
						help={ __(
							'Renders every video directly in your page from one shared player script instead of a separate embedded frame per video, so page-speed tools stop counting the player once for each video. Playlists and videos with hover previews keep using frames.',
							'jetpack-videopress-pkg'
						) }
						checked={ inlinePlayerEnabled }
						disabled={ disabled }
						onChange={ next => save( { videoPressInlinePlayerEnabled: next } ) }
					/>
				</Stack>
			</Card.Content>
		</Card.Root>
	);
};
 
const StageInner = () => {
	// Settings has no upload button of its own, but a free user who has used
	// their upload otherwise reaches this tab with no upgrade path in sight —
	// surface the same at-limit notice the Library shows (VIDP-311).
	const { isAtLimit } = useFreeTier();
 
	return (
		<DashboardLayout activeTab="settings">
			<div className="jp-videopress-settings">
				{ isAtLimit && <FreeTierNotice message={ FREE_TIER_AT_LIMIT_MESSAGE } /> }
				<SettingsForm />
			</div>
		</DashboardLayout>
	);
};
 
const Stage = () => (
	<QueryClientWrapper>
		<StageInner />
	</QueryClientWrapper>
);
 
export { Stage as stage };