-
Notifications
You must be signed in to change notification settings - Fork 2k
Notification Settings: Enable to configure notification settings by site #106080
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
gabrielcaires
merged 37 commits into
trunk
from
update/cml-879-enable-configure-notification-settings-by-site
Oct 2, 2025
Merged
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
3a7a5ef
Add me-notfication-devices fetchers
gabrielcaires 8c07f9f
Add missing types
gabrielcaires 425c1fb
Add generic notification settings mutator
gabrielcaires 9f4d79b
Add web settings component
gabrielcaires fe8d1b5
Add email settings component
gabrielcaires ccdc783
Add device settings
gabrielcaires 60b35fc
Render all settings components
gabrielcaires 0ed934f
Rename device settings folder
gabrielcaires a0dc176
Adjust card title
gabrielcaires 9fc5735
Adjust card title
gabrielcaires fd09225
Only send mimimum necessary data to update the settings
gabrielcaires 46356d8
Fix wrong device settings merge
gabrielcaires 5b86f98
Fix tests
gabrielcaires 18edccc
Remove manual snackbar trigger
gabrielcaires b716aca
Use section header
gabrielcaires 38eb971
Add preload data
gabrielcaires 9dc2386
Merge branch 'trunk' into update/add-settings-comments-screen
gabrielcaires a6bcb4d
Update empty state
gabrielcaires 4b74cb2
WIP
gabrielcaires 7ee2c7f
Add device settings component
gabrielcaires fc99d95
Add email settings component
gabrielcaires 7678305
Add web settings component
gabrielcaires 20ef6df
Implement the page
gabrielcaires d3e313f
Merge branch 'trunk' into update/cml-879-enable-configure-notificatio…
gabrielcaires 45b2aef
Load data before render page
gabrielcaires c44f0bd
Rename to site-preview
gabrielcaires e9b257b
Remove external link
gabrielcaires 819f3c3
mobile adjustments
gabrielcaires 9663b0b
Merge branch 'trunk' into update/cml-879-enable-configure-notificatio…
gabrielcaires 6807178
Remove unused placeholder
gabrielcaires 7f73c16
Mobile adjustments
gabrielcaires 35eb2b9
Fix app link
gabrielcaires e7aac58
Show confirmation modal
gabrielcaires 70f69bc
Improve copy
gabrielcaires 3490689
Add first version with loading
gabrielcaires f44ca79
Remove unused eslint config
gabrielcaires 5f54ffe
Fix tests
gabrielcaires File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
client/dashboard/me/notifications-sites/collapsible-card/index.scss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| .collapsible-card { | ||
| max-height: 100%; | ||
|
|
||
| .collapsible-card__toggle svg { | ||
| transition: transform 0.2s ease-in-out; | ||
| } | ||
|
|
||
| &.collapsed .collapsible-card__toggle svg { | ||
| transform: rotate(180deg); | ||
| } | ||
|
|
||
| .collapsed &__content { | ||
| display: none; | ||
| } | ||
|
|
||
| &.collapsed { | ||
| overflow: hidden; | ||
| } | ||
|
|
||
| } |
34 changes: 34 additions & 0 deletions
34
client/dashboard/me/notifications-sites/collapsible-card/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { Card, CardBody, __experimentalHStack as HStack, Button } from '@wordpress/components'; | ||
| import { chevronUp } from '@wordpress/icons'; | ||
| import clsx from 'clsx'; | ||
| import { useState } from 'react'; | ||
| import './index.scss'; | ||
|
|
||
| interface CollapsibleCardProps { | ||
| header: React.ReactNode; | ||
| children: React.ReactNode; | ||
| } | ||
|
|
||
| export const CollapsibleCard = ( { header, children }: CollapsibleCardProps ) => { | ||
| const [ isCollapsed, setIsCollapsed ] = useState< boolean >( true ); | ||
|
|
||
| const handleCollapsedChange = () => { | ||
| setIsCollapsed( ! isCollapsed ); | ||
| }; | ||
| return ( | ||
| <Card className={ clsx( 'collapsible-card', { collapsed: isCollapsed } ) }> | ||
| <CardBody> | ||
| <HStack> | ||
| { header } | ||
| <Button | ||
| icon={ chevronUp } | ||
| className={ clsx( 'collapsible-card__toggle', { collapsed: isCollapsed } ) } | ||
| variant="tertiary" | ||
| onClick={ handleCollapsedChange } | ||
| /> | ||
| </HStack> | ||
| { ! isCollapsed && <div className="collapsible-card__content">{ children }</div> } | ||
| </CardBody> | ||
| </Card> | ||
| ); | ||
| }; | ||
23 changes: 23 additions & 0 deletions
23
client/dashboard/me/notifications-sites/helpers/translations.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { __ } from '@wordpress/i18n'; | ||
|
|
||
| const translations = { | ||
| comment_like: __( 'Likes on my comments' ), | ||
| recommended_blog: __( 'Blog recommendations' ), | ||
| new_comment: __( 'Comments on my site' ), | ||
| post_like: __( 'Likes on my posts' ), | ||
| follow: __( 'Subscriptions' ), | ||
| achievement: __( 'Site achievements' ), | ||
| mentions: __( 'Username mentions' ), | ||
| scheduled_publicize: __( 'Jetpack Social' ), | ||
| blogging_prompt: __( 'Daily writing prompts' ), | ||
| draft_post_prompt: __( 'Draft post reminders' ), | ||
| store_order: __( 'New order' ), | ||
| comment_reply: __( 'Replies to my comments' ), | ||
| } as const; | ||
|
|
||
| type TranslationKey = keyof typeof translations; | ||
| type TranslationValue = ( typeof translations )[ TranslationKey ]; | ||
|
|
||
| export const getFieldLabel = ( key: TranslationKey ): TranslationValue => { | ||
| return translations[ key ] ?? key; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { | ||
| userNotificationsSettingsQuery, | ||
| userNotificationsSettingsMutation, | ||
| } from '@automattic/api-queries'; | ||
| import { useSuspenseQuery, useMutation } from '@tanstack/react-query'; | ||
| import { __ } from '@wordpress/i18n'; | ||
|
|
||
| export const useSiteSettings = ( blogId: number ) => { | ||
| return useSuspenseQuery( { | ||
| ...userNotificationsSettingsQuery(), | ||
| select: ( data ) => data?.blogs.find( ( blog ) => blog.blog_id === blogId ), | ||
| staleTime: 1000 * 60 * 5, | ||
| } ); | ||
| }; | ||
|
|
||
| export const useSettingsMutation = () => { | ||
| return useMutation( { | ||
| ...userNotificationsSettingsMutation(), | ||
| meta: { | ||
| snackbar: { | ||
| success: __( 'Settings saved successfully.' ), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We now have guidelines re: snackbar copy, please take a look 😄 |
||
| error: __( 'There was a problem saving your changes. Please, try again.' ), | ||
| }, | ||
| }, | ||
| } ); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { Spinner, __experimentalHStack as HStack } from '@wordpress/components'; | ||
|
|
||
| export const Loading = ( { style }: { style?: React.CSSProperties } ) => { | ||
| return ( | ||
| <HStack style={ style } justify="center" alignment="center"> | ||
| <Spinner /> | ||
| </HStack> | ||
| ); | ||
| }; |
21 changes: 21 additions & 0 deletions
21
client/dashboard/me/notifications-sites/site-list-settings/index.scss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| .site-list-settings { | ||
| .components-tab-panel__tabs { | ||
| border-bottom: solid 1px #ddd; | ||
| } | ||
| &__site-settings { | ||
| margin-left: -24px; | ||
| margin-right: -24px; | ||
| } | ||
| .components-tab-panel__tabs, .components-tab-panel__tab-content { | ||
| padding-left: 24px; | ||
| padding-right: 24px; | ||
| } | ||
| .components-tab-panel__tab-content { | ||
| padding-top: 16px; | ||
| } | ||
| &__card-placeholder { | ||
| height: 112px; | ||
| } | ||
| } | ||
|
|
||
|
|
73 changes: 73 additions & 0 deletions
73
client/dashboard/me/notifications-sites/site-list-settings/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import { Site } from '@automattic/api-core'; | ||
| import { sitesQuery } from '@automattic/api-queries'; | ||
| import { useFuzzySearch } from '@automattic/search'; | ||
| import { useSuspenseQuery } from '@tanstack/react-query'; | ||
| import { | ||
| Card, | ||
| CardBody, | ||
| __experimentalVStack as VStack, | ||
| SearchControl, | ||
| } from '@wordpress/components'; | ||
| import { createInterpolateElement } from '@wordpress/element'; | ||
| import { __, sprintf } from '@wordpress/i18n'; | ||
| import { useDeferredValue, useState } from 'react'; | ||
| import { CollapsibleCard } from '../collapsible-card'; | ||
| import { SitePreview } from '../site-preview'; | ||
| import { SiteSettings } from '../site-settings'; | ||
|
|
||
| import './index.scss'; | ||
|
|
||
| export const SiteListSettings = () => { | ||
| const [ search, setSearch ] = useState< string | undefined >(); | ||
| const { data: sites } = useSuspenseQuery( { | ||
| ...sitesQuery( { include_a8c_owned: true, site_visibility: 'visible' } ), | ||
| } ); | ||
|
|
||
| const deferredSearch = useDeferredValue( search ); | ||
|
|
||
| const filteredSites = useFuzzySearch< Site >( { | ||
| data: sites, | ||
| keys: [ 'name', 'URL' ], | ||
| query: deferredSearch, | ||
| } ); | ||
|
|
||
| const handleSearchChange = ( value: string | undefined ) => { | ||
| setSearch( value ); | ||
| }; | ||
|
|
||
| return ( | ||
| <VStack spacing={ 8 } className="site-list-settings"> | ||
| <SearchControl | ||
| value={ search } | ||
| placeholder={ __( 'Search for a site' ) } | ||
| onChange={ handleSearchChange } | ||
| __nextHasNoMarginBottom | ||
| /> | ||
| <VStack spacing={ 4 }> | ||
| { filteredSites.map( ( site: Site ) => ( | ||
| <CollapsibleCard key={ site.ID } header={ <SitePreview site={ site } /> }> | ||
| <SiteSettings siteId={ site.ID } className="site-list-settings__site-settings" /> | ||
| </CollapsibleCard> | ||
| ) ) } | ||
| { filteredSites.length === 0 && ( | ||
| <Card> | ||
| <CardBody> | ||
| { createInterpolateElement( | ||
| sprintf( | ||
| // translators: %s is the search query | ||
| __( 'No sites found with the search query <strong>%(search)s</strong>' ), | ||
| { | ||
| search: search, | ||
| } | ||
| ), | ||
| { | ||
| strong: <strong />, | ||
| } | ||
| ) } | ||
| </CardBody> | ||
| </Card> | ||
| ) } | ||
| </VStack> | ||
| </VStack> | ||
| ); | ||
| }; |
37 changes: 37 additions & 0 deletions
37
client/dashboard/me/notifications-sites/site-preview/index.scss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| .site-preview { | ||
| width: 100%; | ||
| overflow: hidden; | ||
|
|
||
| &__badge { | ||
| grid-column-start: 2; | ||
| } | ||
|
|
||
| &__url { | ||
| color: $gray-700; | ||
| display: flex; | ||
| flex: 1 1 100%; | ||
| min-width: 0; | ||
| width: 100%; | ||
|
|
||
| .components-external-link__contents { | ||
| max-width: 170px; | ||
| text-overflow: ellipsis; | ||
| overflow: hidden; | ||
| white-space: nowrap; | ||
|
|
||
| @include break-mobile { | ||
| overflow: unset; | ||
| max-width: unset; | ||
| max-width: 250px; | ||
| } | ||
| @include break-small { | ||
| max-width: unset; | ||
| } | ||
| } | ||
|
|
||
| &:hover { | ||
| color: var(--wp-admin-theme-color); | ||
| } | ||
| } | ||
|
|
||
| } |
64 changes: 64 additions & 0 deletions
64
client/dashboard/me/notifications-sites/site-preview/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { Site } from '@automattic/api-core'; | ||
| import { Badge } from '@automattic/ui'; | ||
| import { | ||
| __experimentalGrid as Grid, | ||
| __experimentalVStack as VStack, | ||
| ExternalLink, | ||
| } from '@wordpress/components'; | ||
| import { __ } from '@wordpress/i18n'; | ||
| import RouterLinkButton from '../../../components/router-link-button'; | ||
| import { isSitePlanTrial } from '../../../sites/plans'; | ||
| import { getSiteManagementUrl } from '../../../sites/site-fields'; | ||
| import SiteIcon from '../../../sites/site-icon'; | ||
| import { isP2, isStagingSite } from '../../../utils/site-types'; | ||
|
|
||
| import './index.scss'; | ||
|
|
||
| const getSiteBadge = ( site: Site ) => { | ||
| if ( isStagingSite( site ) ) { | ||
| return __( 'Staging' ); | ||
| } | ||
| if ( isSitePlanTrial( site ) ) { | ||
| return __( 'Trial' ); | ||
| } | ||
| if ( isP2( site ) ) { | ||
| return __( 'P2' ); | ||
| } | ||
| return null; | ||
| }; | ||
|
|
||
| interface Props { | ||
| site: Site; | ||
| } | ||
|
|
||
| export const SitePreview = ( { site }: Props ) => { | ||
| const badge = getSiteBadge( site ); | ||
|
|
||
| return ( | ||
| <Grid | ||
| className="site-preview" | ||
| columns={ 2 } | ||
| columnGap={ 12 } | ||
| rowGap={ badge ? 12 : 0 } | ||
| alignment="topLeft" | ||
| templateColumns="44px 1fr" | ||
| > | ||
| <SiteIcon site={ site } size={ 44 } /> | ||
| <VStack alignment="topLeft" spacing={ 1 }> | ||
| { site.name !== '' && ( | ||
| <RouterLinkButton | ||
| variant="link" | ||
| to={ getSiteManagementUrl( site ) ?? '' } | ||
| disabled={ site.is_deleted } | ||
| > | ||
| { site.name } | ||
| </RouterLinkButton> | ||
| ) } | ||
| <ExternalLink className="site-preview__url" href={ site.URL }> | ||
| { site.URL } | ||
| </ExternalLink> | ||
| </VStack> | ||
| <div className="site-preview__badge">{ badge && <Badge>{ badge }</Badge> }</div> | ||
| </Grid> | ||
| ); | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will need to be accessible.
Some issues:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I will do it in the next PR because this one is already too big.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good! Maybe consider moving this to the component folder as well. I know working on the performance tab, I was looking for something with this functionality.