diff --git a/client/blocks/reader-site-subscription/details.tsx b/client/blocks/reader-site-subscription/details.tsx
index b2d8ec0dcb6f8..9e43932757f9e 100644
--- a/client/blocks/reader-site-subscription/details.tsx
+++ b/client/blocks/reader-site-subscription/details.tsx
@@ -18,6 +18,7 @@ import {
getPaymentInterval,
} from './helpers';
import SiteSubscriptionSettings from './settings';
+import SubscribeToNewsletterCategories from './subscribe-to-newsletter-categories';
import './styles.scss';
const SiteSubscriptionDetails = ( {
@@ -221,11 +222,17 @@ const SiteSubscriptionDetails = ( {
emailMeNewComments={ !! deliveryMethods.email?.send_comments }
/>
+ { !! deliveryMethods.email?.send_posts && (
+
+ ) }
+
{ /* TODO: Move to SiteSubscriptionInfo component when payment details are in. */ }
-
{ translate( 'Subscription' ) }
+
+ { translate( 'Subscription details' ) }
+
- { translate( 'Date' ) }
-
diff --git a/client/blocks/reader-site-subscription/styles.scss b/client/blocks/reader-site-subscription/styles.scss
index 6b296db84dba3..d1d3ae865f708 100644
--- a/client/blocks/reader-site-subscription/styles.scss
+++ b/client/blocks/reader-site-subscription/styles.scss
@@ -136,6 +136,10 @@
}
}
}
+
+ &__loading {
+ margin-bottom: 20px;
+ }
}
&__unsubscribe-button.components-button {
diff --git a/client/blocks/reader-site-subscription/subscribe-to-newsletter-categories.tsx b/client/blocks/reader-site-subscription/subscribe-to-newsletter-categories.tsx
new file mode 100644
index 0000000000000..b38278c501db8
--- /dev/null
+++ b/client/blocks/reader-site-subscription/subscribe-to-newsletter-categories.tsx
@@ -0,0 +1,86 @@
+import config from '@automattic/calypso-config';
+import { Spinner } from '@automattic/components';
+import { ToggleControl } from '@wordpress/components';
+import { useTranslate } from 'i18n-calypso';
+import { useMemo } from 'react';
+import {
+ useNewsletterCategorySubscriptionMutation,
+ useSubscribedNewsletterCategories,
+} from 'calypso/data/newsletter-categories';
+
+type SubscribeToNewsletterCategoriesProps = {
+ siteId: number;
+};
+
+const SubscribeToNewsletterCategories = ( { siteId }: SubscribeToNewsletterCategoriesProps ) => {
+ const translate = useTranslate();
+ const { data: subscribedNewsletterCategoriesData, isLoading } = useSubscribedNewsletterCategories(
+ { siteId }
+ );
+ const { mutate, isLoading: isSaving } = useNewsletterCategorySubscriptionMutation( siteId );
+
+ const subscribedCategoryIds = useMemo(
+ () =>
+ subscribedNewsletterCategoriesData?.newsletterCategories
+ .filter( ( category ) => !! category.subscribed )
+ .map( ( category ) => category.id ) || [],
+ [ subscribedNewsletterCategoriesData ]
+ );
+
+ const handleToggle = ( categoryId: number ) => {
+ mutate( [
+ {
+ term_id: categoryId,
+ subscribe: ! subscribedCategoryIds.includes( categoryId ),
+ },
+ ] );
+ };
+
+ if (
+ ! config.isEnabled( 'settings/newsletter-categories' ) ||
+ ! subscribedNewsletterCategoriesData?.enabled
+ ) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+
{ translate( 'Subscription' ) }
+
+ { isLoading ? (
+
+
+
+ ) : (
+
+ - { translate( 'Subscribed to' ) }
+ -
+ { subscribedNewsletterCategoriesData?.newsletterCategories.map(
+ ( newletterCategory ) => (
+
+
handleToggle( newletterCategory.id ) }
+ disabled={ isSaving }
+ label={ newletterCategory.name }
+ />
+
+ { translate( 'Receive emails for new posts in %s', {
+ args: [ newletterCategory.name ],
+ comment: 'Name of the site that the user tried to resubscribe to.',
+ } ) }
+
+
+ )
+ ) }
+
+
+ ) }
+
+ >
+ );
+};
+
+export default SubscribeToNewsletterCategories;
diff --git a/client/data/newsletter-categories/index.ts b/client/data/newsletter-categories/index.ts
index 69bca125ff932..b1833c646b7f2 100644
--- a/client/data/newsletter-categories/index.ts
+++ b/client/data/newsletter-categories/index.ts
@@ -3,3 +3,5 @@ export { default as useNewsletterCategoriesFeatureEnabled } from './use-newslett
export { default as useNewsletterCategoriesQuery } from './use-newsletter-categories-query';
export { default as useMarkAsNewsletterCategoryMutation } from './use-mark-as-newsletter-category-mutation';
export { default as useUnmarkAsNewsletterCategoryMutation } from './use-unmark-as-newsletter-category-mutation';
+export { default as useSubscribedNewsletterCategories } from './use-subscribed-newsletter-categories-query';
+export { default as useNewsletterCategorySubscriptionMutation } from './use-newsletter-category-subscription-mutation';
diff --git a/client/data/newsletter-categories/test/use-subscriber-newsletter-categories-query.test.tsx b/client/data/newsletter-categories/test/use-subscribed-newsletter-categories-query.test.tsx
similarity index 89%
rename from client/data/newsletter-categories/test/use-subscriber-newsletter-categories-query.test.tsx
rename to client/data/newsletter-categories/test/use-subscribed-newsletter-categories-query.test.tsx
index 89a18d3c11a64..96d607e9a5884 100644
--- a/client/data/newsletter-categories/test/use-subscriber-newsletter-categories-query.test.tsx
+++ b/client/data/newsletter-categories/test/use-subscribed-newsletter-categories-query.test.tsx
@@ -6,11 +6,11 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook, waitFor } from '@testing-library/react';
import React from 'react';
import request from 'wpcom-proxy-request';
-import useSubscriberNewsletterCategories from '../use-subscriber-newsletter-categories-query';
+import useSubscribedNewsletterCategories from '../use-subscribed-newsletter-categories-query';
jest.mock( 'wpcom-proxy-request', () => jest.fn() );
-describe( 'useSubscriberNewsletterCategories', () => {
+describe( 'useSubscribedNewsletterCategories', () => {
let queryClient: QueryClient;
let wrapper: any;
@@ -56,7 +56,7 @@ describe( 'useSubscriberNewsletterCategories', () => {
],
} );
- const { result } = renderHook( () => useSubscriberNewsletterCategories( { siteId: 123 } ), {
+ const { result } = renderHook( () => useSubscribedNewsletterCategories( { siteId: 123 } ), {
wrapper,
} );
@@ -89,7 +89,7 @@ describe( 'useSubscriberNewsletterCategories', () => {
newsletter_categories: [],
} );
- const { result } = renderHook( () => useSubscriberNewsletterCategories( { siteId: 123 } ), {
+ const { result } = renderHook( () => useSubscribedNewsletterCategories( { siteId: 123 } ), {
wrapper,
} );
@@ -103,7 +103,7 @@ describe( 'useSubscriberNewsletterCategories', () => {
success: true,
} );
- renderHook( () => useSubscriberNewsletterCategories( { siteId: 123 } ), {
+ renderHook( () => useSubscribedNewsletterCategories( { siteId: 123 } ), {
wrapper,
} );
diff --git a/client/data/newsletter-categories/types.ts b/client/data/newsletter-categories/types.ts
index 5d671728d13f9..f471b285d563d 100644
--- a/client/data/newsletter-categories/types.ts
+++ b/client/data/newsletter-categories/types.ts
@@ -24,3 +24,8 @@ export type NewsletterCategory = {
parent: number;
subscribed?: boolean;
};
+
+export type NewsletterCategoriesResponse = {
+ enabled: boolean;
+ newsletter_categories: NewsletterCategory[];
+};
diff --git a/client/data/newsletter-categories/use-categories-query.tsx b/client/data/newsletter-categories/use-categories-query.tsx
index e9bc786a66e5d..9df91d234e3f8 100644
--- a/client/data/newsletter-categories/use-categories-query.tsx
+++ b/client/data/newsletter-categories/use-categories-query.tsx
@@ -2,9 +2,11 @@ import { useQuery, UseQueryResult } from '@tanstack/react-query';
import request from 'wpcom-proxy-request';
import type { Category } from './types';
+export const getCategoriesKey = ( siteId?: string | number ) => [ 'categories', siteId ];
+
const useCategoriesQuery = ( siteId?: string | number ): UseQueryResult< Category[] > => {
return useQuery( {
- queryKey: [ 'categories', siteId ],
+ queryKey: getCategoriesKey( siteId ),
queryFn: () =>
request< Category[] >( {
path: `/sites/${ siteId }/categories`,
diff --git a/client/data/newsletter-categories/use-mark-as-newsletter-category-mutation.tsx b/client/data/newsletter-categories/use-mark-as-newsletter-category-mutation.tsx
index 025139c64a67c..5703e732d71b9 100644
--- a/client/data/newsletter-categories/use-mark-as-newsletter-category-mutation.tsx
+++ b/client/data/newsletter-categories/use-mark-as-newsletter-category-mutation.tsx
@@ -60,8 +60,8 @@ const useMarkAsNewsletterCategoryMutation = ( siteId: string | number ) => {
onError: ( error, variables, context ) => {
queryClient.setQueryData( cacheKey, context?.previousData );
},
- onSettled: () => {
- queryClient.invalidateQueries( cacheKey );
+ onSettled: async () => {
+ await queryClient.invalidateQueries( cacheKey );
},
} );
};
diff --git a/client/data/newsletter-categories/use-newsletter-categories-query.tsx b/client/data/newsletter-categories/use-newsletter-categories-query.tsx
index 0f9740100798e..84fcbf3d3c676 100644
--- a/client/data/newsletter-categories/use-newsletter-categories-query.tsx
+++ b/client/data/newsletter-categories/use-newsletter-categories-query.tsx
@@ -1,18 +1,13 @@
import { useQuery, UseQueryResult } from '@tanstack/react-query';
import request from 'wpcom-proxy-request';
-import { NewsletterCategories, NewsletterCategory } from './types';
+import { NewsletterCategories, NewsletterCategoriesResponse } from './types';
type NewsletterCategoryQueryProps = {
siteId?: string | number;
};
-type NewsletterCategoryResponse = {
- enabled: boolean;
- newsletter_categories: NewsletterCategory[];
-};
-
-const convertNewsletterCategoryResponse = (
- response: NewsletterCategoryResponse
+export const convertNewsletterCategoriesResponse = (
+ response: NewsletterCategoriesResponse
): NewsletterCategories => {
return {
enabled: response.enabled,
@@ -31,11 +26,11 @@ const useNewsletterCategories = ( {
return useQuery( {
queryKey: getNewsletterCategoriesKey( siteId ),
queryFn: () =>
- request< NewsletterCategoryResponse >( {
+ request< NewsletterCategoriesResponse >( {
path: `/sites/${ siteId }/newsletter-categories`,
apiVersion: '2',
apiNamespace: 'wpcom/v2',
- } ).then( convertNewsletterCategoryResponse ),
+ } ).then( convertNewsletterCategoriesResponse ),
enabled: !! siteId,
} );
};
diff --git a/client/data/newsletter-categories/use-newsletter-category-subscription-mutation.tsx b/client/data/newsletter-categories/use-newsletter-category-subscription-mutation.tsx
new file mode 100644
index 0000000000000..b0b367162d617
--- /dev/null
+++ b/client/data/newsletter-categories/use-newsletter-category-subscription-mutation.tsx
@@ -0,0 +1,72 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import request from 'wpcom-proxy-request';
+import { NewsletterCategories, NewsletterCategoriesResponse } from './types';
+import { getSubscribedNewsletterCategoriesKey } from './use-subscribed-newsletter-categories-query';
+
+type NewsletterCategorySubscription = {
+ term_id: number;
+ subscribe: boolean;
+};
+
+const useNewsletterCategorySubscriptionMutation = ( siteId: string | number ) => {
+ const queryClient = useQueryClient();
+ const subscribedCategoriesCacheKey = getSubscribedNewsletterCategoriesKey( siteId );
+
+ return useMutation( {
+ mutationFn: async ( categorySubscriptions: NewsletterCategorySubscription[] ) => {
+ return await request< NewsletterCategoriesResponse >( {
+ path: `/sites/${ siteId }/newsletter-categories/subscriptions`,
+ method: 'POST',
+ apiVersion: '2',
+ apiNamespace: 'wpcom/v2',
+ body: { categories: categorySubscriptions },
+ } );
+ },
+ onMutate: async ( categorySubscriptions: NewsletterCategorySubscription[] ) => {
+ await queryClient.cancelQueries( subscribedCategoriesCacheKey );
+
+ const previousData = queryClient.getQueryData< NewsletterCategories >(
+ subscribedCategoriesCacheKey
+ );
+
+ queryClient.setQueryData(
+ subscribedCategoriesCacheKey,
+ ( oldData?: NewsletterCategories ) => {
+ const newSubscribedCategories =
+ previousData?.newsletterCategories.map( ( category ) => {
+ const categorySubscription = categorySubscriptions.find(
+ ( subscription ) => subscription.term_id === category.id
+ );
+
+ if ( ! categorySubscription ) {
+ return category;
+ }
+
+ return {
+ ...category,
+ subscribed: categorySubscription.subscribe,
+ };
+ } ) || [];
+
+ const updatedData = {
+ ...oldData,
+ enabled: oldData?.enabled || false,
+ newsletterCategories: newSubscribedCategories,
+ };
+
+ return updatedData;
+ }
+ );
+
+ return { previousData };
+ },
+ onError: ( error, variables, context ) => {
+ queryClient.setQueryData( subscribedCategoriesCacheKey, context?.previousData );
+ },
+ onSettled: async () => {
+ await queryClient.invalidateQueries( subscribedCategoriesCacheKey );
+ },
+ } );
+};
+
+export default useNewsletterCategorySubscriptionMutation;
diff --git a/client/data/newsletter-categories/use-subscriber-newsletter-categories-query.tsx b/client/data/newsletter-categories/use-subscribed-newsletter-categories-query.tsx
similarity index 79%
rename from client/data/newsletter-categories/use-subscriber-newsletter-categories-query.tsx
rename to client/data/newsletter-categories/use-subscribed-newsletter-categories-query.tsx
index 7b4891c399121..21db3ef48190c 100644
--- a/client/data/newsletter-categories/use-subscriber-newsletter-categories-query.tsx
+++ b/client/data/newsletter-categories/use-subscribed-newsletter-categories-query.tsx
@@ -12,10 +12,10 @@ type NewsletterCategoryResponse = {
newsletter_categories: NewsletterCategory[];
};
-export const getSubscriberNewsletterCategoriesKey = (
+export const getSubscribedNewsletterCategoriesKey = (
siteId?: string | number,
subscriptionId?: number
-) => [ `newsletter-categories`, siteId, subscriptionId ];
+) => [ 'subscribed-newsletter-categories', siteId, subscriptionId ];
const convertNewsletterCategoryResponse = (
response: NewsletterCategoryResponse
@@ -24,12 +24,12 @@ const convertNewsletterCategoryResponse = (
newsletterCategories: response.newsletter_categories,
} );
-const useSubscriberNewsletterCategories = ( {
+const useSubscribedNewsletterCategories = ( {
siteId,
subscriptionId,
}: NewsletterCategoryQueryProps ): UseQueryResult< NewsletterCategories > => {
return useQuery( {
- queryKey: getSubscriberNewsletterCategoriesKey( siteId, subscriptionId ),
+ queryKey: getSubscribedNewsletterCategoriesKey( siteId, subscriptionId ),
queryFn: () =>
request< NewsletterCategoryResponse >( {
path: `/sites/${ siteId }/newsletter-categories/subscriptions${
@@ -42,4 +42,4 @@ const useSubscriberNewsletterCategories = ( {
} );
};
-export default useSubscriberNewsletterCategories;
+export default useSubscribedNewsletterCategories;
diff --git a/client/data/newsletter-categories/use-unmark-as-newsletter-category-mutation.tsx b/client/data/newsletter-categories/use-unmark-as-newsletter-category-mutation.tsx
index d212634fd136b..7ba9ec00a2531 100644
--- a/client/data/newsletter-categories/use-unmark-as-newsletter-category-mutation.tsx
+++ b/client/data/newsletter-categories/use-unmark-as-newsletter-category-mutation.tsx
@@ -52,8 +52,8 @@ const useUnmarkAsNewsletterCategoryMutation = ( siteId: string | number ) => {
onError: ( error, variables, context ) => {
queryClient.setQueryData( cacheKey, context?.previousData );
},
- onSettled: () => {
- queryClient.invalidateQueries( cacheKey );
+ onSettled: async () => {
+ await queryClient.invalidateQueries( cacheKey );
},
} );
};
diff --git a/client/landing/subscriptions/components/settings/styles.scss b/client/landing/subscriptions/components/settings/styles.scss
index 71e64c5fb0146..d6f78b9230832 100644
--- a/client/landing/subscriptions/components/settings/styles.scss
+++ b/client/landing/subscriptions/components/settings/styles.scss
@@ -1,14 +1,14 @@
@import "@automattic/color-studio/dist/color-variables";
@import "@automattic/typography/styles/variables";
-$setting-item-margin-bottom: 16px;
+$setting-item-margin-bottom: 20px;
.settings {
.setting-item {
margin-bottom: $setting-item-margin-bottom;
&__hint {
- margin-top: 16px;
+ margin-top: 8px;
color: $studio-gray-50;
font-size: $font-body-extra-small;
text-align: left;
diff --git a/client/my-sites/subscribers/components/subscriber-details/subscriber-details.tsx b/client/my-sites/subscribers/components/subscriber-details/subscriber-details.tsx
index 44b76587710f3..c06442a41d336 100644
--- a/client/my-sites/subscribers/components/subscriber-details/subscriber-details.tsx
+++ b/client/my-sites/subscribers/components/subscriber-details/subscriber-details.tsx
@@ -30,7 +30,10 @@ const SubscriberDetails = ( {
const translate = useTranslate();
const subscriptionPlans = useSubscriptionPlans( subscriber );
const newsletterCategoryNames = useMemo(
- () => newsletterCategories?.map( ( category ) => category.name ),
+ () =>
+ newsletterCategories
+ ?.filter( ( category ) => !! category.subscribed )
+ .map( ( category ) => category.name ),
[ newsletterCategories ]
);
const { avatar, date_subscribed, display_name, email_address, country, url } = subscriber;
diff --git a/client/my-sites/subscribers/subscriber-details-page.tsx b/client/my-sites/subscribers/subscriber-details-page.tsx
index 84eec27e53644..001f159bb0d5a 100644
--- a/client/my-sites/subscribers/subscriber-details-page.tsx
+++ b/client/my-sites/subscribers/subscriber-details-page.tsx
@@ -5,7 +5,7 @@ import { useDispatch } from 'react-redux';
import { Item } from 'calypso/components/breadcrumb';
import FixedNavigationHeader from 'calypso/components/fixed-navigation-header';
import Main from 'calypso/components/main';
-import useSubscriberNewsletterCategories from 'calypso/data/newsletter-categories/use-subscriber-newsletter-categories-query';
+import { useSubscribedNewsletterCategories } from 'calypso/data/newsletter-categories';
import { useSelector } from 'calypso/state';
import { successNotice } from 'calypso/state/notices/actions';
import { getSelectedSiteId, getSelectedSiteSlug } from 'calypso/state/ui/selectors';
@@ -46,8 +46,8 @@ const SubscriberDetailsPage = ( {
userId
);
- const { data: newsletterCategoriesData, isLoading: isLoadingNewsletterCategories } =
- useSubscriberNewsletterCategories( {
+ const { data: subscribedNewsletterCategoriesData, isLoading: isLoadingNewsletterCategories } =
+ useSubscribedNewsletterCategories( {
siteId: selectedSiteId as number,
subscriptionId: subscriptionId || subscriber?.subscription_id,
} );
@@ -114,8 +114,8 @@ const SubscriberDetailsPage = ( {
siteId={ selectedSiteId }
subscriptionId={ subscriptionId }
userId={ userId }
- newsletterCategoriesEnabled={ newsletterCategoriesData?.enabled }
- newsletterCategories={ newsletterCategoriesData?.newsletterCategories }
+ newsletterCategoriesEnabled={ subscribedNewsletterCategoriesData?.enabled }
+ newsletterCategories={ subscribedNewsletterCategoriesData?.newsletterCategories }
/>
) }