Skip to content

Conversation

@alshakero
Copy link
Member

@alshakero alshakero commented Dec 30, 2025

Fixes dotcom-15390

Proposed Changes

This hooks up the Help Center and Odie client to the locally persisted chat and router states. In other words, this adds an alternative to Calypso preferences.

Why are these changes being made?

This allows logged out users to have coherent and continuous sessions.

Testing Instructions

Odie chats

  1. Run this locally.
  2. Visit http://calypso.localhost:3000/
  3. Open the HC.
  4. Start a chat by sending a distinct greeting (like howdy) and wait for Odie to respond.
  5. Refresh the page; the HC should continue the session.
  6. Click back; the homepage should contain a link to the conversation.
  7. Click it, the conversation should work.

Chat history

  1. Go to support history.
  2. The conversation should be there.
  3. Clicking it should render it, and it should function.

UI state

  1. Refreshing the page should automatically re-open the HC if it was left open and vice versa if not.
  2. Minimizing the HC and refreshing the page should keep it minimized.
  3. Opening a support doc, then closing the HC, should wipe the router state (reopening should land you at home).

Chat continuity after logging in

  1. Start a chat while logged out.
  2. Log in.
  3. The HC should continue rendering that chat.
  4. Go to support history.
  5. It should be an item among other chats.

@matticbot
Copy link
Contributor

matticbot commented Dec 30, 2025

This PR modifies the release build for the following Calypso Apps:

For info about this notification, see here: PCYsg-OT6-p2

  • agents-manager
  • blaze-dashboard
  • help-center
  • notifications
  • odyssey-stats

To test WordPress.com changes, run install-plugin.sh $pluginSlug logged-odie on your sandbox.

@matticbot
Copy link
Contributor

matticbot commented Dec 30, 2025

Here is how your PR affects size of JS and CSS bundles shipped to the user's browser:

App Entrypoints (~502 bytes removed 📉 [gzipped])

Details
name                    parsed_size           gzip_size
entry-reauth-required        -803 B  (-0.0%)     -247 B  (-0.0%)
entry-main                   -803 B  (-0.0%)     -246 B  (-0.0%)
entry-login                  -803 B  (-0.0%)     -245 B  (-0.0%)
entry-subscriptions          -777 B  (-0.0%)     -251 B  (-0.0%)
entry-stepper                -473 B  (-0.0%)     -151 B  (-0.0%)
entry-dashboard-dotcom       -346 B  (-0.0%)     -109 B  (-0.0%)
entry-dashboard-ciab         -346 B  (-0.0%)     -109 B  (-0.0%)
entry-domains-landing        -134 B  (-0.0%)      -85 B  (-0.0%)
entry-browsehappy            -134 B  (-0.1%)      -85 B  (-0.1%)

Common code that is always downloaded and parsed every time the app is loaded, no matter which route is used.

Sections (~305 bytes removed 📉 [gzipped])

Details
name                               parsed_size           gzip_size
async-step-unified-plans                -696 B  (-0.1%)     -118 B  (-0.0%)
staging-site                            -266 B  (-0.0%)      -30 B  (-0.0%)
site-settings                           -266 B  (-0.0%)      -30 B  (-0.0%)
site-performance                        -266 B  (-0.0%)      -30 B  (-0.0%)
site-monitoring                         -266 B  (-0.0%)      -30 B  (-0.0%)
site-logs                               -266 B  (-0.0%)      -30 B  (-0.0%)
plans                                   -266 B  (-0.0%)      -30 B  (-0.0%)
hosting                                 -266 B  (-0.0%)      -30 B  (-0.0%)
github-deployments                      -266 B  (-0.0%)      -30 B  (-0.0%)
sites-dashboard                         -263 B  (-0.0%)      -28 B  (-0.0%)
overview                                -263 B  (-0.0%)      -28 B  (-0.0%)
domains                                 -263 B  (-0.0%)      -28 B  (-0.0%)
woocommerce-installation                 -54 B  (-0.0%)       -7 B  (-0.0%)
themes                                   -54 B  (-0.0%)      -10 B  (-0.0%)
theme                                    -54 B  (-0.0%)       -8 B  (-0.0%)
subscribers                              -54 B  (-0.0%)       -5 B  (-0.0%)
stats                                    -54 B  (-0.0%)      -13 B  (-0.0%)
site-purchases                           -54 B  (-0.0%)       -5 B  (-0.0%)
settings-podcast                         -54 B  (-0.0%)       -9 B  (-0.0%)
settings-performance                     -54 B  (-0.0%)      -10 B  (-0.0%)
settings-newsletter                      -54 B  (-0.0%)       -5 B  (-0.0%)
settings                                 -54 B  (-0.0%)       -7 B  (-0.0%)
scan                                     -54 B  (-0.0%)       -6 B  (-0.0%)
purchases                                -54 B  (-0.0%)       -5 B  (-0.0%)
promote-post-i2                          -54 B  (-0.0%)       -7 B  (-0.0%)
plugins                                  -54 B  (-0.0%)       -8 B  (-0.0%)
people                                   -54 B  (-0.0%)       -7 B  (-0.0%)
marketplace                              -54 B  (-0.0%)       -8 B  (-0.0%)
marketing                                -54 B  (-0.0%)       -6 B  (-0.0%)
jetpack-social                           -54 B  (-0.0%)       -6 B  (-0.0%)
jetpack-cloud-plugin-management          -54 B  (-0.0%)       -8 B  (-0.0%)
import                                   -54 B  (-0.0%)       -6 B  (-0.0%)
email                                    -54 B  (-0.0%)      -15 B  (-0.0%)
earn                                     -54 B  (-0.0%)       -5 B  (-0.0%)
backup                                   -54 B  (-0.0%)       -6 B  (-0.0%)
a8c-for-agencies-plugins                 -54 B  (-0.0%)       -8 B  (-0.0%)
a8c-for-agencies-marketplace             -54 B  (-0.0%)       -8 B  (-0.0%)
a8c-for-agencies-express-checkout        -54 B  (-0.0%)       -8 B  (-0.0%)
a8c-for-agencies-client                  -54 B  (-0.0%)       -8 B  (-0.0%)
site-migration-flow                      -53 B  (-0.1%)       -7 B  (-0.0%)
site-blocks                              -53 B  (-0.0%)       -5 B  (-0.0%)
security                                 -53 B  (-0.0%)       -5 B  (-0.0%)
privacy                                  -53 B  (-0.0%)       -5 B  (-0.0%)
performance-profiler                     -53 B  (-0.0%)       -7 B  (-0.0%)
notification-settings                    -53 B  (-0.0%)       -5 B  (-0.0%)
me                                       -53 B  (-0.0%)       -5 B  (-0.0%)
jetpack-app                              -53 B  (-0.0%)       -6 B  (-0.0%)
developer                                -53 B  (-0.0%)       -5 B  (-0.0%)
account-close                            -53 B  (-0.0%)       -5 B  (-0.0%)
account                                  -53 B  (-0.0%)       -5 B  (-0.0%)
a8c-for-agencies-sites                   -53 B  (-0.0%)       -6 B  (-0.0%)
home                                     -51 B  (-0.0%)       -4 B  (-0.0%)
checkout                                 -51 B  (-0.0%)       +0 B

Sections contain code specific for a given set of routes. Is downloaded and parsed only when a particular route is navigated to.

Async-loaded Components (~289 bytes removed 📉 [gzipped])

Details
name                                                                              parsed_size           gzip_size
async-load-automattic-help-center                                                     +1494 B  (+0.0%)     +462 B  (+0.1%)
async-load-help-center-app                                                             +851 B  (+0.0%)     +312 B  (+0.0%)
async-load-automattic-agents-manager                                                   +767 B  (+0.0%)     +203 B  (+0.0%)
async-load-automattic-data-stores                                                      -710 B  (-0.8%)     -561 B  (-2.2%)
async-load-signup-steps-page-picker                                                     -54 B  (-0.0%)       -7 B  (-0.0%)
async-load-signup-steps-plans-theme-preselected                                         -53 B  (-0.0%)       -7 B  (-0.0%)
async-load-signup-steps-plans                                                           -53 B  (-0.0%)       -7 B  (-0.0%)
async-load-purchase-modal-wrapper                                                       -53 B  (-0.0%)       -6 B  (-0.0%)
async-load-my-sites-checkout-purchase-modal-is-eligible-for-one-click-checkou...        -53 B  (-0.0%)       -6 B  (-0.0%)
async-load-design                                                                       -53 B  (-0.0%)      -10 B  (-0.0%)
async-load-calypso-layout-guided-tours-component                                        -53 B  (-0.1%)       -7 B  (-0.0%)
async-load-calypso-layout-command-palette                                               -53 B  (-0.0%)       -7 B  (-0.0%)

React components that are loaded lazily, when a certain part of UI is displayed for the first time.

Legend

What is parsed and gzip size?

Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory.
Gzip Size: Compressed size of the JS and CSS files. This much data needs to be downloaded over network.

Generated by performance advisor bot at iscalypsofastyet.com.

Comment on lines 13 to 14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also removing these as it looks like they are not used here

@matticbot matticbot added the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Jan 14, 2026
persistValueSafely( 'logged_out_help_center_preferences', preferences );
} else if ( isLoggedIn ) {
// Delete local preferences when logged in to avoid conflicts.
persistValueSafely( 'logged_out_help_center_preferences', null );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put logged_out_help_center_preferences in a const somewhere? I see it being used a few times around

Comment on lines 314 to 330
if ( ! supportInteraction && chatId && ! isLoggedOutSession ) {
supportInteraction = await startNewInteraction( {
event_external_id: chatId.toString(),
event_source: 'odie',
} );
} else if ( supportInteraction && ! odieId && chatId ) {
} else if ( supportInteraction && ! odieId && chatId && ! isLoggedOutSession ) {
supportInteraction = await addEventToInteraction( {
interactionId: supportInteraction.uuid,
eventData: {
event_external_id: chatId.toString(),
event_source: 'odie',
},
} );
} else if ( isLoggedOutSession ) {
// If the user is not logged in, we don't need to create a new support interaction.
updateLoggedOutSession( chatId.toString(), returnedChat.session_id, botSlug );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of

	if ( isLoggedOutSession ) {
		// If the user is not logged in, we don't need to create a new support interaction.
		updateLoggedOutSession( chatId.toString(), returnedChat.session_id, botSlug );
	} else {
		if ( ! supportInteraction && chatId ) {
			...

		}

My brain works better this way, totally up to you!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's much better!

@alshakero alshakero marked this pull request as draft January 15, 2026 17:26
@alshakero alshakero marked this pull request as ready for review January 16, 2026 11:24
Comment on lines +17 to +19
window.localStorage.removeItem( PREFERENCES_KEY + 'help_center_open' );
window.localStorage.removeItem( PREFERENCES_KEY + 'help_center_minimized' );
window.localStorage.removeItem( PREFERENCES_KEY + 'help_center_router_history' );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: slightly better for maintainability

Suggested change
window.localStorage.removeItem( PREFERENCES_KEY + 'help_center_open' );
window.localStorage.removeItem( PREFERENCES_KEY + 'help_center_minimized' );
window.localStorage.removeItem( PREFERENCES_KEY + 'help_center_router_history' );
try {
const keys = Object.keys( memoryStore ) as Array< keyof Preferences[ 'calypso_preferences' ] >;
keys.forEach( ( key ) => {
window.localStorage.removeItem( PREFERENCES_KEY + key );
} );

Copy link
Contributor

@heavyweight heavyweight left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't find any blockers

@alshakero alshakero merged commit 7358d18 into trunk Jan 16, 2026
13 checks passed
@alshakero alshakero deleted the logged-odie branch January 16, 2026 14:09
@github-actions github-actions bot removed the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Jan 16, 2026
heydemoura pushed a commit that referenced this pull request Jan 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants