Skip to content

Commit

Permalink
fix: cache the url to keep the page live (microsoft#2969)
Browse files Browse the repository at this point in the history
* add sessionStoreage

* check the location when jump to begin dialog

* use props state to replace the store state

* fix lint

Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
Co-authored-by: Andy Brown <asbrown002@gmail.com>
  • Loading branch information
3 people authored May 11, 2020
1 parent f02918d commit e1627c0
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 110 deletions.
106 changes: 4 additions & 102 deletions Composer/packages/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ErrorBoundary } from './components/ErrorBoundary';
import { RequireAuth } from './components/RequireAuth';
import onboardingState from './utils/onboardingStorage';
import { isElectron } from './utils/electronUtil';
import { useLinks } from './utils/hooks';

initializeIcons(undefined, { disableWarnings: true });

Expand All @@ -29,111 +30,13 @@ const AppUpdater = React.lazy(() => import('./components/AppUpdater').then(modul
// eslint-disable-next-line react/display-name
const Content = forwardRef<HTMLDivElement>((props, ref) => <div css={content} {...props} ref={ref} />);

const topLinks = (projectId: string, openedDialogId: string) => {
const botLoaded = !!projectId;
let links = [
{
to: '/home',
iconName: 'Home',
labelName: formatMessage('Home'),
exact: true,
disabled: false,
},
{
to: `/bot/${projectId}/dialogs/${openedDialogId}`,
iconName: 'SplitObject',
labelName: formatMessage('Design Flow'),
exact: false,
disabled: !botLoaded,
},
// {
// to: '/test-conversation',
// iconName: 'WaitListConfirm',
// labelName: formatMessage('Test Conversation'),
// exact: false,
// disabled: true, // will delete
// },
{
to: `/bot/${projectId}/language-generation`,
iconName: 'Robot',
labelName: formatMessage('Bot Responses'),
exact: false,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/language-understanding`,
iconName: 'People',
labelName: formatMessage('User Input'),
exact: false,
disabled: !botLoaded,
},
// {
// to: '/evaluate-performance',
// iconName: 'Chart',
// labelName: formatMessage('Evaluate performance'),
// exact: false,
// disabled: true,
// },
{
to: `/bot/${projectId}/notifications`,
iconName: 'Warning',
labelName: formatMessage('Notifications'),
exact: true,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/publish`,
iconName: 'CloudUpload',
labelName: formatMessage('Publish'),
exact: true,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/skills`,
iconName: 'PlugDisconnected',
labelName: formatMessage('Skills'),
exact: true,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/settings/`,
iconName: 'Settings',
labelName: formatMessage('Settings'),
exact: false,
disabled: !botLoaded,
},
];

if (process.env.COMPOSER_AUTH_PROVIDER === 'abs-h') {
links = links.filter(link => link.to !== '/home');
}

return links;
};

const bottomLinks = [
// {
// to: '/help',
// iconName: 'unknown',
// labelName: formatMessage('Info'),
// exact: true,
// disabled: true,
// },
{
to: '/about',
iconName: 'info',
labelName: formatMessage('About'),
exact: true,
disabled: false,
},
];

export const App: React.FC = () => {
const { actions, state } = useContext(StoreContext);
const [sideBarExpand, setSideBarExpand] = useState(false);

const { onboardingSetComplete } = actions;
const { botName, projectId, dialogs, locale, designPageLocation, announcement } = state;
const { botName, locale, announcement } = state;
const { topLinks, bottomLinks } = useLinks();

useEffect(() => {
onboardingSetComplete(onboardingState.getComplete());
Expand All @@ -143,7 +46,6 @@ export const App: React.FC = () => {

const renderAppUpdater = isElectron();

const openedDialogId = designPageLocation.dialogId || dialogs.find(({ isRoot }) => isRoot === true)?.id || 'Main';
return (
<Fragment>
<div
Expand Down Expand Up @@ -176,7 +78,7 @@ export const App: React.FC = () => {
/>
<div css={dividerTop} />{' '}
<FocusZone allowFocusRoot={true}>
{topLinks(projectId, openedDialogId).map((link, index) => {
{topLinks.map((link, index) => {
return (
<NavItem
key={'NavLeftBar' + index}
Expand Down
7 changes: 5 additions & 2 deletions Composer/packages/client/src/components/NavItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Link } from '@reach/router';
import { Icon } from 'office-ui-fabric-react/lib/Icon';

import { StoreContext } from '../../store';
import { useLocation } from '../../utils/hooks';
import { useLocation, useRouterCache } from '../../utils/hooks';

import { link, icon } from './styles';

Expand Down Expand Up @@ -36,6 +36,9 @@ export const NavItem: React.FC<INavItemProps> = props => {
const {
location: { pathname },
} = useLocation();

const linkTo = useRouterCache(to);

const active = pathname.startsWith(to);

const addRef = useCallback(ref => onboardingAddCoachMarkRef({ [`nav${labelName.replace(' ', '')}`]: ref }), []);
Expand All @@ -61,7 +64,7 @@ export const NavItem: React.FC<INavItemProps> = props => {
return (
<Link
data-testid={'LeftNav-CommandBarButton' + labelName}
to={to}
to={linkTo}
aria-disabled={disabled}
aria-label={labelName + (active ? '; selected' : '')}
ref={addRef}
Expand Down
11 changes: 6 additions & 5 deletions Composer/packages/client/src/pages/design/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const getTabFromFragment = () => {
const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: string }>> = props => {
const { state, actions } = useContext(StoreContext);
const visualPanelRef: React.RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);
const { dialogs, designPageLocation, breadcrumb, visualEditorSelection, projectId, schemas, focusPath } = state;
const { dialogs, breadcrumb, visualEditorSelection, projectId, schemas, focusPath } = state;
const {
removeDialog,
setDesignPageLocation,
Expand All @@ -96,8 +96,9 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
clearUndoHistory,
onboardingAddCoachMarkRef,
} = actions;
const { location } = props;
const { dialogId, selected } = designPageLocation;
const { location, dialogId } = props;
const params = new URLSearchParams(location?.search);
const selected = params.get('selected') || '';
const [triggerModalVisible, setTriggerModalVisibility] = useState(false);
const [dialogJsonVisible, setDialogJsonVisibility] = useState(false);
const [currentDialog, setCurrentDialog] = useState<DialogInfo>(dialogs[0]);
Expand All @@ -120,10 +121,10 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st

useEffect(() => {
const index = currentDialog.triggers.findIndex(({ type }) => type === SDKKinds.OnBeginDialog);
if (index >= 0) {
if (index >= 0 && !location?.search) {
selectTo(createSelectedPath(index));
}
}, [currentDialog?.id]);
}, [currentDialog?.id, location]);

useEffect(() => {
if (location && props.dialogId && props.projectId) {
Expand Down
37 changes: 36 additions & 1 deletion Composer/packages/client/src/utils/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { useState, useEffect } from 'react';
import { useState, useEffect, useContext, useRef } from 'react';
import { globalHistory } from '@reach/router';
import replace from 'lodash/replace';
import find from 'lodash/find';

import { bottomLinks, topLinks } from './pageLinks';
import { StoreContext } from './../store';
import routerCache from './routerCache';

export const useLocation = () => {
const { location, navigate } = globalHistory;
Expand All @@ -12,3 +18,32 @@ export const useLocation = () => {

return state;
};

export const useLinks = () => {
const { state } = useContext(StoreContext);
const { projectId, dialogs, designPageLocation } = state;
const openedDialogId = designPageLocation.dialogId || dialogs.find(({ isRoot }) => isRoot === true)?.id || 'Main';

return { topLinks: topLinks(projectId, openedDialogId), bottomLinks };
};

export const useRouterCache = (to: string) => {
const [state, setState] = useState(routerCache.getAll());
const { topLinks, bottomLinks } = useLinks();
const linksRef = useRef(topLinks.concat(bottomLinks));
linksRef.current = topLinks.concat(bottomLinks);
useEffect(() => {
globalHistory.listen(({ location }) => {
const links = linksRef.current;
const { href, origin } = location;
const uri = replace(href, origin, '');
const target = find(links, link => uri.startsWith(link.to));
if (target) {
routerCache.set(target.to, uri);
setState(routerCache.getAll());
}
});
}, []);

return state[to] || to;
};
81 changes: 81 additions & 0 deletions Composer/packages/client/src/utils/pageLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import formatMessage from 'format-message';

export const topLinks = (projectId: string, openedDialogId: string) => {
const botLoaded = !!projectId;
let links = [
{
to: '/home',
iconName: 'Home',
labelName: formatMessage('Home'),
exact: true,
disabled: false,
},
{
to: `/bot/${projectId}/dialogs/${openedDialogId}`,
iconName: 'SplitObject',
labelName: formatMessage('Design Flow'),
exact: false,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/language-generation`,
iconName: 'Robot',
labelName: formatMessage('Bot Responses'),
exact: false,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/language-understanding`,
iconName: 'People',
labelName: formatMessage('User Input'),
exact: false,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/notifications`,
iconName: 'Warning',
labelName: formatMessage('Notifications'),
exact: true,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/publish`,
iconName: 'CloudUpload',
labelName: formatMessage('Publish'),
exact: true,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/skills`,
iconName: 'PlugDisconnected',
labelName: formatMessage('Skills'),
exact: true,
disabled: !botLoaded,
},
{
to: `/bot/${projectId}/settings/`,
iconName: 'Settings',
labelName: formatMessage('Settings'),
exact: false,
disabled: !botLoaded,
},
];

if (process.env.COMPOSER_AUTH_PROVIDER === 'abs-h') {
links = links.filter(link => link.to !== '/home');
}

return links;
};

export const bottomLinks = [
{
to: '/about',
iconName: 'info',
labelName: formatMessage('About'),
exact: true,
disabled: false,
},
];
30 changes: 30 additions & 0 deletions Composer/packages/client/src/utils/routerCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { ClientStorage } from './storage';

const KEY = 'RouterCache';

class RouterCache {
private storage: ClientStorage;
private _all;

constructor() {
this.storage = new ClientStorage(window.sessionStorage);
this._all = this.storage.get(KEY, {});
}

get(to: string) {
return this._all[to] || {};
}

getAll() {
return this._all;
}

set(linkTo: string, uri: string) {
this._all[linkTo] = uri;
this.storage.set(KEY, this._all);
}
}

export default new RouterCache();

0 comments on commit e1627c0

Please sign in to comment.