Skip to content

Commit

Permalink
upgrade blocknote + add plugin interface
Browse files Browse the repository at this point in the history
  • Loading branch information
YousefED committed May 15, 2024
1 parent 62ab381 commit 50c3954
Show file tree
Hide file tree
Showing 24 changed files with 11,306 additions and 4,707 deletions.
12,129 changes: 7,683 additions & 4,446 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions packages/editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
"private": true,
"dependencies": {
"react-confetti-explosion": "^2.1.2",
"@hocuspocus/provider": "^2.4.0",
"@hocuspocus/provider": "2.4.0",
"@atlaskit/atlassian-navigation": "^2.6.13",
"@atlaskit/avatar": "^21.3.7",
"@atlaskit/breadcrumbs": "^11.10.5",
"@atlaskit/button": "^16.8.2",
"@atlaskit/css-reset": "^6.5.3",
"@atlaskit/checkbox": "^13.3.0",
"@atlaskit/dropdown-menu": "^11.10.5",
"@atlaskit/empty-state": "^7.6.3",
"@atlaskit/flag": "^15.2.15",
Expand All @@ -26,7 +27,7 @@
"@atlaskit/textfield": "^5.6.3",
"@atlaskit/tree": "^8.8.5",
"@tiptap/core": "^2.0.4",
"@blocknote/core": "^0.9.3",
"@blocknote/core": "^0.13.2",
"@emotion/react": "^11.4.0",
"@supabase/auth-ui-react": "^0.4.5",
"@supabase/auth-ui-shared": "^0.1.7",
Expand Down Expand Up @@ -57,14 +58,15 @@
"vscode-lib": "^0.1.2",
"web-vitals": "^1.0.1",
"y-indexeddb": "9.0.6",
"y-websocket": "^2.0.3",
"y-protocols": "^1.0.5",
"yjs": "^13.6.4",
"react-inspector": "^6.0.1"
},
"scripts": {
"copytypes:self": "tsc --declaration --emitDeclarationOnly --noEmit false --composite false --declarationDir ./public/types/@typecell-org/editor",
"copytypes:externaldep": "mkdir -p public/types/$npm_config_pkgname && cp -rf ../../node_modules/$npm_config_pkgname/. public/types/$npm_config_pkgname",
"copytypes:allexternaldeps": "npm run copytypes:externaldep --pkgname=@types/react && npm run copytypes:externaldep --pkgname=@types/scheduler && npm run copytypes:externaldep --pkgname=@types/prop-types && npm run copytypes:externaldep --pkgname=csstype",
"copytypes:allexternaldeps": "npm run copytypes:externaldep --pkgname=@types/react && npm run copytypes:externaldep --pkgname=@types/prop-types && npm run copytypes:externaldep --pkgname=csstype",
"copytypes:dep": "mkdir -p public/types/@typecell-org/$npm_config_pkgname && cp -rf ../$npm_config_pkgname/types/. public/types/@typecell-org/$npm_config_pkgname",
"copytypes:alldeps": "npm run copytypes:dep --pkgname=util && npm run copytypes:dep --pkgname=engine && npm run copytypes:dep --pkgname=frame",
"copytypes": "npm run copytypes:self && npm run copytypes:alldeps && npm run copytypes:allexternaldeps",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ export function FrameHost(props: {
>();

const methods = {
markPlugins: async (identifierStr: string, value: boolean) => {
const identifier = parseIdentifier(identifierStr);
props.sessionStore.documentCoordinator?.markPlugins(identifier, value);
},
processYjsMessage: async (message: ArrayBuffer) => {
provider.onMessage(message, "penpal");
},
Expand Down
6 changes: 3 additions & 3 deletions packages/editor/src/app/main/components/ProfilePopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import { toProfilePage } from "../../routes/routes";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const Trigger = observer((props: any) => {
const { triggerRef, isSelected, testId, ...passProps } = props;
const { triggerRef, isSelected, testId, sessionStore, ...passProps } = props;
return (
<Profile
testId="profile-button"
icon={
<Avatar
name={props.sessionStore.loggedInUserId}
src={props.sessionStore.profile?.avatar_url || undefined}
name={sessionStore.loggedInUserId}
src={sessionStore.profile?.avatar_url || undefined}
size="32"
round={true}
textSizeRatio={2}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ import { DocumentResource } from "../../../../store/DocumentResource";
import { SessionStore } from "../../../../store/local/SessionStore";
import {
ClosePermissionsDialog,
ClosePluginDialog,
IsPermissionsDialogOpen,
IsPluginDialogOpen,
OpenPermissionsDialog,
OpenPluginDialog,
} from "../../../routes/routes";
import { MenuBar } from "../menuBar/MenuBar";

Expand All @@ -26,6 +29,7 @@ import SupabasePermissionsDialog from "../../../supabase-auth/routes/permissions
import { Breadcrumb } from "./Breadcrumb";
import styles from "./DocumentMenu.module.css";
import { ForkAlert } from "./ForkAlert";
import PluginDialog from "./PluginDialog";
import { ShareButton } from "./ShareButton";

type Props = {
Expand All @@ -36,7 +40,7 @@ type Props = {
// TODO: move?
function userCanEditPermissions(
sessionStore: SessionStore,
identifier: Identifier
identifier: Identifier,
) {
if (identifier instanceof HttpsIdentifier) {
return false;
Expand All @@ -56,7 +60,7 @@ export const DocumentMenu: React.FC<Props> = observer((props) => {
const { sessionStore } = props;
const canEditPermissions = userCanEditPermissions(
sessionStore,
props.document.identifier
props.document.identifier,
);
const location = useLocation();
const navigate = useNavigate();
Expand Down Expand Up @@ -127,13 +131,27 @@ export const DocumentMenu: React.FC<Props> = observer((props) => {
Permissions
</DropdownItem>
)}
{props.document instanceof DocumentResource && (
<DropdownItem onClick={() => OpenPluginDialog(navigate)}>
Plugins
</DropdownItem>
)}
</DropdownMenu>
</li>
</>
)}
</ul>
</aside>
{canEditPermissions && permissionsArea}
{props.document instanceof DocumentResource && (
<PluginDialog
close={() => ClosePluginDialog(navigate)}
isOpen={IsPluginDialogOpen(location)}
identifier={props.document.identifier}
sessionStore={sessionStore}
document={props.document}
/>
)}
</MenuBar>
);
});
173 changes: 173 additions & 0 deletions packages/editor/src/app/main/components/documentMenu/PluginDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { ModalTransition } from "@atlaskit/modal-dialog";
import { observer } from "mobx-react-lite";

import Button from "@atlaskit/button";
import Checkbox from "@atlaskit/checkbox";
import Modal, {
ModalBody,
ModalFooter,
ModalHeader,
ModalTitle,
} from "@atlaskit/modal-dialog";
import { useResource } from "@typecell-org/util";
import { useState } from "react";
import { Identifier } from "../../../../identifiers/Identifier";
import { DocConnection } from "../../../../store/DocConnection";
import { DocumentResource } from "../../../../store/DocumentResource";
import { SessionStore } from "../../../../store/local/SessionStore";
// import { SupabaseSessionStore } from "../../SupabaseSessionStore";

const PluginCheckbox = (props: {
id: string;
sessionStore: SessionStore;
onChange: (checked: boolean) => void;
checked?: boolean;
disabled?: boolean;
}) => {
const { sessionStore } = props;

const doc = useResource(() => {
const d = DocConnection.load(props.id, sessionStore);
return [
d,
() => {
d.dispose();
},
];
}, [props.id, sessionStore]);

if (typeof doc.doc === "string") {
return <div>Loading...</div>;
}

const dr = doc.doc.getSpecificType(DocumentResource);
return (
<div>
<Checkbox
isChecked={props.checked}
isDisabled={props.disabled}
onChange={(e) => {
props.onChange(e.target.checked);
}}
value={doc.doc.id}
label={`${dr.title} (${doc.doc.id})`}
size="medium"
/>
{/* */}
</div>
);
};

const PluginDialog = observer(
(props: {
isOpen: boolean;
close: () => void;
identifier: Identifier;
sessionStore: SessionStore;
document: DocumentResource;
}) => {
const { sessionStore } = props;
// const user = sessionStore.user;
// if (typeof user === "string" || user.type === "guest-user") {
// return null;
// }
const availablePlugins = [
...(props.sessionStore.documentCoordinator?.documents.values() || []),
]
.filter((d) => d.has_plugins)
.map((el) => el.id);

const enabledPlugins = [...props.document.plugins.keys()];

const oldPlugins = enabledPlugins.filter(
(p) => !availablePlugins.includes(p),
);

const availableNotEnabledPlugins = availablePlugins.filter(
(p) => !enabledPlugins.includes(p),
);

const [changedStates, setChangedStates] = useState<Record<string, boolean>>(
{},
);

return (
<ModalTransition>
{props.isOpen && (
<Modal
// TODO
// css={{ overflow: "visible" }}
onClose={() => props.close()}>
<ModalHeader>
<ModalTitle>Plugins</ModalTitle>
</ModalHeader>
<ModalBody>
{enabledPlugins.map((plugin) => (
<div key={plugin}>
<PluginCheckbox
onChange={(val) => {
setChangedStates((prev) => ({ ...prev, [plugin]: val }));
}}
checked={changedStates[plugin] ?? true}
sessionStore={sessionStore}
id={plugin}
/>
</div>
))}
{oldPlugins.map((plugin) => (
<div key={plugin}>
<PluginCheckbox
onChange={(val) => {
setChangedStates((prev) => ({ ...prev, [plugin]: val }));
}}
checked={changedStates[plugin] ?? true}
disabled={true}
sessionStore={sessionStore}
id={plugin}
/>
</div>
))}
{availableNotEnabledPlugins.map((plugin) => (
<div key={plugin}>
<PluginCheckbox
onChange={(val) => {
setChangedStates((prev) => ({ ...prev, [plugin]: val }));
}}
checked={changedStates[plugin] ?? false}
sessionStore={sessionStore}
id={plugin}
/>
</div>
))}
</ModalBody>
<ModalFooter>
<Button
appearance="primary"
autoFocus
type="submit"
onClick={() => {
for (const [plugin, enabled] of Object.entries(
changedStates,
)) {
if (enabled) {
props.document.plugins.set(plugin, true);
} else {
props.document.plugins.delete(plugin);
}
}
props.close();
}}>
Apply
</Button>
<Button appearance="subtle" onClick={() => props.close()}>
Cancel
</Button>
</ModalFooter>
</Modal>
)}
</ModalTransition>
);
},
);

export default PluginDialog;
13 changes: 13 additions & 0 deletions packages/editor/src/app/routes/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ export function IsPermissionsDialogOpen(location: Location) {
return (location as any).state?.permissionsDialog;
}

export function OpenPluginDialog(navigate: NavigateFunction) {
navigate({}, { state: { pluginDialog: true } });
}

export function ClosePluginDialog(navigate: NavigateFunction) {
navigate({}, { state: { pluginDialog: undefined } });
}

export function IsPluginDialogOpen(location: Location) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (location as any).state?.pluginDialog;
}

export function toRegisterScreen(): To {
return { pathname: "/register" };
}
Expand Down
Loading

0 comments on commit 50c3954

Please sign in to comment.