Skip to content

Commit

Permalink
feat: migrate editor to prosekit (#4888)
Browse files Browse the repository at this point in the history
  • Loading branch information
bigint authored May 5, 2024
2 parents 58b59ee + 26e79b8 commit 8f3c6f5
Show file tree
Hide file tree
Showing 61 changed files with 2,358 additions and 1,494 deletions.
16 changes: 8 additions & 8 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,6 @@
"@hey/lens": "workspace:*",
"@hey/ui": "workspace:*",
"@lens-protocol/metadata": "^1.1.6",
"@lexical/code": "^0.14.5",
"@lexical/hashtag": "^0.14.5",
"@lexical/link": "^0.14.5",
"@lexical/markdown": "^0.14.5",
"@lexical/react": "^0.14.5",
"@lexical/rich-text": "^0.14.5",
"@lexical/utils": "^0.14.5",
"@livepeer/react": "3.1.9",
"@radix-ui/react-hover-card": "^1.0.7",
"@rajesh896/broprint.js": "^2.1.1",
Expand All @@ -54,12 +47,12 @@
"framer-motion": "^11.1.7",
"graphql": "^16.8.1",
"idb-keyval": "^6.2.1",
"lexical": "^0.14.5",
"next": "^14.2.3",
"next-themes": "^0.3.0",
"party-js": "^2.2.0",
"plur": "^5.1.0",
"plyr-react": "^5.3.0",
"prosekit": "0.6.10",
"rc-slider": "10.6.2",
"react": "^18.3.1",
"react-chartjs-2": "^5.2.0",
Expand All @@ -69,9 +62,16 @@
"react-markdown": "^9.0.0",
"react-tracked": "^1.7.14",
"react-virtuoso": "^4.7.10",
"rehype-parse": "^9.0.0",
"rehype-remark": "^10.0.0",
"remark-breaks": "^4.0.0",
"remark-html": "^16.0.1",
"remark-linkify-regex": "^1.2.1",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
"strip-markdown": "^6.0.0",
"unified": "^11.0.4",
"unist-util-visit-parents": "^6.0.1",
"urlcat": "^3.1.0",
"use-resize-observer": "^9.1.0",
"uuid": "^9.0.1",
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/components/Bookmarks/Feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import type {
PublicationBookmarksRequest,
PublicationMetadataMainFocusType
} from '@hey/lens';
import type { FC } from 'react';
import type { StateSnapshot, VirtuosoHandle } from 'react-virtuoso';

import SinglePublication from '@components/Publication/SinglePublication';
import PublicationsShimmer from '@components/Shared/Shimmer/PublicationsShimmer';
import { BookmarkIcon } from '@heroicons/react/24/outline';
import { LimitType, usePublicationBookmarksQuery } from '@hey/lens';
import { Card, EmptyState, ErrorMessage } from '@hey/ui';
import { type FC, useRef } from 'react';
import { useRef } from 'react';
import { Virtuoso } from 'react-virtuoso';
import { useImpressionsStore } from 'src/store/non-persisted/useImpressionsStore';
import { useTipsStore } from 'src/store/non-persisted/useTipsStore';
Expand Down
13 changes: 11 additions & 2 deletions apps/web/src/components/Common/Providers/PreferencesProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const PreferencesProvider: FC = () => {
queryFn: () =>
getScore(sessionProfileId).then((score) => {
setScore(score.score);
return score;
}),
queryKey: ['getScore', sessionProfileId]
});
Expand All @@ -102,7 +103,11 @@ const PreferencesProvider: FC = () => {

// Fetch allowed tokens
useQuery({
queryFn: () => getAllTokens().then((tokens) => setAllowedTokens(tokens)),
queryFn: () =>
getAllTokens().then((tokens) => {
setAllowedTokens(tokens);
return tokens;
}),
queryKey: ['getAllTokensPreference']
});

Expand All @@ -118,7 +123,11 @@ const PreferencesProvider: FC = () => {

// Fetch fiat rates
useQuery({
queryFn: () => getFiatRates().then((rates) => setFiatRates(rates)),
queryFn: () =>
getFiatRates().then((rates) => {
setFiatRates(rates);
return rates;
}),
queryKey: ['getFiatRates']
});

Expand Down
1 change: 1 addition & 0 deletions apps/web/src/components/Common/Providers/ProProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const ProProvider: FC = () => {
getPro(sessionProfileId).then((data) => {
setIsPro(data.isPro);
setProExpiresAt(data.expiresAt);
return data;
}),
queryKey: ['getPro', sessionProfileId || '']
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import type { Draft } from '@hey/types/hey';
import type { FC } from 'react';

import { useEditorContext } from '@components/Composer/Editor';
import Loader from '@components/Shared/Loader';
import getAuthApiHeaders from '@helpers/getAuthApiHeaders';
import { ArchiveBoxArrowDownIcon } from '@heroicons/react/24/outline';
import { HEY_API_URL } from '@hey/data/constants';
import stopEventPropagation from '@hey/helpers/stopEventPropagation';
import { Button, EmptyState, ErrorMessage } from '@hey/ui';
import { $convertFromMarkdownString } from '@lexical/markdown';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
import { useState } from 'react';
Expand All @@ -27,7 +26,7 @@ const List: FC<ListProps> = ({ setShowModal }) => {
const [drafts, setDrafts] = useState<Draft[]>([]);
const [deleting, setDeleting] = useState(false);

const [editor] = useLexicalComposerContext();
const editor = useEditorContext();

const getDrafts = async (): Promise<[] | Draft[]> => {
try {
Expand Down Expand Up @@ -95,9 +94,7 @@ const List: FC<ListProps> = ({ setShowModal }) => {
};

const onSelectDraft = (draft: Draft) => {
editor.update(() => {
$convertFromMarkdownString(draft.content);
});
editor?.setMarkdown(draft.content);

setPublicationContent(draft.content);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { FC } from 'react';

import { ArchiveBoxArrowDownIcon } from '@heroicons/react/24/outline';
import { Modal, Tooltip } from '@hey/ui';
import { motion } from 'framer-motion';
import { type FC, useState } from 'react';
import { useState } from 'react';

import List from './List';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { FC } from 'react';

import { STATIC_IMAGES_URL } from '@hey/data/constants';
import { Input } from '@hey/ui';
import { type FC, useRef } from 'react';
import { useRef } from 'react';
import usePreventScrollOnNumberInput from 'src/hooks/usePreventScrollOnNumberInput';
import { useAllowedTokensStore } from 'src/store/persisted/useAllowedTokensStore';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { FC } from 'react';

import { Input } from '@hey/ui';
import { type FC } from 'react';

import { useRentableBillboardActionStore } from '.';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { FC } from 'react';
import type { Address } from 'viem';

import { STATIC_IMAGES_URL } from '@hey/data/constants';
import { Select } from '@hey/ui';
import { type FC } from 'react';
import { useAllowedTokensStore } from 'src/store/persisted/useAllowedTokensStore';

import { useRentableBillboardActionStore } from '.';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { UniswapQuote } from '@hey/types/hey';
import type { FC } from 'react';
import type { Address } from 'viem';

import { WMATIC_ADDRESS } from '@hey/data/constants';
import getUniswapQuote from '@hey/helpers/getUniswapQuote';
import { Input } from '@hey/ui';
import { type FC, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { CHAIN } from 'src/constants';
import { type Address, isAddress } from 'viem';
import { isAddress } from 'viem';

import { useSwapActionStore } from '.';

Expand Down
61 changes: 61 additions & 0 deletions apps/web/src/components/Composer/Editor/Editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { FC } from 'react';

import { defineEditorExtension } from '@helpers/prosekit/extension';
import { htmlFromMarkdown } from '@helpers/prosekit/markdown';
import getAvatar from '@hey/helpers/getAvatar';
import { Image } from '@hey/ui';
import dynamic from 'next/dynamic';
import 'prosekit/basic/style.css';
import { createEditor } from 'prosekit/core';
import { ProseKit } from 'prosekit/react';
import { useMemo, useRef } from 'react';
import useContentChange from 'src/hooks/prosekit/useContentChange';
import { usePaste } from 'src/hooks/prosekit/usePaste';
import { usePublicationStore } from 'src/store/non-persisted/publication/usePublicationStore';
import { useProfileStore } from 'src/store/persisted/useProfileStore';

import { useEditorHandle } from './EditorHandle';

// Lazy load EditorMenus to reduce bundle size
const EditorMenus = dynamic(() => import('./EditorMenus'), { ssr: false });

const Editor: FC = () => {
const { currentProfile } = useProfileStore();
const { publicationContent } = usePublicationStore();
const defaultMarkdownRef = useRef(publicationContent);

const defaultHTML = useMemo(() => {
const markdown = defaultMarkdownRef.current;
return markdown ? htmlFromMarkdown(markdown) : undefined;
}, []);

const editor = useMemo(() => {
const extension = defineEditorExtension();
return createEditor({ defaultHTML, extension });
}, [defaultHTML]);

useContentChange(editor);
usePaste(editor);
useEditorHandle(editor);

return (
<ProseKit editor={editor}>
<div className="box-border flex h-full w-full justify-stretch overflow-y-auto overflow-x-hidden px-5 py-4">
<Image
alt={currentProfile?.id}
className="mr-3 size-11 rounded-full border bg-gray-200 dark:border-gray-700"
src={getAvatar(currentProfile)}
/>
<div className="flex flex-1 flex-col overflow-x-hidden">
<EditorMenus />
<div
className="relative mt-[8.5px] box-border h-full min-h-[80px] flex-1 overflow-auto leading-6 outline-0 sm:leading-[26px]"
ref={editor.mount}
/>
</div>
</div>
</ProseKit>
);
};

export default Editor;
76 changes: 76 additions & 0 deletions apps/web/src/components/Composer/Editor/EditorHandle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { EditorExtension } from '@helpers/prosekit/extension';
import type { Editor } from 'prosekit/core';
import type { FC } from 'react';

import { setMarkdownContent } from '@helpers/prosekit/markdownContent';
import { createContext, useContext, useEffect, useState } from 'react';

interface EditorHandle {
insertText: (text: string) => void;
setMarkdown: (markdown: string) => void;
}

const HandleContext = createContext<EditorHandle | null>(null);
const SetHandleContext = createContext<((handle: EditorHandle) => void) | null>(
null
);

const Provider = ({ children }: { children: React.ReactNode }) => {
const [handle, setHandle] = useState<EditorHandle | null>(null);

return (
<HandleContext.Provider value={handle}>
<SetHandleContext.Provider value={setHandle}>
{children}
</SetHandleContext.Provider>
</HandleContext.Provider>
);
};

/**
* A hook for accessing the text editor handle.
*/
export const useEditorContext = (): EditorHandle | null => {
return useContext(HandleContext);
};

/**
* A hook to register the text editor handle.
*/
export const useEditorHandle = (editor: Editor<EditorExtension>) => {
const setHandle = useContext(SetHandleContext);

useEffect(() => {
const handle: EditorHandle = {
insertText: (text: string): void => {
if (!editor.mounted) {
return;
}

editor.commands.insertText({ text });
},
setMarkdown: (markdown: string): void => {
setMarkdownContent(editor, markdown);
}
};

setHandle?.(handle);
}, [setHandle, editor]);
};

/**
* A higher-order component for providing the text editor handle.
*/
export const withEditorContext = <Props extends object>(
Component: FC<Props>
): FC<Props> => {
const WithEditorContext: FC<Props> = (props: Props) => {
return (
<Provider>
<Component {...props} />
</Provider>
);
};

return WithEditorContext;
};
17 changes: 17 additions & 0 deletions apps/web/src/components/Composer/Editor/EditorMenus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { FC } from 'react';

import EmojiPicker from './EmojiPicker';
import InlineMenu from './InlineMenu';
import MentionPicker from './MentionPicker';

const EditorMenus: FC = () => {
return (
<>
<InlineMenu />
<MentionPicker />
<EmojiPicker />
</>
);
};

export default EditorMenus;
Loading

1 comment on commit 8f3c6f5

@vercel
Copy link

@vercel vercel bot commented on 8f3c6f5 May 5, 2024

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

web – ./apps/web

heyxyz.vercel.app
hey.xyz
web-git-main-heyxyz.vercel.app
web-heyxyz.vercel.app

Please sign in to comment.