Skip to content
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

Fix navigation through DOM views with pages #1565

Merged
merged 15 commits into from
Jan 20, 2023

Conversation

apedroferreira
Copy link
Member

@apedroferreira apedroferreira commented Jan 16, 2023

Closes #1551

  • Loads first page in the URL, even if it's not the first page or a page.
  • Does not get rid of query params in URL on first page load.
  • Always use DOM view state as the source of truth for the current view. Load initial view from URL. Update URL as a side-effect.

Preview: https://toolpad-pr-1565.onrender.com/

@apedroferreira apedroferreira added bug 🐛 Something doesn't work regression A bug, but worse labels Jan 16, 2023
@apedroferreira apedroferreira self-assigned this Jan 16, 2023
@oliviertassinari oliviertassinari requested a deployment to fix-dom-page-views-navigation - toolpad-db PR #1565 January 16, 2023 20:22 — with Render Abandoned
@oliviertassinari oliviertassinari temporarily deployed to fix-dom-page-views-navigation - toolpad PR #1565 January 16, 2023 20:23 — with Render Destroyed
@@ -362,25 +364,49 @@ const UNDOABLE_ACTIONS = new Set<DomActionType>([
'DESELECT_NODE',
]);

export const getCurrentPageDomView = (location: Location): DomView => {
Copy link
Member Author

@apedroferreira apedroferreira Jan 16, 2023

Choose a reason for hiding this comment

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

Should I move this function to some other file? Not sure where to move it to.

@oliviertassinari oliviertassinari temporarily deployed to fix-dom-page-views-navigation - toolpad PR #1565 January 16, 2023 20:25 — with Render Destroyed
@oliviertassinari oliviertassinari temporarily deployed to fix-dom-page-views-navigation - toolpad PR #1565 January 16, 2023 20:35 — with Render Destroyed
@oliviertassinari
Copy link
Member

Do we know what created this regression?

@Janpot
Copy link
Member

Janpot commented Jan 17, 2023

I'm not sure about this, it feels a bit brittle. How should the undoable views interact with the browser back button?

I don't believe it would currently be possible to keep the browser history in sync with the undo stack. I feel like as soon as we're in the editor flow, the undoable view changes shouldn't create new browser history entries. And if that is the case then:

  1. Should we be not using react-router for the undoable views, instead have the route defined by the editor state? (i.e. not use <Route>, but switch routes based on editor state)
  2. Initialize the state using the current url
  3. in an effect synchronize all state changes back to the url

Do we know what created this regression?

@oliviertassinari This was introduced when taking the view switching and selection into account for undo/redo. I think the initial implementation gets into a chicken/egg situation between application state (undo stack) and the browser history. I believe it would be best to make one the single source of truth (application state) and have the other simply follow suit. For that we'll have to break out of react-router for this part.

@apedroferreira
Copy link
Member Author

apedroferreira commented Jan 17, 2023

I tried to make the source of truth be the DOM state, but yeah I did not anticipate this issue coming from the URLs and I tried out this solution but it's not the simplest...
Picking a single source of truth and not letting the other influence anything sounds like a way to do it - so either always use URLs (could be tough) or always using the DOM state without relying on URLs.
I can probably simplify this instead of trying to make it work so universally - will give it a try.

@apedroferreira apedroferreira changed the title Fix navigation through DOM views with pages WIP: Fix navigation through DOM views with pages Jan 17, 2023
@apedroferreira apedroferreira changed the title WIP: Fix navigation through DOM views with pages [WIP] Fix navigation through DOM views with pages Jan 17, 2023
@apedroferreira apedroferreira marked this pull request as draft January 17, 2023 20:42
@oliviertassinari oliviertassinari temporarily deployed to fix-dom-page-views-navigation - toolpad PR #1565 January 18, 2023 17:36 — with Render Destroyed
@oliviertassinari oliviertassinari temporarily deployed to fix-dom-page-views-navigation - toolpad PR #1565 January 18, 2023 18:28 — with Render Destroyed
@apedroferreira apedroferreira marked this pull request as ready for review January 18, 2023 18:59
@apedroferreira apedroferreira changed the title [WIP] Fix navigation through DOM views with pages Fix navigation through DOM views with pages Jan 18, 2023
@apedroferreira
Copy link
Member Author

I'm not sure about this, it feels a bit brittle. How should the undoable views interact with the browser back button?

I don't believe it would currently be possible to keep the browser history in sync with the undo stack. I feel like as soon as we're in the editor flow, the undoable view changes shouldn't create new browser history entries. And if that is the case then:

  1. Should we be not using react-router for the undoable views, instead have the route defined by the editor state? (i.e. not use <Route>, but switch routes based on editor state)
  2. Initialize the state using the current url
  3. in an effect synchronize all state changes back to the url

Do we know what created this regression?

@oliviertassinari This was introduced when taking the view switching and selection into account for undo/redo. I think the initial implementation gets into a chicken/egg situation between application state (undo stack) and the browser history. I believe it would be best to make one the single source of truth (application state) and have the other simply follow suit. For that we'll have to break out of react-router for this part.

@Janpot how about this? I followed the approach you suggested, seems like a good solution!

const navigate = useNavigate();

const firstPage = pages.length > 0 ? pages[0] : null;

React.useEffect(() => {
Copy link
Member

Choose a reason for hiding this comment

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

I believe this could be a bit DRYer if written as:

function getPathnameForView (appId, view) {
  switch (view.kind) {
    case 'page': return `/app/${appId}/pages/${view.nodeId}`;
    // ...
  }
}

React.useEffect(() => {
  const desiredPath = getPathnameForView(currentView)
  if (desiredPath !== location.pathname) {
    navigate({  pathname: desiredPath }, { replace: true });
  }
}, [appId, currentView, location.pathname]

</Route>
</Routes>
<AppEditorShell appId={appId}>
{currentView.kind === 'page' ? (
Copy link
Member

Choose a reason for hiding this comment

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

The intent of this isn't very clear from structuring the code this way. (The intent being "only one of these needs to be rendered"). Moreover, the last element needs to always be kept in sync with the lines above. Ideally this would be written as a switch. Infortunately there's still no way to write switch expressions or do pattern matching in javascript, but perhaps this could be written as:

{(() => {
  switch (currentView.kind) {
    case 'page': return <PageEditor appId={appId} nodeId={currentView.nodeId} />;
    // ...
  }
})()}

It's a bit ugly with all those brackets. Alternatively this could be written in a useMemo, or extracted as a function.

@@ -362,25 +366,54 @@ const UNDOABLE_ACTIONS = new Set<DomActionType>([
'DESELECT_NODE',
]);

export const getInitialPageDomView = (location: Location, defaultPageNodeId?: NodeId): DomView => {
Copy link
Member

Choose a reason for hiding this comment

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

This looks like the inverse of the getPathnameFromView. Perhaps we could put the two functions physically together and name them similarly?

getPathnameFromView and getViewFromPathname?

@Janpot
Copy link
Member

Janpot commented Jan 20, 2023

how about this? I followed the approach you suggested, seems like a good solution!

I like this implementation a lot, hopefully it works well in practice. Just a few stylistic comments

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Jan 20, 2023
@oliviertassinari oliviertassinari temporarily deployed to fix-dom-page-views-navigation - toolpad PR #1565 January 20, 2023 16:20 — with Render Destroyed
@apedroferreira
Copy link
Member Author

I like this implementation a lot, hopefully it works well in practice. Just a few stylistic comments

Great suggestions again, there was definitely some unneeded repetition.
I've added the suggestions now.

case 'codeComponent':
return `/app/${appId}/codeComponents/${view.nodeId}`;
default:
return null;
Copy link
Member

@Janpot Janpot Jan 20, 2023

Choose a reason for hiding this comment

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

Should there be any view for which no pathame exists? Perhaps this should throw?

Suggested change
return null;
throw new Error(`Unknown view "${(view as DomView).kind}".`)

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good! I've added it in a new commit to include small type adjustments since the result can't be null

@oliviertassinari oliviertassinari temporarily deployed to fix-dom-page-views-navigation - toolpad PR #1565 January 20, 2023 17:22 — with Render Destroyed
Copy link
Member

@Janpot Janpot left a comment

Choose a reason for hiding this comment

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

👌 Very nice!

@apedroferreira apedroferreira merged commit 5831ec5 into master Jan 20, 2023
@apedroferreira apedroferreira deleted the fix-dom-page-views-navigation branch January 20, 2023 17:38
@oliviertassinari
Copy link
Member

Great, the DX is much better with this fix. Also the e2e test was much needed, great to see it 👌.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work regression A bug, but worse
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Reload of a page editor always switches to the first page
3 participants