PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.6.1
Jetpack – WP Security, Backup, Speed, & Growth v14.6.1
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / subscriptions / newsletter-widget / src / helpers.ts
jetpack / modules / subscriptions / newsletter-widget / src Last commit date
components 1 year ago constants.ts 1 year ago helpers.ts 1 year ago index.tsx 1 year ago style.scss 1 year ago types.ts 1 year ago
helpers.ts
190 lines
1 import analytics from '@automattic/jetpack-analytics';
2 import { getRedirectUrl } from '@automattic/jetpack-components';
3 import { dateI18n } from '@wordpress/date';
4 import { SubscriberTotalsByDate, ChartSubscriptionDataPoint } from './types';
5
6 /**
7 * Creates an event handler function for tracking user interactions
8 *
9 * @param tracks - The tracks analytics object
10 * @param eventName - The "action" part of the event name. Will be appended to "jetpack_newsletter_widget_"
11 * @param eventProperties - Additional properties to include in the event
12 * @returns A callback function that records the event when triggered. To primarily be used as an onClick prop.
13 *
14 * @example
15 * const handleClick = createTracksEventHandler( tracks, 'learn_more_click', { locale: 'en' } );
16 */
17 export const createTracksEventHandler = (
18 tracks: typeof analytics.tracks,
19
20 eventAction: string,
21
22 eventProperties: Record< string, unknown > = {}
23 ) => {
24 return () => {
25 tracks.recordEvent( `jetpack_newsletter_widget_${ eventAction }`, eventProperties );
26 };
27 };
28
29 /**
30 * Helper function to build the Jetpack redirect source URL.
31 * @param url - The url to redirect to. Note: it can only be to a whitelisted domain, and query params and anchors must be passed to getRedirectUrl as arguments.
32 * @param isWpcomSite - The the site on the WordPress.com platform. Simple or WoA.
33 * @return The URL that can be passed to the getRedirectUrl function.
34 * @example
35 * const site = 'example.wordpress.com';
36 * const redirectUrl = buildJPRedirectSource( `subscribers/${ site }`, true );
37 *
38 * <a href={ getRedirectUrl( redirectUrl ) }>Subscriber</a>;
39 */
40 export const buildJPRedirectSource = ( url: string, isWpcomSite: boolean = true ) => {
41 const host = isWpcomSite ? 'wordpress.com' : 'cloud.jetpack.com';
42 return `https://${ host }/${ url }`;
43 };
44
45 /**
46 * Generates the URL for subscriber statistics based on site context.
47 *
48 * @param {string} site - The site identifier
49 * @param {boolean} isWpcomSite - Whether the site is on WordPress.com
50 * @param {string} adminUrl - The admin URL for self-hosted sites
51 * @returns {string} The appropriate subscriber stats URL
52 */
53 export const getSubscriberStatsUrl = (
54 site: string,
55 isWpcomSite: boolean,
56 adminUrl: string
57 ): string => {
58 return isWpcomSite
59 ? getRedirectUrl( buildJPRedirectSource( `stats/subscribers/${ site }` ) )
60 : `${ adminUrl }admin.php?page=stats#!/stats/subscribers/${ site }`;
61 };
62
63 /**
64 * Generates the URL for newsletter settings based on site context.
65 *
66 * @param {string} site - The site identifier
67 * @param {boolean} isWpcomSite - Whether the site is on WordPress.com
68 * @param {string} adminUrl - The admin URL for self-hosted sites
69 * @returns {string} The appropriate newsletter settings URL
70 */
71 export const getNewsletterSettingsUrl = (
72 site: string,
73 isWpcomSite: boolean,
74 adminUrl: string
75 ): string => {
76 return isWpcomSite
77 ? getRedirectUrl( buildJPRedirectSource( 'settings/newsletter/' + site ) )
78 : `${ adminUrl }admin.php?page=jetpack#newsletter`;
79 };
80
81 /**
82 * Formats a number into a localized string representation.
83 *
84 * @param {number} num - The number to format.
85 * @returns {string} The formatted number string.
86 */
87 export const formatNumber = ( num: number ): string => {
88 return num.toLocaleString();
89 };
90
91 /**
92 * Formats a date into a string representation.
93 *
94 * @param {Date} date - The date to format.
95 * @param {'short' | 'full'} format - Format type: 'short' for "Jan 5" or 'full' for "Jan 5, 2023".
96 * @returns {string} The formatted date string.
97 */
98 export const formatDate = ( date: Date, format: 'short' | 'full' = 'short' ) => {
99 if ( format === 'short' ) {
100 // 'M j' = Short month name followed by day number without leading zeros (e.g., "Jan 5")
101 return dateI18n( 'M j', date );
102 }
103
104 // 'M j, Y' = Short month name, day number without leading zeros, and year (e.g., "Jan 5, 2023")
105 return dateI18n( 'M j, Y', date );
106 };
107
108 /**
109 * Formats a date specifically for axis tick labels.
110 *
111 * @param {Date} date - The date to format.
112 * @returns {string} The formatted date string in short format.
113 */
114 export const formatAxisTickDate = ( date: Date ) => formatDate( date, 'short' );
115
116 /**
117 * Calculates evenly spaced tick values for the X-axis for time series data.
118 * Assumes the data is sorted by date, earliest is first.
119 *
120 * @param {ChartSubscriptionDataPoint[]} data - The subscription data array.
121 * @returns {Date[]} An array of dates representing tick positions at 0%, 25%, 50%, 75%, and 100% of the time range.
122 */
123 export const getXAxisTickValues = ( data: ChartSubscriptionDataPoint[] ) => {
124 if ( data.length < 2 ) return data.map( d => d.date );
125
126 const firstDate = data[ 0 ].date;
127 const lastDate = data[ data.length - 1 ].date;
128
129 // Calculate total time span in milliseconds
130 const timeSpan = lastDate.getTime() - firstDate.getTime();
131
132 return [
133 firstDate,
134 new Date( firstDate.getTime() + timeSpan * 0.25 ),
135 new Date( firstDate.getTime() + timeSpan * 0.5 ),
136 new Date( firstDate.getTime() + timeSpan * 0.75 ),
137 lastDate,
138 ];
139 };
140
141 /**
142 * Transforms daily subscription counts into a format for the visx chart package.
143 *
144 * @param {Record<string, DailySubscriptionStat>} countsByDay - Object mapping date strings to daily subscription counts.
145 * @returns {ChartSubscriptionDataPoint[]} An array of subscription statistics.
146 */
147 export const transformData = (
148 countsByDay: SubscriberTotalsByDate
149 ): ChartSubscriptionDataPoint[] => {
150 return Object.entries( countsByDay )
151 .map( ( [ dateStr, counts ] ) => {
152 const date = new Date( dateStr );
153
154 if ( isNaN( date.getTime() ) ) {
155 return null;
156 }
157
158 return {
159 date,
160 all: counts?.all ?? 0,
161 paid: counts?.paid ?? 0,
162 };
163 } )
164 .filter( Boolean )
165 .sort( ( a, b ) => a.date.getTime() - b.date.getTime() );
166 };
167
168 /**
169 * Calculates the maximum value of the subscription statistics to determine the left axis tick label margin.
170 * Larger labels will get cut off so we must dynamically increase the margin based on how many digits are in the largest number.
171 *
172 * @param {ChartSubscriptionDataPoint[]} subs - The subscription statistics array. The same data used to render the chart.
173 * @returns {number} The calculated left axis margin.
174 */
175 export const calcLeftAxisMargin = ( subs: ChartSubscriptionDataPoint[] ): number => {
176 const DEFAULT_MARGIN = 30;
177 const CHAR_PX_WIDTH = 8;
178 const PADDING = 10;
179
180 if ( subs.length === 0 ) {
181 return DEFAULT_MARGIN;
182 }
183
184 const maxValue = Math.max( ...subs.map( d => Math.max( d.all || 0, d.paid || 0 ) ) );
185 // Estimate character width (in pixels) and calculate margin
186 // Each digit is roughly 8px, plus add some padding
187 const digitCount = maxValue.toString().length;
188 return Math.max( DEFAULT_MARGIN, digitCount * CHAR_PX_WIDTH + PADDING );
189 };
190