Skip to content

Fix settings page 500 error #219

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

Merged
merged 1 commit into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 49 additions & 23 deletions packages/web/src/app/[domain]/components/configEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,8 @@ import { useThemeNormalized } from "@/hooks/useThemeNormalized";
import { json, jsonLanguage, jsonParseLinter } from "@codemirror/lang-json";
import { linter } from "@codemirror/lint";
import { EditorView, hoverTooltip } from "@codemirror/view";
import CodeMirror, { ReactCodeMirrorRef } from "@uiw/react-codemirror";
import {
handleRefresh,
jsonCompletion,
jsonSchemaHover,
jsonSchemaLinter,
stateExtensions
} from "codemirror-json-schema";
import { useRef, forwardRef, useImperativeHandle, Ref, ReactNode, useState } from "react";
import CodeMirror, { Extension, ReactCodeMirrorRef } from "@uiw/react-codemirror";
import { useRef, forwardRef, useImperativeHandle, Ref, ReactNode, useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Schema } from "ajv";
Expand Down Expand Up @@ -94,7 +87,7 @@ export function onQuickAction<T>(
const cursorPos = next.lastIndexOf(selectionText);
if (cursorPos >= 0) {
view.dispatch({
selection: {
selection: {
anchor: cursorPos,
head: cursorPos + selectionText.length
}
Expand All @@ -120,7 +113,6 @@ const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCo
const editorRef = useRef<ReactCodeMirrorRef>(null);
const [isViewMoreActionsEnabled, setIsViewMoreActionsEnabled] = useState(false);
const [height, setHeight] = useState(224);

useImperativeHandle(
forwardedRef,
() => editorRef.current as ReactCodeMirrorRef
Expand All @@ -129,6 +121,51 @@ const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCo
const keymapExtension = useKeymapExtension(editorRef.current?.view);
const { theme } = useThemeNormalized();

// ⚠️ DISGUSTING HACK AHEAD ⚠️
// Background: When navigating to the /connections/:id?tab=settings page, we were hitting a 500 error with the following
// message server side:
//
// > Internal error: Error: Element type is invalid: expected a string (for built-in components) or a class/function
// > (for composite components) but got: undefined. You likely forgot to export your component from the file it's
// > defined in, or you might have mixed up default and named imports.
//
// Why was this happening? We have no idea, but we isolated it to the extensions exported by the `codemirror-json-schema`
// package. The solution that worked was to dynamically import the package inside of the useEffect and load the extensions
// async.
//
// So, yeah. - Brendan
const [jsonSchemaExtensions, setJsonSchemaExtensions] = useState<Extension[]>([]);
useEffect(() => {
const loadExtensions = async () => {
const {
handleRefresh,
jsonCompletion,
jsonSchemaHover,
jsonSchemaLinter,
stateExtensions
} = await import('codemirror-json-schema');
return [
linter(jsonParseLinter(), {
delay: 300,
}),
linter(jsonSchemaLinter(), {
needsRefresh: handleRefresh,
}),
jsonLanguage.data.of({
autocomplete: jsonCompletion(),
}),
hoverTooltip(jsonSchemaHover()),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stateExtensions(schema as any),
]
}

loadExtensions().then((extensions) => {
console.debug('Loaded json schema extensions');
setJsonSchemaExtensions(extensions);
});
}, [schema]);

return (
<div className="border rounded-md">
<div className="flex flex-row items-center flex-wrap p-1">
Expand Down Expand Up @@ -211,19 +248,8 @@ const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCo
extensions={[
keymapExtension,
json(),
linter(jsonParseLinter(), {
delay: 300,
}),
linter(jsonSchemaLinter(), {
needsRefresh: handleRefresh,
}),
jsonLanguage.data.of({
autocomplete: jsonCompletion(),
}),
hoverTooltip(jsonSchemaHover()),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stateExtensions(schema as any),
customAutocompleteStyle,
...jsonSchemaExtensions,
]}
theme={theme === "dark" ? "dark" : "light"}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default function SharedConnectionCreationForm<T>({
return checkIfSecretExists(secretKey, domain);
}, { message: "Secret not found" }),
});
}, [schema, domain]);
}, [schema, domain, additionalConfigValidation]);

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export const EditorContextMenu = ({
}
}
)
}, [captureEvent, path, repoName, selection.from, selection.to, toast, view, revisionName]);
}, [selection.from, selection.to, domain, repoName, revisionName, path, toast, captureEvent, view]);

return (
<div
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import { getDisplayTime } from "@/lib/utils";
import Image from "next/image";
import { StatusIcon } from "../../components/statusIcon";
Expand Down
8 changes: 2 additions & 6 deletions packages/web/src/app/[domain]/connections/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default async function ConnectionManagementPage({ params, searchParams }:

const currentTab = searchParams.tab || "overview";


return (
<Tabs value={currentTab} className="w-full">
<Header className="mb-6" withTopMargin={false}>
Expand Down Expand Up @@ -85,12 +86,7 @@ export default async function ConnectionManagementPage({ params, searchParams }:
</TabsContent>
<TabsContent
value="settings"
// @note: There was some bugginess with the ConfigEditor ref not being set again
// after the parent component was unmounted and remounted. This workarouns makes it
// s.t., hide the settings tab when it is inactive, instead of unmounting it.
// @see: https://github.com/radix-ui/primitives/issues/2359#issuecomment-2481321719
className="flex flex-col gap-6 data-[state=inactive]:hidden"
forceMount={true}
className="flex flex-col gap-6"
>
<DisplayNameSetting connectionId={connection.id} name={connection.name} />
<ConfigSetting
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import { cn, CodeHostType, getCodeHostIcon } from "@/lib/utils";
import { useMemo } from "react";
import Image from "next/image";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import useCaptureEvent from "@/hooks/useCaptureEvent";
import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam";
import { useSearchHistory } from "@/hooks/useSearchHistory";
import { Repository, SearchQueryParams, SearchResultFile } from "@/lib/types";
import { createPathWithQueryParams, measureSync } from "@/lib/utils";
import { createPathWithQueryParams } from "@/lib/utils";
import { SymbolIcon } from "@radix-ui/react-icons";
import { useQuery } from "@tanstack/react-query";
import { useRouter } from "next/navigation";
Expand Down