Skip to content

Commit

Permalink
Jetpack Social| Fix scheduled posts UI (#34182)
Browse files Browse the repository at this point in the history
* Enhance RecordBarMeter component with `className`

* Add changelog

* Jetpack Social | Warn the user that their scheduled posts won't get shared if they will get over limits. (#34183)

* Enhance RecordBarMeter component with `className`

* Add changelog

* Add store types

* Update store selectors and actions

* Update consumer hook to use new selectors

* Create useShareLimits hook

* Move `ShareCounter` to publicize-components and name it `ShareLimitsBar`

* Create separate notice components

* Fix the post editor notices

* Update Jetpack Social admin page to use the new meter bar

* Remove unused code left after #34111

* Add changelog

* Fix share count

* Update index.js

* Include active connections count for notices

* Fix i18n build error

* Enhance RecordBarMeter component with `className`

* Add changelog

* Extract constants and improve types

* Extract messages to utility function

* Enhance RecordBarMeter component with `className`

* Add changelog

* Fix up versions

* Revert "Remove unused code left after #34111"

This reverts commit ca45999.

* Use @wordpress/element instead of react

* Fix messaging

* Let TS infer type

* Combine the share limits logic into a single useSelect call

* Fix the limits shown even after a paid plan

* Use boolean instead of number

* Add unit tests for sharing data store (#34206)

* Enhance RecordBarMeter component with `className`

* Add changelog

* Add store types

* Update store selectors and actions

* Update consumer hook to use new selectors

* Create useShareLimits hook

* Move `ShareCounter` to publicize-components and name it `ShareLimitsBar`

* Create separate notice components

* Fix the post editor notices

* Update Jetpack Social admin page to use the new meter bar

* Remove unused code left after #34111

* Add changelog

* Fix share count

* Update index.js

* Include active connections count for notices

* Fix i18n build error

* Enhance RecordBarMeter component with `className`

* Add changelog

* Extract constants and improve types

* Add types/jest

* Add tests for sharesData selectors

* Add tests for useShareLimits hook

* rename to *.test.js

* extract utility

* Create add-tests-for-sharing-data

* Update shares-data.test.js

* Extract messages to utility function

* Use the messages from utility function

* Enhance RecordBarMeter component with `className`

* Add changelog

* Fix up versions

* Revert "Remove unused code left after #34111"

This reverts commit ca45999.

* Use @wordpress/element instead of react

* Fix messaging

* Let TS infer type

* Combine the share limits logic into a single useSelect call

* Fix the limits shown even after a paid plan

* Enhance RecordBarMeter component with `className`

* Add changelog

* Enhance RecordBarMeter component with `className`

* Add changelog

* Jetpack Social | Warn the user that their scheduled posts won't get shared if they will get over limits. (#34183)

* Enhance RecordBarMeter component with `className`

* Add changelog

* Add store types

* Update store selectors and actions

* Update consumer hook to use new selectors

* Create useShareLimits hook

* Move `ShareCounter` to publicize-components and name it `ShareLimitsBar`

* Create separate notice components

* Fix the post editor notices

* Update Jetpack Social admin page to use the new meter bar

* Remove unused code left after #34111

* Add changelog

* Fix share count

* Update index.js

* Include active connections count for notices

* Fix i18n build error

* Enhance RecordBarMeter component with `className`

* Add changelog

* Extract constants and improve types

* Extract messages to utility function

* Enhance RecordBarMeter component with `className`

* Add changelog

* Fix up versions

* Revert "Remove unused code left after #34111"

This reverts commit ca45999.

* Use @wordpress/element instead of react

* Fix messaging

* Let TS infer type

* Combine the share limits logic into a single useSelect call

* Fix the limits shown even after a paid plan

* Update tests

* Fix up versions

* Jetpack Social| Fix scheduled post double count (#34323)

* Update store selectors and resolvers to save initial connections

* Update useShareLimits hook to subtract scheduled post if editing one

* Update ShareCountInfo to subtract the scheduled post from count

* Add changelog

* Fix versions

* changelog!!!!

* Update share limits bar design (#34392)

* Make share data selectors independent of the connections

* Enhance RecordMeterBar component

* Enhance ShareLimitsBar component

* Pass connections data to useShareLimits

* Update editor form components

* Update social admin page header

* Add support for providing text variant

* Add enabledConnections to used/scheduled number

* Fix enabledConnections being NaN

* Update the notice and meter to match the new designs/logic.

* Update the admin page header component

* Fix tests

* Revert changes to RecordBarMeter

* Revert an unnecessary change to RecordMeterBar

* Add changelog

* Create update-share-limits-bar-design

* Fix "msgid argument is not a string literal" build error 100th time in Jetpack

* Fix "Optimization seems to have broken the following translation strings"  - "left"

* Revert "Fix "Optimization seems to have broken the following translation strings"  - "left""

This reverts commit d1d7cda.

* Use "remaining" instead of "left" for the sake of passing the build

* Fix "Optimization seems to have broken the following translation strings - "left"" build error

* Simplify share limits bar logic

* Clean up unused logic

* Restore outOfConnections logic

* Fix unit tests

* Clean up types.ts

* Update notice CTA design

* Use "or" instead of "and"

* Fix the bar title and caption

* Fix "Optimization seems to have broken the following translation strings"

* Fix typo

* Make it a minor change instead of a patch

* Add enabled connections text

* Remove padding for the bar on Social admin page

* Fix up versions

* When under the limits, show an inline upgrade nudge instead of notice

* Revert "When under the limits, show an inline upgrade nudge instead of notice"

This reverts commit b0b1909.

* Fix the order of the title and notice

* Clean things up a bit

---------

Co-authored-by: Gergely Juhasz <[email protected]>
Co-authored-by: Paul Bunkham <[email protected]>

* Fix up versions and lockfile

* Fix the bar width

* Fix TS build

* Restore dependencies messed up while resolving conflicts.

* Restore the record meter bar styles

* Make the bar go red after 90% usage

* Use "connection" instead of "network"

* Hide connections text when publicize is OFF

* Fix up versions

---------

Co-authored-by: Gergely Juhasz <[email protected]>
Co-authored-by: Paul Bunkham <[email protected]>
  • Loading branch information
3 people authored Dec 13, 2023
1 parent 43e3ea7 commit 5858f1e
Show file tree
Hide file tree
Showing 47 changed files with 1,260 additions and 235 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Added `className` prop to RecordMeterBar component
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Changes reverted


Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { __ } from '@wordpress/i18n';
import classNames from 'classnames';
import React, { useMemo } from 'react';
import numberFormat from '../number-format';

Expand Down Expand Up @@ -36,6 +37,26 @@ export type RecordMeterBarProps = {
* The sort style for legend item. If not provided, it defaults to no sorting.
*/
sortByCount?: 'ascending' | 'descending';
/**
* Additional class name to be added to the component
*/
className?: string;
/**
* Table caption
*/
tableCaption?: string;
/**
* Title/label for the legend
*/
legendTitle?: string;
/**
* Recorc type label for screen readers
*/
recordTypeLabel?: string;
/**
* Record count label for screen readers
*/
recordCountLabel?: string;
};

/**
Expand All @@ -49,6 +70,11 @@ const RecordMeterBar: React.FC< RecordMeterBarProps > = ( {
items = [],
showLegendLabelBeforeCount = false,
sortByCount,
className,
tableCaption,
legendTitle,
recordTypeLabel,
recordCountLabel,
} ) => {
const total = useMemo( () => {
// If total count is not given, then compute it from items' count
Expand All @@ -71,7 +97,7 @@ const RecordMeterBar: React.FC< RecordMeterBarProps > = ( {
}, [ items, sortByCount ] );

return (
<div className="record-meter-bar">
<div className={ classNames( 'record-meter-bar', className ) }>
<div className="record-meter-bar__items" aria-hidden="true">
{ itemsToRender.map( ( { count, label, backgroundColor } ) => {
const widthPercent = ( ( count / total ) * 100 ).toPrecision( 2 );
Expand All @@ -81,6 +107,7 @@ const RecordMeterBar: React.FC< RecordMeterBarProps > = ( {
} ) }
</div>
<div className="record-meter-bar__legend" aria-hidden="true">
{ legendTitle && <div className="record-meter-bar__legend--title">{ legendTitle }</div> }
<ul className="record-meter-bar__legend--items">
{ itemsToRender.map( ( { count, label, backgroundColor } ) => {
const formattedCount = numberFormat( count );
Expand Down Expand Up @@ -112,11 +139,11 @@ const RecordMeterBar: React.FC< RecordMeterBarProps > = ( {
</ul>
</div>
<table className="screen-reader-text">
<caption>{ __( 'Summary of the records', 'jetpack' ) }</caption>
<caption>{ tableCaption || __( 'Summary of the records', 'jetpack' ) }</caption>
<tbody>
<tr>
<th scope="col">{ __( 'Record type', 'jetpack' ) }</th>
<th scope="col">{ __( 'Record count', 'jetpack' ) }</th>
<th scope="col">{ recordTypeLabel || __( 'Record type', 'jetpack' ) }</th>
<th scope="col">{ recordCountLabel || __( 'Record count', 'jetpack' ) }</th>
</tr>
{ itemsToRender.map( ( { label, count } ) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const ThemeProvider: React.FC< ThemeProviderProps > = ( {

// Do not wrap when the DOM element target is defined.
if ( targetDom ) {
return children;
return <>{ children }</>;
}

return <div ref={ themeWrapperRef }>{ children }</div>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type ThemeProviderProps = {
/**
* Content
*/
children?: React.ReactElement;
children?: React.ReactNode;
/**
* Inser global/reset styles
*/
Expand Down
2 changes: 1 addition & 1 deletion projects/js-packages/components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@automattic/jetpack-components",
"version": "0.45.6",
"version": "0.45.7-alpha",
"description": "Jetpack Components Package",
"author": "Automattic",
"license": "GPL-2.0-or-later",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added


Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Fixed Jetpack Social scheduled post messaging
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Fixed the scheduled post double count for share limits
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: changed

Updated the share limit bar design
2 changes: 2 additions & 0 deletions projects/js-packages/publicize-components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ export * from './src/social-store';
export * from './src/utils';
export * from './src/components/share-post';
export * from './src/hooks/use-sync-post-data-to-store';
export * from './src/components/share-limits-bar';
export * from './src/hooks/use-saving-post';
export * from './src/hooks/use-share-limits';
export * from './src/components/share-buttons';
2 changes: 1 addition & 1 deletion projects/js-packages/publicize-components/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@automattic/jetpack-publicize-components",
"version": "0.42.0",
"version": "0.43.0-alpha",
"description": "A library of JS components required by the Publicize editor plugin",
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/publicize-components/#readme",
"bugs": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { PanelRow } from '@wordpress/components';
import { _n, sprintf } from '@wordpress/i18n';
import usePublicizeConfig from '../../hooks/use-publicize-config';
import useSocialMediaConnections from '../../hooks/use-social-media-connections';
import styles from './styles.module.scss';

/**
* Displays enabled connections text.
*
* @returns {import('react').ReactElement} Enabled connections text.
*/
export function EnabledConnectionsNotice() {
const { enabledConnections } = useSocialMediaConnections();
const { isPublicizeEnabled } = usePublicizeConfig();

return enabledConnections.length && isPublicizeEnabled ? (
<PanelRow>
<p className={ styles[ 'enabled-connections-notice' ] }>
{ sprintf(
/* translators: %d: number of connections */
_n(
'This post will be shared to %d connection.',
'This post will be shared to %d connections.',
enabledConnections.length,
'jetpack'
),
enabledConnections.length
) }
</p>
</PanelRow>
) : null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

import { getRedirectUrl } from '@automattic/jetpack-components';
import { getSiteFragment } from '@automattic/jetpack-shared-extension-utils';
import { Button, PanelRow, Disabled, ExternalLink } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { Fragment, createInterpolateElement, useMemo, useCallback } from '@wordpress/element';
import { _n, sprintf, __ } from '@wordpress/i18n';
import { Button, Disabled, ExternalLink, PanelRow } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { createInterpolateElement, Fragment, useCallback, useMemo } from '@wordpress/element';
import { __, _n } from '@wordpress/i18n';
import useAttachedMedia from '../../hooks/use-attached-media';
import useDismissNotice from '../../hooks/use-dismiss-notice';
import useFeaturedImage from '../../hooks/use-featured-image';
Expand All @@ -22,13 +22,15 @@ import useRefreshAutoConversionSettings from '../../hooks/use-refresh-auto-conve
import useRefreshConnections from '../../hooks/use-refresh-connections';
import useSocialMediaConnections from '../../hooks/use-social-media-connections';
import useSocialMediaMessage from '../../hooks/use-social-media-message';
import { CONNECTION_SERVICE_INSTAGRAM_BUSINESS, SOCIAL_STORE_ID } from '../../social-store';
import { CONNECTION_SERVICE_INSTAGRAM_BUSINESS, store as socialStore } from '../../social-store';
import { getSupportedAdditionalConnections } from '../../utils';
import PublicizeConnection from '../connection';
import MediaSection from '../media-section';
import MessageBoxControl from '../message-box-control';
import Notice from '../notice';
import PublicizeSettingsButton from '../settings-button';
import { EnabledConnectionsNotice } from './enabled-connections-notice';
import { ShareCountInfo } from './share-count-info';
import styles from './styles.module.scss';

const MONTH_IN_SECONDS = 30 * 24 * 60 * 60;
Expand All @@ -42,7 +44,6 @@ const checkConnectionCode = ( connection, code ) =>
* @param {object} props - The component props.
* @param {boolean} props.isPublicizeEnabled - Whether Publicize is enabled for this post.
* @param {boolean} props.isPublicizeDisabledBySitePlan - A combination of the republicize feature being enabled and/or the post not being published.
* @param {number} props.numberOfSharesRemaining - The number of shares remaining for the current period. Optional.
* @param {boolean} props.isEnhancedPublishingEnabled - Whether enhanced publishing options are available. Optional.
* @param {boolean} props.isSocialImageGeneratorAvailable - Whether the Social Image Generator feature is available. Optional.
* @param {string} props.connectionsAdminUrl - URL to the Admin connections page
Expand All @@ -54,7 +55,6 @@ const checkConnectionCode = ( connection, code ) =>
export default function PublicizeForm( {
isPublicizeEnabled,
isPublicizeDisabledBySitePlan,
numberOfSharesRemaining = null,
isEnhancedPublishingEnabled = false,
shouldShowAdvancedPlanNudge = false,
isSocialImageGeneratorAvailable = false,
Expand All @@ -73,6 +73,12 @@ export default function PublicizeForm( {
connection => connection.service_name === 'instagram-business'
);

const { showShareLimits, numberOfSharesRemaining } = useSelect( select => {
return {
showShareLimits: select( socialStore ).showShareLimits(),
numberOfSharesRemaining: select( socialStore ).numberOfSharesRemaining(),
};
}, [] );
const shouldShowInstagramNotice =
! hasInstagramConnection &&
getSupportedAdditionalConnections().includes( CONNECTION_SERVICE_INSTAGRAM_BUSINESS ) &&
Expand All @@ -92,26 +98,7 @@ export default function PublicizeForm( {
checkConnectionCode( connection, 'unsupported' )
);

const outOfConnections =
numberOfSharesRemaining !== null && numberOfSharesRemaining <= enabledConnections.length;

const { isEditedPostDirty } = useSelect( 'core/editor' );
const { autosave } = useDispatch( 'core/editor' );
const autosaveAndRedirect = useCallback(
async ev => {
const target = ev.target.getAttribute( 'target' );
if ( isEditedPostDirty() && ! target ) {
ev.preventDefault();
await autosave();
window.location.href = ev.target.href;
}
if ( target ) {
ev.preventDefault();
window.open( ev.target.href, target, 'noreferrer' );
}
},
[ autosave, isEditedPostDirty ]
);
const outOfConnections = showShareLimits && numberOfSharesRemaining <= enabledConnections.length;

const onAdvancedNudgeDismiss = useCallback(
() => dismissNotice( NOTICES.advancedUpgradeEditor, 3 * MONTH_IN_SECONDS ),
Expand All @@ -124,7 +111,7 @@ export default function PublicizeForm( {
);

const isAutoConversionEnabled = useSelect(
select => select( SOCIAL_STORE_ID ).isAutoConversionEnabled(),
select => select( socialStore ).isAutoConversionEnabled(),
[]
);

Expand Down Expand Up @@ -319,53 +306,8 @@ export default function PublicizeForm( {
</li>
</ul>
</PanelRow>
{ numberOfSharesRemaining !== null && (
<PanelRow>
<Notice type={ numberOfSharesRemaining < connections.length ? 'warning' : 'default' }>
<Fragment>
{ createInterpolateElement(
sprintf(
/* translators: %d is the number of shares remaining, upgradeLink is the link to upgrade to a different plan */
_n(
'You have %d share remaining in the next 30 days. <upgradeLink>Upgrade now</upgradeLink> to share more.',
'You have %d shares remaining in the next 30 days. <upgradeLink>Upgrade now</upgradeLink> to share more.',
numberOfSharesRemaining,
'jetpack'
),
numberOfSharesRemaining
),
{
upgradeLink: (
<a
className={ styles[ 'upgrade-link' ] }
href={ getRedirectUrl( 'jetpack-social-basic-plan-block-editor', {
site: getSiteFragment(),
query: 'redirect_to=' + encodeURIComponent( window.location.href ),
} ) }
onClick={ autosaveAndRedirect }
/>
),
}
) }
<br />
<a
className={ styles[ 'more-link' ] }
href={ getRedirectUrl( 'jetpack-social-block-editor-more-info', {
site: getSiteFragment(),
...( adminUrl
? { query: 'redirect_to=' + encodeURIComponent( adminUrl ) }
: {} ),
} ) }
target="_blank"
rel="noopener noreferrer"
onClick={ autosaveAndRedirect }
>
{ __( 'More about Jetpack Social.', 'jetpack' ) }
</a>
</Fragment>
</Notice>
</PanelRow>
) }
<EnabledConnectionsNotice />
<ShareCountInfo />
{ renderNotices() }
{ showValidationNotice &&
( Object.values( validationErrors ).includes( NO_MEDIA_ERROR )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Text, ThemeProvider } from '@automattic/jetpack-components';
import { getRedirectUrl } from '@automattic/jetpack-components';
import { getSiteFragment } from '@automattic/jetpack-shared-extension-utils';
import { Button, PanelRow } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { __, _x } from '@wordpress/i18n';
import { useShareLimits } from '../../hooks/use-share-limits';
import { store as socialStore } from '../../social-store';
import { ShareLimitsBar } from '../share-limits-bar';
import { ShareCountNotice } from './share-count-notice';
import styles from './styles.module.scss';
import { useAutoSaveAndRedirect } from './use-auto-save-and-redirect';

export const ShareCountInfo: React.FC = () => {
const showShareLimits = useSelect( select => select( socialStore ).showShareLimits(), [] );
const { noticeType, usedCount, scheduledCount, remainingCount } = useShareLimits();
const autosaveAndRedirect = useAutoSaveAndRedirect();

if ( ! showShareLimits ) {
return null;
}

return (
<PanelRow>
<div className={ styles[ 'share-count-info' ] }>
<ThemeProvider>
<Text variant="body-extra-small" className={ styles[ 'auto-share-title' ] }>
{ __( 'Auto-share usage', 'jetpack' ) }
</Text>
<ShareCountNotice />
<ShareLimitsBar
usedCount={ usedCount }
scheduledCount={ scheduledCount }
remainingCount={ remainingCount }
className={ styles[ 'bar-wrapper' ] }
noticeType={ noticeType }
/>
{ noticeType === 'default' ? (
<Button
key="upgrade"
variant="link"
onClick={ autosaveAndRedirect }
href={ getRedirectUrl( 'jetpack-social-basic-plan-block-editor', {
site: getSiteFragment(),
query: 'redirect_to=' + encodeURIComponent( window.location.href ),
} ) }
>
{ _x( 'Upgrade to share more.', 'Call to action to buy a new plan', 'jetpack' ) }
</Button>
) : null }
</ThemeProvider>
</div>
</PanelRow>
);
};
Loading

0 comments on commit 5858f1e

Please sign in to comment.