Fix undo/redo and SO.resolve redirect issues (#116773)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Corey Robertson 2021-11-01 11:22:29 -04:00 committed by GitHub
parent f6f7c2d1de
commit 25426708a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 16 deletions

View file

@ -6,11 +6,13 @@
*/
import React, { FC, useRef, useEffect } from 'react';
import { Observable } from 'rxjs';
import PropTypes from 'prop-types';
import { History } from 'history';
// @ts-expect-error
import createHashStateHistory from 'history-extra/dist/createHashStateHistory';
import { ScopedHistory } from 'kibana/public';
import { skipWhile, timeout, take } from 'rxjs/operators';
import { useNavLinkService } from '../../services';
// @ts-expect-error
import { shortcutManager } from '../../lib/shortcut_manager';
@ -40,14 +42,50 @@ export const App: FC<{ history: ScopedHistory }> = ({ history }) => {
});
});
// We are using our own history due to needing pushState functionality not yet available on standard history
// This effect will listen for changes on the scoped history and push that to our history
// This is needed for SavedObject.resolve redirects
useEffect(() => {
return history.listen((location) => {
historyRef.current.replace(location.hash.substr(1));
return history.listen(({ pathname, hash }) => {
// The scoped history could have something that triggers a url change, and that change is not seen by
// our hash router. For example, a scopedHistory.replace() as done as part of the saved object resolve
// alias match flow will do the replace on the scopedHistory, and our app doesn't react appropriately
// So, to work around this, whenever we see a url on the scoped history, we're going to wait a beat and see
// if it shows up in our hash router. If it doesn't, then we're going to force it onto our hash router
// I don't like this at all, and to overcome this we should switch away from hash router sooner rather than later
// and just use scopedHistory as our history object
const expectedPath = hash.substr(1);
const action = history.action;
// Observable of all the path
const hashPaths$ = new Observable<string>((subscriber) => {
subscriber.next(historyRef.current.location.pathname);
const unsubscribeHashListener = historyRef.current.listen(({ pathname: newPath }) => {
subscriber.next(newPath);
});
return unsubscribeHashListener;
});
const subscription = hashPaths$
.pipe(
skipWhile((value) => value !== expectedPath),
timeout(100),
take(1)
)
.subscribe({
error: (e) => {
if (action === 'REPLACE') {
historyRef.current.replace(expectedPath);
} else {
historyRef.current.push(expectedPath);
}
},
});
window.setTimeout(() => subscription.unsubscribe(), 150);
});
}, [history]);
}, [history, historyRef]);
return (
<ShortcutManagerContextWrapper>

View file

@ -35,7 +35,7 @@ export const useWorkpad = (
const [error, setError] = useState<string | Error | undefined>(undefined);
const [resolveInfo, setResolveInfo] = useState<
{ aliasId: string | undefined; outcome: string } | undefined
{ id: string; aliasId: string | undefined; outcome: string } | undefined
>(undefined);
useEffect(() => {
@ -47,15 +47,16 @@ export const useWorkpad = (
workpad: { assets, ...workpad },
} = await workpadResolve(workpadId);
setResolveInfo({ aliasId, outcome });
setResolveInfo({ aliasId, outcome, id: workpadId });
if (outcome === 'conflict') {
// If it's an alias match, we know we are going to redirect so don't even dispatch that we got the workpad
if (outcome !== 'aliasMatch') {
workpad.aliasId = aliasId;
}
dispatch(setAssets(assets));
dispatch(setWorkpad(workpad, { loadPages }));
dispatch(setZoomScale(1));
dispatch(setAssets(assets));
dispatch(setWorkpad(workpad, { loadPages }));
dispatch(setZoomScale(1));
}
} catch (e) {
setError(e as Error | string);
}
@ -63,15 +64,21 @@ export const useWorkpad = (
}, [workpadId, dispatch, setError, loadPages, workpadResolve]);
useEffect(() => {
(() => {
// If the resolved info is not for the current workpad id, bail out
if (resolveInfo && resolveInfo.id !== workpadId) {
return;
}
(async () => {
if (!resolveInfo) return;
const { aliasId, outcome } = resolveInfo;
if (outcome === 'aliasMatch' && platformService.redirectLegacyUrl && aliasId) {
platformService.redirectLegacyUrl(`#${getRedirectPath(aliasId)}`, getWorkpadLabel());
const redirectPath = getRedirectPath(aliasId);
await platformService.redirectLegacyUrl(`#${redirectPath}`, getWorkpadLabel());
}
})();
}, [resolveInfo, getRedirectPath, platformService]);
}, [workpadId, resolveInfo, getRedirectPath, platformService]);
return [storedWorkpad.id === workpadId ? storedWorkpad : undefined, error];
};