Skip to content

Commit e3a4c1b

Browse files
committed
Improve route and tab handling on resource deletion
1 parent ecfc58a commit e3a4c1b

6 files changed

Lines changed: 106 additions & 102 deletions

File tree

packages/client/src/collection.tsx

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import { TreeItem, TreeItemLink, TreeItemProps } from '@the-dev-tools/ui/tree';
5454
import { saveFile, useEscapePortal } from '@the-dev-tools/ui/utils';
5555
import { useConnectMutation } from '~/api/connect-query';
5656
import { useMutate, useQuery } from '~data-client';
57+
import { useOnEndpointDelete } from '~endpoint';
5758

5859
const workspaceRoute = getRouteApi('/_authorized/workspace/$workspaceIdCan');
5960

@@ -401,8 +402,9 @@ const EndpointTree = ({ collectionId, endpoint, example, id: endpointIdCan, pare
401402
const matchRoute = useMatchRoute();
402403
const navigate = useNavigate();
403404

405+
const onEndpointDelete = useOnEndpointDelete();
406+
404407
const { workspaceId } = workspaceRoute.useLoaderData();
405-
const { workspaceIdCan } = workspaceRoute.useParams();
406408

407409
const { containerRef, navigate: toNavigate = false, showControls } = useContext(CollectionListTreeContext);
408410

@@ -508,15 +510,8 @@ const EndpointTree = ({ collectionId, endpoint, example, id: endpointIdCan, pare
508510

509511
<MenuItem
510512
onAction={async () => {
513+
await onEndpointDelete({ endpointId, exampleId });
511514
await dataClient.fetch(EndpointDeleteEndpoint, { endpointId });
512-
if (
513-
!matchRoute({
514-
params: { endpointIdCan },
515-
to: '/workspace/$workspaceIdCan/endpoint/$endpointIdCan/example/$exampleIdCan',
516-
})
517-
)
518-
return;
519-
await navigate({ params: { workspaceIdCan }, to: '/workspace/$workspaceIdCan' });
520515
}}
521516
variant='danger'
522517
>
@@ -568,10 +563,10 @@ const ExampleItem = ({ collectionId, endpointId, example, id: exampleIdCan }: Ex
568563
const lastResponseIdCan = lastResponseId && Ulid.construct(lastResponseId).toCanonical();
569564

570565
const matchRoute = useMatchRoute();
571-
const navigate = useNavigate();
566+
567+
const onEndpointDelete = useOnEndpointDelete();
572568

573569
const { workspaceId } = workspaceRoute.useLoaderData();
574-
const { workspaceIdCan } = workspaceRoute.useParams();
575570

576571
const { containerRef, navigate: toNavigate = false, showControls } = useContext(CollectionListTreeContext);
577572

@@ -637,15 +632,8 @@ const ExampleItem = ({ collectionId, endpointId, example, id: exampleIdCan }: Ex
637632

638633
<MenuItem
639634
onAction={async () => {
635+
await onEndpointDelete({ endpointId, exampleId });
640636
await dataClient.fetch(ExampleDeleteEndpoint, { exampleId });
641-
if (
642-
!matchRoute({
643-
params: { exampleIdCan },
644-
to: '/workspace/$workspaceIdCan/endpoint/$endpointIdCan/example/$exampleIdCan',
645-
})
646-
)
647-
return;
648-
await navigate({ params: { workspaceIdCan }, to: '/workspace/$workspaceIdCan' });
649637
}}
650638
variant='danger'
651639
>

packages/client/src/endpoint.tsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { QueryErrorResetBoundary, useQuery as useReactQuery } from '@tanstack/react-query';
2-
import { createFileRoute, getRouteApi, useMatchRoute, useNavigate, useRouteContext } from '@tanstack/react-router';
2+
import { createFileRoute, getRouteApi, useNavigate, useRouteContext } from '@tanstack/react-router';
33
import { createColumnHelper } from '@tanstack/react-table';
44
import CodeMirror from '@uiw/react-codemirror';
55
import { Array, Duration, Match, MutableHashMap, Option, pipe, Schema, String, Struct } from 'effect';
@@ -51,7 +51,7 @@ import { Menu, MenuItem, useContextMenuState } from '@the-dev-tools/ui/menu';
5151
import { MethodBadge } from '@the-dev-tools/ui/method-badge';
5252
import { Modal } from '@the-dev-tools/ui/modal';
5353
import { PanelResizeHandle } from '@the-dev-tools/ui/resizable-panel';
54-
import { addTab } from '@the-dev-tools/ui/router';
54+
import { addTab, useRemoveTab } from '@the-dev-tools/ui/router';
5555
import { Select } from '@the-dev-tools/ui/select';
5656
import { Separator } from '@the-dev-tools/ui/separator';
5757
import { tw } from '@the-dev-tools/ui/tailwind-literal';
@@ -104,14 +104,28 @@ export const Route = makeRoute({
104104
const { endpointId, exampleId } = match.loaderData;
105105

106106
addTab({
107-
id: JSON.stringify({ endpointId, exampleId, route: Route.id }),
107+
id: endpointTabId({ endpointId, exampleId }),
108108
match,
109109
node: <EndpointTab endpointId={endpointId} />,
110110
});
111111
},
112112
shouldReload: false,
113113
});
114114

115+
interface EndpointTabIdProps {
116+
endpointId: Uint8Array;
117+
exampleId: Uint8Array;
118+
}
119+
120+
export const endpointTabId = ({ endpointId, exampleId }: EndpointTabIdProps) =>
121+
JSON.stringify({ endpointId, exampleId, route: Route.id });
122+
123+
export const useOnEndpointDelete = () => {
124+
const context = workspaceRoute.useRouteContext();
125+
const removeTab = useRemoveTab();
126+
return (props: EndpointTabIdProps) => removeTab({ ...context, id: endpointTabId(props) });
127+
};
128+
115129
function Page() {
116130
const { endpointId, exampleId } = Route.useLoaderData();
117131
const { workspaceId } = workspaceRoute.useLoaderData();
@@ -476,9 +490,10 @@ interface EndpointHeaderProps {
476490
export const EndpointHeader = ({ endpointId, exampleId }: EndpointHeaderProps) => {
477491
const { dataClient } = useRouteContext({ from: '__root__' });
478492

479-
const matchRoute = useMatchRoute();
480493
const navigate = useNavigate();
481494

495+
const onEndpointDelete = useOnEndpointDelete();
496+
482497
const example = useQuery(ExampleGetEndpoint, { exampleId });
483498

484499
const [exampleUpdate, exampleUpdateLoading] = useMutate(ExampleUpdateEndpoint);
@@ -578,15 +593,8 @@ export const EndpointHeader = ({ endpointId, exampleId }: EndpointHeaderProps) =
578593

579594
<MenuItem
580595
onAction={async () => {
596+
await onEndpointDelete({ endpointId, exampleId });
581597
await dataClient.fetch(ExampleDeleteEndpoint, { exampleId });
582-
if (
583-
!matchRoute({
584-
params: { endpointIdCan: Ulid.construct(endpointId).toCanonical() },
585-
to: '/workspace/$workspaceIdCan/endpoint/$endpointIdCan/example/$exampleIdCan',
586-
})
587-
)
588-
return;
589-
await navigate({ from: Route.fullPath, to: '/workspace/$workspaceIdCan' });
590598
}}
591599
variant='danger'
592600
>

packages/client/src/flow/flow.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import { ReferenceContext } from '../reference';
6565
import { useFlowCopyPaste } from './copy-paste';
6666
import { ConnectionLine, Edge, edgeTypes, useMakeEdge } from './edge';
6767
import { FlowContext, flowRoute, HandleKind, HandleKindSchema, workspaceRoute } from './internal';
68+
import { useOnFlowDelete } from './layout';
6869
import { Node, useMakeNode } from './node';
6970
import { ConditionNode, ConditionPanel } from './nodes/condition';
7071
import { ForNode, ForPanel } from './nodes/for';
@@ -280,7 +281,8 @@ export const TopBar = () => {
280281
const { zoom } = useViewport();
281282

282283
const matchRoute = useMatchRoute();
283-
const navigate = useNavigate();
284+
285+
const onFlowDelete = useOnFlowDelete();
284286

285287
const [flowUpdate, flowUpdateLoading] = useMutate(FlowUpdateEndpoint);
286288

@@ -358,15 +360,8 @@ export const TopBar = () => {
358360

359361
<MenuItem
360362
onAction={async () => {
363+
await onFlowDelete(flowId);
361364
await dataClient.fetch(FlowDeleteEndpoint, { flowId });
362-
if (
363-
!matchRoute({
364-
params: { flowIdCan: Ulid.construct(flowId).toCanonical() },
365-
to: '/workspace/$workspaceIdCan/flow/$flowIdCan',
366-
})
367-
)
368-
return;
369-
await navigate({ from: Route.fullPath, to: '/workspace/$workspaceIdCan' });
370365
}}
371366
variant='danger'
372367
>

packages/client/src/flow/layout.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { QueryErrorResetBoundary } from '@tanstack/react-query';
2-
import { createFileRoute, Outlet } from '@tanstack/react-router';
2+
import { createFileRoute, getRouteApi, Outlet } from '@tanstack/react-router';
33
import { Option, pipe, Schema, Struct } from 'effect';
44
import { Ulid } from 'id128';
55
import { FlowGetEndpoint } from '@the-dev-tools/spec/meta/flow/v1/flow.endpoints.js';
66
import { FlowsIcon } from '@the-dev-tools/ui/icons';
7-
import { addTab } from '@the-dev-tools/ui/router';
7+
import { addTab, useRemoveTab } from '@the-dev-tools/ui/router';
88
import { tw } from '@the-dev-tools/ui/tailwind-literal';
99
import { useQuery } from '~data-client';
1010
import { ErrorComponent } from '../error';
@@ -13,6 +13,8 @@ export class FlowSearch extends Schema.Class<FlowSearch>('FlowSearch')({
1313
node: pipe(Schema.String, Schema.optional),
1414
}) {}
1515

16+
const workspaceRoute = getRouteApi('/_authorized/workspace/$workspaceIdCan');
17+
1618
const makeRoute = createFileRoute('/_authorized/workspace/$workspaceIdCan/flow/$flowIdCan');
1719

1820
export const Route = makeRoute({
@@ -38,13 +40,21 @@ export const Route = makeRoute({
3840
const { flowId } = match.loaderData;
3941

4042
addTab({
41-
id: JSON.stringify({ flowId, route: Route.id }),
43+
id: flowTabId(flowId),
4244
match,
4345
node: <FlowTab flowId={flowId} />,
4446
});
4547
},
4648
});
4749

50+
export const flowTabId = (flowId: Uint8Array) => JSON.stringify({ flowId, route: Route.id });
51+
52+
export const useOnFlowDelete = () => {
53+
const context = workspaceRoute.useRouteContext();
54+
const removeTab = useRemoveTab();
55+
return (flowId: Uint8Array) => removeTab({ ...context, id: flowTabId(flowId) });
56+
};
57+
4858
interface FlowTabProps {
4959
flowId: Uint8Array;
5060
}

packages/client/src/workspace/layout.tsx

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
import {
2-
createFileRoute,
3-
Outlet,
4-
ToOptions,
5-
useMatchRoute,
6-
useNavigate,
7-
useRouteContext,
8-
} from '@tanstack/react-router';
1+
import { createFileRoute, Outlet, ToOptions, useNavigate, useRouteContext } from '@tanstack/react-router';
92
import { pipe, Schema } from 'effect';
103
import { Ulid } from 'id128';
114
import { RefObject, useRef } from 'react';
@@ -29,12 +22,13 @@ import { CollectionIcon, FlowsIcon, OverviewIcon } from '@the-dev-tools/ui/icons
2922
import { ListBoxItemLink } from '@the-dev-tools/ui/list-box';
3023
import { Menu, MenuItem, useContextMenuState } from '@the-dev-tools/ui/menu';
3124
import { PanelResizeHandle } from '@the-dev-tools/ui/resizable-panel';
32-
import { makeTabsRx, RouteTabList, TabsRx, useTabShortcuts } from '@the-dev-tools/ui/router';
25+
import { makeTabsRx, RouteTabList, TabsRouteContext } from '@the-dev-tools/ui/router';
3326
import { tw } from '@the-dev-tools/ui/tailwind-literal';
3427
import { TextField, useEditableTextState } from '@the-dev-tools/ui/text-field';
3528
import { saveFile, useEscapePortal } from '@the-dev-tools/ui/utils';
3629
import { useConnectMutation } from '~/api/connect-query';
3730
import { useMutate, useQuery } from '~data-client';
31+
import { useOnFlowDelete } from '~flow/layout';
3832
import { DashboardLayout } from '../authorized';
3933
import { CollectionListTree } from '../collection';
4034
import { EnvironmentsWidget } from '../environment';
@@ -48,7 +42,10 @@ const makeRoute = createFileRoute('/_authorized/workspace/$workspaceIdCan');
4842

4943
export const Route = makeRoute({
5044
validateSearch: (_) => Schema.decodeSync(WorkspaceRouteSearch)(_),
51-
context: (): { tabsRx: TabsRx } => ({ tabsRx: makeTabsRx() }),
45+
context: ({ params: { workspaceIdCan } }): Omit<TabsRouteContext, 'runtime'> => ({
46+
baseRoute: { from: '/', params: { workspaceIdCan }, to: '/workspace/$workspaceIdCan' },
47+
tabsRx: makeTabsRx(),
48+
}),
5249
loader: ({ params: { workspaceIdCan } }) => {
5350
const workspaceId = Ulid.fromCanonical(workspaceIdCan).bytes;
5451
return { workspaceId };
@@ -61,14 +58,12 @@ function Layout() {
6158

6259
const { workspaceId } = Route.useLoaderData();
6360
const { workspaceIdCan } = Route.useParams();
64-
const { tabsRx } = Route.useRouteContext();
61+
const context = Route.useRouteContext();
6562

6663
const workspace = useQuery(WorkspaceGetEndpoint, { workspaceId });
6764

6865
const baseRoute: ToOptions = { from: '/', params: { workspaceIdCan }, to: '/workspace/$workspaceIdCan' };
6966

70-
useTabShortcuts({ baseRoute, runtime, tabsRx });
71-
7267
return (
7368
<DashboardLayout
7469
navbar={
@@ -162,7 +157,7 @@ function Layout() {
162157
<Panel>
163158
<PanelGroup direction='vertical'>
164159
<div className={tw`-mt-px pt-2`}>
165-
<RouteTabList baseRoute={baseRoute} runtime={runtime} tabsRx={tabsRx} />
160+
<RouteTabList baseRoute={baseRoute} runtime={runtime} tabsRx={context.tabsRx} />
166161
</div>
167162
<Panel>
168163
<Outlet />
@@ -239,8 +234,7 @@ const FlowItem = ({ flow: { flowId, name }, id: flowIdCan, listRef }: FlowItemPr
239234
const { workspaceIdCan } = Route.useParams();
240235
const { workspaceId } = Route.useLoaderData();
241236

242-
const matchRoute = useMatchRoute();
243-
const navigate = useNavigate();
237+
const onFlowDelete = useOnFlowDelete();
244238

245239
const [flowUpdate, flowUpdateLoading] = useMutate(FlowUpdateEndpoint);
246240

@@ -301,9 +295,8 @@ const FlowItem = ({ flow: { flowId, name }, id: flowIdCan, listRef }: FlowItemPr
301295

302296
<MenuItem
303297
onAction={async () => {
298+
await onFlowDelete(flowId);
304299
await dataClient.fetch(FlowDeleteEndpoint, { flowId });
305-
if (!matchRoute({ params: { flowIdCan }, to: '/workspace/$workspaceIdCan/flow/$flowIdCan' })) return;
306-
await navigate({ from: Route.fullPath, to: '/workspace/$workspaceIdCan' });
307300
}}
308301
variant='danger'
309302
>

0 commit comments

Comments
 (0)