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

Implement navigation changes #267

Merged
merged 37 commits into from
Sep 10, 2024
Merged

Implement navigation changes #267

merged 37 commits into from
Sep 10, 2024

Conversation

1egoman
Copy link
Collaborator

@1egoman 1egoman commented Sep 9, 2024

This change implements a new global navbar and sidebar, based on the designs in figma.

This is what the main session page (/srcbooks/:id) looks like now:
Screenshot 2024-09-09 at 4 14 01 PM

When a user clicks on any of the sidebar icons and the screen is thinner, a sheet opens:
Screenshot 2024-09-09 at 4 14 09 PM

When a user clicks on any of the sidebar icons and the screen is wider, a persistently open panel is shown:
Screenshot 2024-09-09 at 4 14 19 PM

Finally, the navbar carries over to the home page in addition to the session page:
Screenshot 2024-09-09 at 4 14 28 PM

… parts of the app

This is a challenge because the different navbars need different data as
part of the route loader (ie, `useLoaderData` return value), so there's
not a good way to abstract this without tying into the router somewhere.
… new srcbook url

Previously, cells and channel data were still being stored from the
initial srcbook, not the one that was newly navigated towards.
… in sheet mode

This is what it looked like in the designs!
Comment on lines 43 to 70
errorElement: <ErrorPage />,
},
{
path: '/secrets',
loader: Secrets.loader,
element: <Secrets />,
errorElement: <ErrorPage />,
},
{
path: '/settings',
element: <Settings />,
loader: Settings.loader,
action: Settings.action,
errorElement: <ErrorPage />,
path: '/',
element: (
<LayoutNavbar>
<Outlet />
</LayoutNavbar>
),
loader: configLoader,
children: [
{
path: '/secrets',
loader: Secrets.loader,
element: <Secrets />,
errorElement: <ErrorPage />,
},
{
path: '/settings',
element: <Settings />,
loader: Settings.loader,
action: Settings.action,
errorElement: <ErrorPage />,
},
],
},
],
},
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've had to make some relatively substantial changes to the root router at the top of the app - this is because I split the navbar up into two different underlying implementations, one that renders on the session page, and one that renders everywhere else, and the two different implementations require different data (which meant I had to use two different loaders depending on the navbar).

There may be a more elegant way to do this, but what I have here seems to work.

Comment on lines +50 to +94
const { config, srcbooks, session } = useLoaderData() as SessionLoaderDataType;

// Because we use refs for our state, we need a way to trigger
// component re-renders when the ref state changes.
//
// https://legacy.reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate
//
const [, forceComponentRerender] = useReducer((x) => x + 1, 0);

const channelRef = useRef(SessionChannel.create(session.id));
const connectedSessionIdRef = useRef<SessionType['id'] | null>(null);
const connectedSessionLanguageRef = useRef<SessionType['language'] | null>(null);
const channel = channelRef.current;

useEffectOnce(() => {
useEffect(() => {
if (connectedSessionIdRef.current === session.id) {
return;
}

const oldChannel = channelRef.current;

// Disconnect from the previously connected session
if (connectedSessionIdRef.current) {
oldChannel.unsubscribe();

if (connectedSessionLanguageRef.current === 'typescript') {
oldChannel.push('tsserver:stop', { sessionId: session.id });
}
}

// Reconnect to the new session
channelRef.current = SessionChannel.create(session.id);
connectedSessionIdRef.current = session.id;
connectedSessionLanguageRef.current = session.language;

const channel = channelRef.current;

channel.subscribe();

if (session.language === 'typescript') {
channel.push('tsserver:start', { sessionId: session.id });
}

return () => {
channel.unsubscribe();

if (session.language === 'typescript') {
channel.push('tsserver:stop', { sessionId: session.id });
}
};
});
forceComponentRerender();
}, [session.id, session.language, forceComponentRerender]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I had to make some signifigant changes to the way that the session page connected to the underlying websocket - the way it previously worked, if a user navigated internally within the app to a new srcbook (ie, by clicking a link, etc), the socket wouldn't disconnect and reconnect, so messages related to a new srcbook would be going to a misconfigured socket, leading to the dev server crashing.

This I think needs a fair amount of testing! As far as I can tell it works locally for me, but I am not sure if I am exercising all the cases.

Comment on lines +30 to +58
export const SESSION_MENU_PANELS = [
{
name: 'tableOfContents' as const,
icon: ListIcon,
openWidthInPx: 312,
contents: () => <SessionMenuPanelTableOfContents />,
},
{
name: 'packages' as const,
icon: PackageIcon,
openWidthInPx: 480,
contents: ({ session, openDepsInstallModal }: SessionMenuPanelContentsProps) => (
<SessionMenuPanelPackages session={session} openDepsInstallModal={openDepsInstallModal} />
),
},
{
name: 'settings' as const,
icon: SettingsIcon,
openWidthInPx: 480,
contents: (props: SessionMenuPanelContentsProps) => <SessionMenuPanelSettings {...props} />,
},
// NOTE: re-enable this in the follow up change!
// {
// name: 'secrets' as const,
// icon: KeySquareIcon,
// openWidthInPx: 480,
// contents: () => <SessionMenuPanelSecrets />,
// },
];
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This data structure controls rendering all the side panels. For now, the secrets panel is disabled.

packages/web/src/components/navbar.tsx Outdated Show resolved Hide resolved
Comment on lines +115 to +144
{props.srcbooks
.sort((a, b) => b.openedAt - a.openedAt)
.slice(0, 6)
.map((srcbook) => {
if (srcbook.id === props.session.id) {
return null;
}
const titleCell = srcbook.cells.find((cell) => cell.type === 'title') as
| TitleCellType
| undefined;
if (!titleCell) {
return null;
}

return (
<DropdownMenuItem
key={srcbook.id}
onClick={() => navigate(`/srcbooks/${srcbook.id}`)}
>
{titleCell.text}
</DropdownMenuItem>
);
})}

{/* FIXME: how should more than 6 entries be rendered? */}
{props.srcbooks.length > 6 ? (
<DropdownMenuItem asChild>
<Link to="/">More...</Link>
</DropdownMenuItem>
) : null}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Right now, only the 6 most recent entries are rendered in the inline srcbook picker dropdown. It's unclear from the designs if this is the intention or not, but given this probably should be length limited in some capacity to keep it from overflowing the screen, I limited it to 6, and if there's more than 6, rendered a More... item that just goes back to the home view.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good to me

packages/web/src/components/navbar.tsx Outdated Show resolved Hide resolved
@1egoman 1egoman marked this pull request as ready for review September 10, 2024 17:45
Copy link
Contributor

@benjreinhart benjreinhart left a comment

Choose a reason for hiding this comment

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

Ooo boy this is looking 🔥 I left some comments but this is mostly ready to go!

One other thing: I don't see the icons at the bottom of the left navigation tab (the keyboard shortcuts and feedback).

Screenshot 2024-09-10 at 12 18 45 PM

packages/web/src/components/navbar.tsx Outdated Show resolved Hide resolved
packages/web/src/components/navbar.tsx Outdated Show resolved Hide resolved
packages/web/src/components/navbar.tsx Show resolved Hide resolved
packages/web/src/components/session-menu/secrets-panel.tsx Outdated Show resolved Hide resolved
packages/web/src/components/session-menu/index.tsx Outdated Show resolved Hide resolved
{/* At the xl breakpoint, the sessionMenu appears inline so we pad left to balance*/}
<div className="grow shrink lg:px-0 pb-28">
<div className="max-w-[800px] mx-auto my-12 px-[32px]">
<TitleCell cell={titleCell} updateCellOnServer={updateCellOnServer} />
Copy link
Contributor

Choose a reason for hiding this comment

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

This may have been there before (though I never noticed it if so) but the title cell appears to break at any character.

Screenshot 2024-09-10 at 12 13 56 PM

Nothing here stands out as having done introduced this but possibly related to the styles applied above?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It looks like this behavior currently happens in main:
Screenshot 2024-09-10 at 3 43 21 PM

That being said, I can take a look if desired - word-wrap: break-word might do what you are looking for here?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok we can do that in separate PR then.

Copy link
Contributor

@benjreinhart benjreinhart left a comment

Choose a reason for hiding this comment

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

We'll need to create a changeset for this as well. Run pnpm changeset and follow the prompts. Let me know if that's confusing and I can help

https://github.com/srcbookdev/srcbook/blob/main/CONTRIBUTING.md#making-a-changeset

Copy link
Contributor

@benjreinhart benjreinhart left a comment

Choose a reason for hiding this comment

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

:shipit:

@1egoman 1egoman merged commit 22e6ca0 into main Sep 10, 2024
3 checks passed
@1egoman 1egoman deleted the navigation-changes branch September 10, 2024 20:29
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.

2 participants