Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/agents-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"dependencies": {
"@automattic/agenttic-client": "^0.1.23",
"@automattic/agenttic-ui": "^0.1.23",
"@automattic/help-center": "workspace:^",
"@automattic/oauth-token": "workspace:^",
"@rive-app/react-canvas": "^4.15.6",
"@wordpress/api-fetch": "^7.23.0",
Expand Down
43 changes: 36 additions & 7 deletions packages/agents-manager/src/components/agent-dock/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { AgentsManagerSelect } from '@automattic/data-stores';
import { useDispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { comment, drawerRight, login } from '@wordpress/icons';
import { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { Route, Routes, useNavigate } from 'react-router-dom';
import { useAgentSession } from '../../hooks/use-agent-session';
import useChatLayoutManager from '../../hooks/use-chat-layout-manager';
import { AGENTS_MANAGER_STORE } from '../../stores';
Expand Down Expand Up @@ -79,6 +80,7 @@ export default function AgentDock( {
onClearChat,
sessionStorageKey = 'agents-manager-session',
}: AgentDockProps ) {
const navigate = useNavigate();
const { setIsOpen } = useDispatch( AGENTS_MANAGER_STORE );
const persistedState = useSelect( ( select ) => {
const store: AgentsManagerSelect = select( AGENTS_MANAGER_STORE );
Expand Down Expand Up @@ -172,6 +174,14 @@ export default function AgentDock( {
chatHeaderOptions.push( dockMenuItem );
}

chatHeaderOptions.push( {
icon: login,
title: __( 'Go to test', 'agents-manager' ),
onClick: () => {
navigate( '/test' );
},
} );

return (
<AgentUI.Container
messages={ messages }
Expand Down Expand Up @@ -199,12 +209,31 @@ export default function AgentDock( {
onClose={ isDocked ? closeSidebar : setChatIsClosed }
options={ chatHeaderOptions }
/>
<AgentUI.Messages />
<AgentUI.Footer>
<AgentUI.Suggestions />
<AgentUI.Notice />
<AgentUI.Input />
</AgentUI.Footer>
<Routes>
<Route
path="/"
element={
<>
<AgentUI.Messages />
<AgentUI.Footer>
<AgentUI.Suggestions />
<AgentUI.Notice />
<AgentUI.Input />
</AgentUI.Footer>
</>
}
/>
<Route
path="/test"
element={
<div style={ { display: 'flex', flexDirection: 'column', gap: 20, flex: 1 } }>
<h1>This is a test page</h1>
<button onClick={ () => navigate( -1 ) }>Go to home</button>
<br />
</div>
}
/>
</Routes>
</AgentUI.ConversationView>
</AgentUI.Container>
);
Expand Down
31 changes: 22 additions & 9 deletions packages/agents-manager/src/components/unified-ai-agent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
* Main wrapper component for loading AI agent in Calypso
*/

import { AgentsManagerSelect } from '@automattic/data-stores';
import { PersistentRouter } from '@automattic/help-center';
import { useSelect } from '@wordpress/data';
import { useCallback, useMemo } from 'react';
import { CalypsoContextAdapter } from '../../adapters/context/calypso-context-adapter';
import { createCalypsoAuthProvider } from '../../auth/calypso-auth-provider';
import { AGENTS_MANAGER_STORE } from '../../stores';
import AgentDock from '../agent-dock';
import type { UseAgentChatConfig } from '@automattic/agenttic-client';

Expand Down Expand Up @@ -54,6 +58,10 @@ export default function CalypsoAIAgent( {
savePreference: externalSavePreference,
loadPreference: externalLoadPreference,
}: UnifiedAIAgentProps ) {
const routerHistory = useSelect( ( select ) => {
const store: AgentsManagerSelect = select( AGENTS_MANAGER_STORE );
return store.getRouterHistory();
}, [] );
// Create context adapter for Calypso
// TODO: Pass this to AgentDock once context integration is needed
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -147,14 +155,19 @@ export default function CalypsoAIAgent( {
const loadPreference = externalLoadPreference || defaultLoadPreference;

return (
<AgentDock
agentConfig={ agentConfig }
emptyViewSuggestions={ suggestions }
onClearChat={ handleClearChat }
sessionStorageKey="agents-manager-session"
preferenceKey="agents_manager_state"
savePreference={ savePreference }
loadPreference={ loadPreference }
/>
<PersistentRouter
routerHistory={ routerHistory }
persistenceKey="agents_manager_router_history"
>
<AgentDock
agentConfig={ agentConfig }
emptyViewSuggestions={ suggestions }
onClearChat={ handleClearChat }
sessionStorageKey="agents-manager-session"
preferenceKey="agents_manager_state"
savePreference={ savePreference }
loadPreference={ loadPreference }
/>
</PersistentRouter>
);
}
8 changes: 3 additions & 5 deletions packages/data-stores/src/agents-manager/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function* saveAgentsManagerState( state: AgentsManagerState ) {
// Help center resets when closing. But the agents manager might be different.
if ( ! state.isOpen ) {
saveState.agents_manager_router_history = null;
yield setAgentsManagerRouterHistory( undefined );
yield setRouterHistory( undefined );
}
}

Expand Down Expand Up @@ -60,9 +60,7 @@ export function* saveAgentsManagerState( state: AgentsManagerState ) {
}
}

export function setAgentsManagerRouterHistory(
history: { entries: Location[]; index: number } | undefined
) {
export function setRouterHistory( history: { entries: Location[]; index: number } | undefined ) {
return {
type: 'AGENTS_MANAGER_SET_ROUTER_HISTORY',
history,
Expand Down Expand Up @@ -106,7 +104,7 @@ export function setHasLoaded( hasLoaded: boolean ) {
}

export type AgentsManagerAction =
| ReturnType< typeof setAgentsManagerRouterHistory >
| ReturnType< typeof setRouterHistory >
| ReturnType< typeof setIsLoading >
| ReturnType< typeof setHasLoaded >
| GeneratorReturnType< typeof setIsOpen >
Expand Down
4 changes: 2 additions & 2 deletions packages/data-stores/src/agents-manager/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const isDocked: Reducer< boolean | undefined, AgentsManagerAction > = ( state, a
return state;
};

const agentsManagerRouterHistory: Reducer<
const routerHistory: Reducer<
{ entries: Location[]; index: number } | undefined,
AgentsManagerAction
> = ( state, action ) => {
Expand Down Expand Up @@ -49,7 +49,7 @@ const hasLoaded: Reducer< boolean, AgentsManagerAction > = ( state = false, acti
const reducer = combineReducers( {
isOpen,
isDocked,
agentsManagerRouterHistory,
routerHistory,
isLoading,
hasLoaded,
} );
Expand Down
10 changes: 2 additions & 8 deletions packages/data-stores/src/agents-manager/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@ import { apiFetch } from '@wordpress/data-controls';
import { Location } from 'history';
import { canAccessWpcomApis } from 'wpcom-proxy-request';
import { wpcomRequest } from '../wpcom-request-controls';
import {
setAgentsManagerRouterHistory,
setIsDocked,
setIsOpen,
setIsLoading,
setHasLoaded,
} from './actions';
import { setRouterHistory, setIsDocked, setIsOpen, setIsLoading, setHasLoaded } from './actions';
import type { APIFetchOptions } from '../shared-types';

type Preferences = {
Expand Down Expand Up @@ -37,7 +31,7 @@ export function* getAgentsManagerState() {

// Restore the navigation history from preferences
if ( preferences.agents_manager_router_history ) {
yield setAgentsManagerRouterHistory( preferences.agents_manager_router_history );
yield setRouterHistory( preferences.agents_manager_router_history );
}

// Restore the docked state from preferences
Expand Down
4 changes: 2 additions & 2 deletions packages/data-stores/src/agents-manager/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import type { State } from './reducer';
export const getAgentsManagerState = ( state: State ) => ( {
isOpen: state.isOpen,
isDocked: state.isDocked,
agentsManagerRouterHistory: state.agentsManagerRouterHistory,
routerHistory: state.routerHistory,
isLoading: state.isLoading,
hasLoaded: state.hasLoaded,
} );
export const getIsOpen = ( state: State ) => state.isOpen;
export const getIsDocked = ( state: State ) => state.isDocked;
export const getAgentsManagerRouterHistory = ( state: State ) => state.agentsManagerRouterHistory;
export const getRouterHistory = ( state: State ) => state.routerHistory;
export const getIsLoading = ( state: State ) => state.isLoading;
export const getHasLoaded = ( state: State ) => state.hasLoaded;
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ const OptionalDraggable: FC< OptionalDraggableProps > = ( { draggable, ...props
};

const HelpCenterContainer: React.FC< Container > = ( { handleClose, hidden, currentRoute } ) => {
const { show, isMinimized } = useSelect( ( select ) => {
const { show, isMinimized, persistedHistory } = useSelect( ( select ) => {
const store = select( HELP_CENTER_STORE ) as HelpCenterSelect;
return {
show: store.isHelpCenterShown(),
isMinimized: store.getIsMinimized(),
persistedHistory: store.getHelpCenterRouterHistory(),
};
}, [] );
const { sectionName } = useHelpCenterContext();
Expand Down Expand Up @@ -100,7 +101,7 @@ const HelpCenterContainer: React.FC< Container > = ( { handleClose, hidden, curr
}

return (
<PersistentRouter>
<PersistentRouter routerHistory={ persistedHistory }>
<FeatureFlagProvider>
<OptionalDraggable
draggable={ ! isMobile && ! isMinimized }
Expand Down
9 changes: 6 additions & 3 deletions packages/help-center/src/components/persistent-router.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import * as React from 'react';
import { Router } from 'react-router-dom';
import { usePersistedHistory } from '../hooks/use-persisted-history';
import type { Location } from 'history';

type Props = {
children: React.ReactNode;
children: React.ReactNode | React.ReactPortal;
routerHistory?: { entries: Location[]; index: number } | undefined;
persistenceKey?: string | undefined;
};

/**
* A router like MemoryRouter, but it persists the history to the server using user preferences.
*/
export const PersistentRouter = ( { children }: Props ) => {
const { history, state } = usePersistedHistory();
export const PersistentRouter = ( { children, routerHistory, persistenceKey }: Props ) => {
const { history, state } = usePersistedHistory( { routerHistory, persistenceKey } );

return (
<Router location={ state.location } navigator={ history } navigationType={ state.action }>
Expand Down
35 changes: 21 additions & 14 deletions packages/help-center/src/hooks/use-persisted-history.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { HelpCenterSelect } from '@automattic/data-stores';
import apiFetch from '@wordpress/api-fetch';
import { useSelect } from '@wordpress/data';
import { Action, Location } from 'history';
import { useState, useEffect, useLayoutEffect } from 'react';
import wpcomRequest, { canAccessWpcomApis } from 'wpcom-proxy-request';
import { HELP_CENTER_STORE } from '../stores';
export interface HistoryEvent {
action: Action;
location: Location;
Expand All @@ -16,11 +13,13 @@ export interface HistoryEvent {
* It persists the history to the server using user preferences.
*/
class MemoryHistory {
private persistenceKey: string;
private entries: Location[] = [];
private index: number = -1;
private listeners: ( ( event: HistoryEvent ) => void )[] = [];

constructor(
persistenceKey: string = 'help_center_router_history',
initialEntries: Location[] = [
{ pathname: '/', search: '', hash: '', key: 'default', state: null },
],
Expand All @@ -35,6 +34,7 @@ class MemoryHistory {
this.goForward = this.goForward.bind( this );
this.listen = this.listen.bind( this );
this.createLocation = this.createLocation.bind( this );
this.persistenceKey = persistenceKey;
}
get length(): number {
return this.entries.length;
Expand Down Expand Up @@ -125,7 +125,7 @@ class MemoryHistory {
method: 'PUT',
body: {
calypso_preferences: {
help_center_router_history: { entries: this.entries, index: this.index },
[ this.persistenceKey ]: { entries: this.entries, index: this.index },
},
},
} ).catch( () => {} );
Expand All @@ -135,23 +135,26 @@ class MemoryHistory {
path: '/help-center/open-state',
method: 'PUT',
data: {
help_center_router_history: { entries: this.entries, index: this.index },
[ this.persistenceKey ]: { entries: this.entries, index: this.index },
},
} as Parameters< typeof apiFetch >[ 0 ] ).catch( () => {} );
}
}
}

export const usePersistedHistory = () => {
const [ history, setHistory ] = useState< MemoryHistory >( new MemoryHistory() );
type Props = {
routerHistory?: { entries: Location[]; index: number } | undefined;
persistenceKey?: string | undefined;
};

export const usePersistedHistory = ( { routerHistory, persistenceKey }: Props ) => {
const [ history, setHistory ] = useState< MemoryHistory >(
() => new MemoryHistory( persistenceKey )
);
const [ state, setState ] = useState< HistoryEvent >( {
action: history.action,
location: history.location,
} );
const persistedHistory = useSelect(
( select ) => ( select( HELP_CENTER_STORE ) as HelpCenterSelect ).getHelpCenterRouterHistory(),
[]
);

useLayoutEffect( () => {
return history.listen( setState );
Expand All @@ -162,16 +165,20 @@ export const usePersistedHistory = () => {
// Skip persisted history if help-center=happiness-engineer to allow escalation to live chat, otherwise the location is overwritten.
const helpCenterParam = urlParams.get( 'help-center' );

if ( persistedHistory && helpCenterParam !== 'happiness-engineer' ) {
const history = new MemoryHistory( persistedHistory.entries, persistedHistory.index );
if ( routerHistory && helpCenterParam !== 'happiness-engineer' ) {
const history = new MemoryHistory(
persistenceKey,
routerHistory.entries,
routerHistory.index
);
setHistory( history );

setState( {
action: history.action,
location: history.location,
} );
}
}, [ persistedHistory ] );
}, [ routerHistory, persistenceKey ] );

return { history, state };
};
1 change: 1 addition & 0 deletions packages/help-center/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export { SuccessScreen } from './components/ticket-success-screen';
export { BackButton } from './components/back-button';
export { HelpCenterContactForm } from './components/help-center-contact-form';
export { default as HelpCenterInlineButton } from './components/help-center-inline-button';
export { PersistentRouter } from './components/persistent-router';
export { default as Mail } from './icons/mail';
export { useHelpSearchQuery } from './hooks/use-help-search-query';
export { useShouldUseUnifiedAgent } from './hooks/use-should-use-unified-agent';
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ __metadata:
"@automattic/agenttic-client": "npm:^0.1.23"
"@automattic/agenttic-ui": "npm:^0.1.23"
"@automattic/calypso-typescript-config": "workspace:^"
"@automattic/help-center": "workspace:^"
"@automattic/oauth-token": "workspace:^"
"@rive-app/react-canvas": "npm:^4.15.6"
"@types/react": "npm:^18.3.1"
Expand Down