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

Feat/markdown editor #126

Draft
wants to merge 45 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
22167f9
🙈 use experimental-https for dev server
Fredkiss3 Jan 7, 2024
881f256
🔧 only include workspace symbols in search
Fredkiss3 Jan 7, 2024
11caa79
🚸 prevent double submission with submit-button
Fredkiss3 Jan 7, 2024
f614154
✨ interpret new lines as `<br>` in markdown
Fredkiss3 Jan 7, 2024
12107b7
💄 some ui fixes
Fredkiss3 Jan 7, 2024
d5c7d60
🚧 wip
Fredkiss3 Jan 7, 2024
7d59ec7
🚧 some changes
Fredkiss3 Jan 7, 2024
43f1821
👷 only deploy :dev image for PRs
Fredkiss3 Jan 7, 2024
49b128d
🚧 editor work in progress
Fredkiss3 Jan 7, 2024
637213c
👷 don't upload build SHA github artifacts
Fredkiss3 Jan 7, 2024
a9a14ae
✨ undo/redo + tab features
Fredkiss3 Jan 7, 2024
0316354
✨ add quote/unquote toolbar action + fix preloading markdown
Fredkiss3 Jan 7, 2024
81c14b3
🐛 disable toolbar buttons when not visible
Fredkiss3 Jan 7, 2024
cda6718
Merge branch 'main' into feat/markdown-editor
Fredkiss3 Jan 7, 2024
7764e38
✨ add link done
Fredkiss3 Jan 8, 2024
660188d
🎨 always add trailing commas
Fredkiss3 Jan 8, 2024
62e63aa
✨ strike through feature
Fredkiss3 Jan 8, 2024
acd258e
✨ handle pasting link into text area
Fredkiss3 Jan 8, 2024
4f9eaf0
⏪ don't always add trailing comma
Fredkiss3 Jan 8, 2024
867110e
🚧 WIP : add lists
Fredkiss3 Jan 8, 2024
9907640
⬆️ upgrade next to canary
Fredkiss3 Jan 10, 2024
39ba187
Merge branch 'main' into feat/markdown-editor
Fredkiss3 Jan 12, 2024
03c3797
Merge branch 'main' into feat/markdown-editor
Fredkiss3 Jan 17, 2024
f881128
🚧 some changes
Fredkiss3 Jan 17, 2024
dfb190f
🔥 remove unused code
Fredkiss3 Jan 18, 2024
69cf432
🐛 fix bug with dropdown not focusing the textarea again.
Fredkiss3 Jan 18, 2024
3232d83
♻️ refactor
Fredkiss3 Jan 18, 2024
4ca3368
✨ handle list with selected characters
Fredkiss3 Jan 18, 2024
c47a4d6
💄 Fix list items styling
Fredkiss3 Jan 18, 2024
cc80119
💄 remove button support
Fredkiss3 Jan 18, 2024
7a96cd7
♻️ add more unsupported tags
Fredkiss3 Jan 18, 2024
50e3b01
Merge branch 'main' into feat/markdown-editor
Fredkiss3 Jan 28, 2024
a36c391
♻️ non functionning preview, but no error
Fredkiss3 Jan 28, 2024
613fec3
♻️ pass render markdown action from the root server component down
Fredkiss3 Jan 29, 2024
297ff2b
⚡️ fetch user and repository in parallel
Fredkiss3 Jan 29, 2024
905b834
💄 cut links off by 150 chars max
Fredkiss3 Jan 29, 2024
aa1d68e
💄 tweak `h-` tags styling
Fredkiss3 Jan 29, 2024
6df1bcb
♻️ don't put the promise into another transition
Fredkiss3 Jan 29, 2024
4cda2ae
🐛 correctly format quotes action
Fredkiss3 Jan 29, 2024
4284110
♻️ don't recreate the promise map on every render
Fredkiss3 Jan 29, 2024
1d0f45a
🚧 add some fuild font CSS
Fredkiss3 Jan 29, 2024
becc387
♻️ use `has` instead of `get` (same thing honestly)🤷
Fredkiss3 Jan 29, 2024
96cdb17
💄 fix markdown styling
Fredkiss3 Jan 29, 2024
e71fef2
Merge branch 'main' into feat/markdown-editor
Fredkiss3 Jan 31, 2024
fa7d304
🚧 wip
Fredkiss3 Feb 11, 2024
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
8 changes: 8 additions & 0 deletions src/app/(app)/[user]/[repository]/issues/new/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// components
import { NewIssueForm } from "~/components/issues/new-issue-form";
import { Markdown } from "~/components/markdown/markdown";

// utils
import { notFound } from "next/navigation";
Expand Down Expand Up @@ -36,6 +37,13 @@ export default async function NewIssuePage(props: NewIssuePageProps) {
<NewIssueForm
currentUserAvatarUrl={currentUser.avatar_url}
currentUserUsername={currentUser.username}
renderMarkdownAction={async (
content: string,
repositoryPath: `${string}/${string}`
) => {
"use server";
return <Markdown content={content} repository={repositoryPath} />;
}}
/>
);
}
13 changes: 9 additions & 4 deletions src/components/issues/new-issue-form.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
// "use client";
"use client";
import * as React from "react";
// components
import { GearIcon, InfoIcon } from "@primer/octicons-react";
import { Input } from "~/components/input";
import { MarkdownEditorClient } from "~/components/markdown-editor/markdown-editor.client";
import { MarkdownEditor } from "~/components/markdown-editor/markdown-editor";
import { SubmitButton } from "~/components/submit-button";
import { Avatar } from "~/components/avatar";

// utils
import { clsx } from "~/lib/shared/utils.shared";

// types
import type { MarkdownEditorProps } from "~/components/markdown-editor/markdown-editor";
export type NewIssueFormProps = {
currentUserUsername: string;
currentUserAvatarUrl: string;
renderMarkdownAction: MarkdownEditorProps["renderMarkdownAction"];
};

export function NewIssueForm({
currentUserAvatarUrl,
currentUserUsername
currentUserUsername,
renderMarkdownAction
}: NewIssueFormProps) {
return (
<section className="grid px-5">
Expand Down Expand Up @@ -67,11 +70,12 @@ export function NewIssueForm({
autoFocus
/>

<MarkdownEditorClient
<MarkdownEditor
name="body"
label="Add a description"
placeholder="Add your description here..."
required
renderMarkdownAction={renderMarkdownAction}
/>
</div>
</div>
Expand Down Expand Up @@ -136,6 +140,7 @@ function AssigneeFormInput() {
</div>
);
}

function LabelFormInput() {
return (
<div className="border-b border-neutral flex flex-col gap-2 text-sm pb-4">
Expand Down
31 changes: 2 additions & 29 deletions src/components/markdown-editor/markdown-editor-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,12 @@ import * as React from "react";
// components
import { ErrorBoundary } from "react-error-boundary";
import { Skeleton } from "~/components/skeleton";
import { RscClientRenderer } from "~/components/custom-rsc-renderer/rsc-client-renderer";

// utils
import { getIssueHoverCard } from "~/actions/issue.action";
import { getMarkdownPreview } from "~/actions/markdown.action";
import { lifetimeCache } from "~/lib/shared/lifetime-cache";

export type MarkdownEditorPreviewProps = {
repositoryPath: `${string}/${string}`;
content: string;
renderedMarkdown: Promise<React.JSX.Element>;
};

export function MarkdownEditorPreview(props: MarkdownEditorPreviewProps) {
// this is so that the action is included in the client manifest of this page and the hovercard
// in the preview works
const _ = getIssueHoverCard;
return (
<>
<ErrorBoundary
Expand All @@ -45,26 +35,9 @@ export function MarkdownEditorPreview(props: MarkdownEditorPreviewProps) {
</div>
}
>
{/* <RscClientRenderer
promise={loadMarkdownJSX(
loadMarkdownPreview(props.content, props.repositoryPath),
false
)}
/> */}
{React.use(props.renderedMarkdown)}
</React.Suspense>
</ErrorBoundary>
</>
);
}

// const loadMarkdownPreview = lifetimeCache(getMarkdownPreview);
// // const loadMarkdownJSX = lifetimeCache(renderPayloadOrPromiseToJSX);

// export async function prerenderMarkdownPreview(
// content: string,
// repositoryPath: `${string}/${string}`
// ) {
// React.startTransition(() => {
// loadMarkdownJSX(loadMarkdownPreview(content, repositoryPath), false);
// });
// }
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,38 @@ import { MarkdownEditorToolbar } from "~/components/markdown-editor/markdown-edi
import { clsx, isValidURL } from "~/lib/shared/utils.shared";
import { z } from "zod";
import { useTypedParams } from "~/lib/client/hooks/use-typed-params";
// import { prerenderMarkdownPreview } from "~/components/markdown-editor/markdown-editor-preview";
import { setFieldText } from "text-field-edit";
import { enableTabToIndent } from "indent-textarea";

// types
import type { TextareaProps } from "~/components/textarea";

export type MarkdownEditorProps = Omit<TextareaProps, "value">;
export type MarkdownEditorProps = Omit<TextareaProps, "value"> & {
renderMarkdownAction: (
content: string,
repositoryPath: `${string}/${string}`
) => Promise<React.JSX.Element>;
};

const TABS = {
PREVIEW: "PREVIEW",
EDITOR: "EDITOR"
} as const;
type TabValue = (typeof TABS)[keyof typeof TABS];

const paramsSchema = z.object({
const repoParamsSchema = z.object({
user: z.string(),
repository: z.string()
});

export function MarkdownEditorClient({
export function MarkdownEditor({
label,
defaultValue,
renderMarkdownAction,
...props
}: MarkdownEditorProps) {
const params = useTypedParams(
paramsSchema,
repoParamsSchema,
"This component should be used within a user/repository path"
);

Expand All @@ -50,6 +55,16 @@ export function MarkdownEditorClient({
const textAreaRef = React.useRef<React.ElementRef<"textarea"> | null>(null);
const lastTextareaSelectionRange = React.useRef({ start: -1, end: -1 });

const renderMarkdown = React.useCallback(
(content: string) =>
renderMarkdownAction(content, `${params.user}/${params.repository}`),
[params.repository, params.user, renderMarkdownAction]
);
const [lastRenderPromise, addPromise] = usePromiseRenderMap(
lastSavedTextContent,
renderMarkdown
);

React.useEffect(() => {
const observer = new ResizeObserver(([entry]) => {
const height = (entry.target as HTMLTextAreaElement).offsetHeight;
Expand Down Expand Up @@ -112,7 +127,10 @@ export function MarkdownEditorClient({
onValueChange={(tab) => {
setSelectedTab(tab as TabValue);
if (textAreaRef.current) {
setLastSavedTextContent(textAreaRef.current.value);
const lastTextContent = textAreaRef.current.value.trim();
setLastSavedTextContent(lastTextContent);
addPromise(lastTextContent);

lastTextareaSelectionRange.current = {
start: textAreaRef.current.selectionStart,
end: textAreaRef.current.selectionEnd
Expand Down Expand Up @@ -140,14 +158,9 @@ export function MarkdownEditorClient({
<Tabs.Trigger
value={TABS.PREVIEW}
onMouseEnter={() => {
if (
textAreaRef.current &&
textAreaRef.current.value.trim().length > 0
) {
// prerenderMarkdownPreview(
// textAreaRef.current.value.trim(),
// `${params.user}/${params.repository}`
// );
const textContent = textAreaRef.current?.value.trim();
if (textContent) {
addPromise(textContent);
}
}}
className={clsx(
Expand Down Expand Up @@ -218,11 +231,8 @@ export function MarkdownEditorClient({
name={props.name}
/>

{lastSavedTextContent.trim().length > 0 ? (
<MarkdownEditorPreview
content={lastSavedTextContent.trim()}
repositoryPath={`${params.user}/${params.repository}`}
/>
{lastSavedTextContent.trim().length > 0 && lastRenderPromise ? (
<MarkdownEditorPreview renderedMarkdown={lastRenderPromise} />
) : (
<span>Nothing to preview</span>
)}
Expand Down Expand Up @@ -254,3 +264,29 @@ export function MarkdownEditorClient({
</>
);
}

function usePromiseRenderMap(
content: string,
renderPromise: (content: string) => Promise<React.JSX.Element>
) {
const [promiseRenderMap, setPromiseRenderMap] = React.useState<
Map<string, Promise<React.JSX.Element>>
>(new Map());
const [, startTransition] = React.useTransition();
const lastRenderPromise = promiseRenderMap.get(content) ?? null;

const addPromise = React.useCallback(
(newContent: string) => {
startTransition(() => {
if (!promiseRenderMap.has(newContent)) {
const newMap = new Map(promiseRenderMap);
newMap.set(newContent, renderPromise(newContent));
setPromiseRenderMap(newMap);
Fredkiss3 marked this conversation as resolved.
Show resolved Hide resolved
}
});
},
[promiseRenderMap, renderPromise]
);

return [lastRenderPromise, addPromise] as const;
}
Loading