diff --git a/app/components/CommandBar.tsx b/app/components/CommandBar.tsx index f89fbc0a0a..58103dd895 100644 --- a/app/components/CommandBar.tsx +++ b/app/components/CommandBar.tsx @@ -25,9 +25,9 @@ function CommandBar() { const { rootAction } = useKBar((state) => ({ rootAction: state.currentRootActionId - ? ((state.actions[ + ? (state.actions[ state.currentRootActionId - ] as unknown) as CommandBarAction) + ] as unknown as CommandBarAction) : undefined, })); diff --git a/app/components/ContentEditable.tsx b/app/components/ContentEditable.tsx index c0f9dfb2b3..1e37f03fdd 100644 --- a/app/components/ContentEditable.tsx +++ b/app/components/ContentEditable.tsx @@ -85,27 +85,33 @@ const ContentEditable = React.forwardRef( }, })); - const wrappedEvent = ( - callback: - | React.FocusEventHandler - | React.FormEventHandler - | React.KeyboardEventHandler - | undefined - ) => (event: any) => { - const text = contentRef.current?.innerText || ""; + const wrappedEvent = + ( + callback: + | React.FocusEventHandler + | React.FormEventHandler + | React.KeyboardEventHandler + | undefined + ) => + (event: any) => { + const text = contentRef.current?.innerText || ""; - if (maxLength && isPrintableKeyEvent(event) && text.length >= maxLength) { - event?.preventDefault(); - return; - } + if ( + maxLength && + isPrintableKeyEvent(event) && + text.length >= maxLength + ) { + event?.preventDefault(); + return; + } - if (text !== lastValue.current) { - lastValue.current = text; - onChange && onChange(text); - } + if (text !== lastValue.current) { + lastValue.current = text; + onChange && onChange(text); + } - callback?.(event); - }; + callback?.(event); + }; // This is to account for being within a React.Suspense boundary, in this // case the component may be rendered with display: none. React 18 may solve diff --git a/app/components/ContextMenu/MouseSafeArea.tsx b/app/components/ContextMenu/MouseSafeArea.tsx index a1cd354bcb..817ab14e98 100644 --- a/app/components/ContextMenu/MouseSafeArea.tsx +++ b/app/components/ContextMenu/MouseSafeArea.tsx @@ -24,8 +24,12 @@ type Positions = { export default function MouseSafeArea(props: { parentRef: React.RefObject; }) { - const { x = 0, y = 0, height: h = 0, width: w = 0 } = - props.parentRef.current?.getBoundingClientRect() || {}; + const { + x = 0, + y = 0, + height: h = 0, + width: w = 0, + } = props.parentRef.current?.getBoundingClientRect() || {}; const [mouseX, mouseY] = useMousePosition(); const positions = { x, y, h, w, mouseX, mouseY }; diff --git a/app/components/DocumentExplorer.tsx b/app/components/DocumentExplorer.tsx index 9790b494f6..cc5c44cb24 100644 --- a/app/components/DocumentExplorer.tsx +++ b/app/components/DocumentExplorer.tsx @@ -45,9 +45,8 @@ function DocumentExplorer({ onSubmit, onSelect, items }: Props) { const [selectedNode, selectNode] = React.useState( null ); - const [initialScrollOffset, setInitialScrollOffset] = React.useState( - 0 - ); + const [initialScrollOffset, setInitialScrollOffset] = + React.useState(0); const [activeNode, setActiveNode] = React.useState(0); const [expandedNodes, setExpandedNodes] = React.useState([]); const [itemRefs, setItemRefs] = React.useState< diff --git a/app/components/DocumentListItem.tsx b/app/components/DocumentListItem.tsx index 2667e2e6aa..fa77c89eae 100644 --- a/app/components/DocumentListItem.tsx +++ b/app/components/DocumentListItem.tsx @@ -181,7 +181,7 @@ const Actions = styled(EventBoundary)` color: ${s("textSecondary")}; ${NudeButton} { - &: ${hover}, &[aria-expanded= "true" ] { + &: ${hover}, &[aria-expanded= "true"] { background: ${s("sidebarControlHoverBackground")}; } } diff --git a/app/components/DocumentViews.tsx b/app/components/DocumentViews.tsx index a836e0545d..363ac1cbd5 100644 --- a/app/components/DocumentViews.tsx +++ b/app/components/DocumentViews.tsx @@ -33,9 +33,10 @@ function DocumentViews({ document, isOpen }: Props) { documentViews, (view) => !presentIds.includes(view.user.id) ); - const users = React.useMemo(() => sortedViews.map((v) => v.user), [ - sortedViews, - ]); + const users = React.useMemo( + () => sortedViews.map((v) => v.user), + [sortedViews] + ); return ( <> diff --git a/app/components/Editor.tsx b/app/components/Editor.tsx index 195042df22..6e533003ed 100644 --- a/app/components/Editor.tsx +++ b/app/components/Editor.tsx @@ -67,10 +67,8 @@ function Editor(props: Props, ref: React.RefObject | null) { const localRef = React.useRef(); const preferences = auth.user?.preferences; const previousHeadings = React.useRef(null); - const [ - activeLinkElement, - setActiveLink, - ] = React.useState(null); + const [activeLinkElement, setActiveLink] = + React.useState(null); const previousCommentIds = React.useRef(); const handleLinkActive = React.useCallback((element: HTMLAnchorElement) => { diff --git a/app/components/GroupListItem.tsx b/app/components/GroupListItem.tsx index b7431dddf1..35e9303e7e 100644 --- a/app/components/GroupListItem.tsx +++ b/app/components/GroupListItem.tsx @@ -28,11 +28,8 @@ type Props = { function GroupListItem({ group, showFacepile, renderActions }: Props) { const { groupMemberships } = useStores(); const { t } = useTranslation(); - const [ - membersModalOpen, - setMembersModalOpen, - setMembersModalClosed, - ] = useBoolean(); + const [membersModalOpen, setMembersModalOpen, setMembersModalClosed] = + useBoolean(); const memberCount = group.memberCount; const membershipsInGroup = groupMemberships.inGroup(group.id); const users = membershipsInGroup diff --git a/app/components/SearchPopover.tsx b/app/components/SearchPopover.tsx index 27f9c83456..c1eab3d420 100644 --- a/app/components/SearchPopover.tsx +++ b/app/components/SearchPopover.tsx @@ -76,9 +76,8 @@ function SearchPopover({ shareId }: Props) { [popover, cachedQuery] ); - const searchInputRef = popover.unstable_referenceRef as React.RefObject< - HTMLInputElement - >; + const searchInputRef = + popover.unstable_referenceRef as React.RefObject; const firstSearchItem = React.useRef(null); diff --git a/app/components/Sidebar/components/SharedDocumentLink.tsx b/app/components/Sidebar/components/SharedDocumentLink.tsx index e4d9eebde9..4bc1ca629c 100644 --- a/app/components/Sidebar/components/SharedDocumentLink.tsx +++ b/app/components/Sidebar/components/SharedDocumentLink.tsx @@ -42,9 +42,10 @@ function DocumentLink( !!node.children.length || activeDocument?.parentDocumentId === node.id; const document = documents.get(node.id); - const showChildren = React.useMemo(() => !!hasChildDocuments, [ - hasChildDocuments, - ]); + const showChildren = React.useMemo( + () => !!hasChildDocuments, + [hasChildDocuments] + ); const [expanded, setExpanded] = React.useState(showChildren); diff --git a/app/components/WebsocketProvider.tsx b/app/components/WebsocketProvider.tsx index 3f56f38a49..f3bdc1a63e 100644 --- a/app/components/WebsocketProvider.tsx +++ b/app/components/WebsocketProvider.tsx @@ -30,9 +30,8 @@ type SocketWithAuthentication = Socket & { authenticated?: boolean; }; -export const WebsocketContext = React.createContext( - null -); +export const WebsocketContext = + React.createContext(null); type Props = RootStore; diff --git a/app/editor/components/LinkEditor.tsx b/app/editor/components/LinkEditor.tsx index af744ea362..62d64e5afa 100644 --- a/app/editor/components/LinkEditor.tsx +++ b/app/editor/components/LinkEditor.tsx @@ -272,16 +272,15 @@ class LinkEditor extends React.Component { view.focus(); }; - handleSelectLink = (url: string, title: string) => ( - event: React.MouseEvent - ) => { - event.preventDefault(); - this.save(url, title); + handleSelectLink = + (url: string, title: string) => (event: React.MouseEvent) => { + event.preventDefault(); + this.save(url, title); - if (this.initialSelectionLength) { - this.moveSelectionToEnd(); - } - }; + if (this.initialSelectionLength) { + this.moveSelectionToEnd(); + } + }; moveSelectionToEnd = () => { const { to, view } = this.props; diff --git a/app/editor/components/SelectionToolbar.tsx b/app/editor/components/SelectionToolbar.tsx index c4ed4dd6d1..65fced58c0 100644 --- a/app/editor/components/SelectionToolbar.tsx +++ b/app/editor/components/SelectionToolbar.tsx @@ -197,10 +197,8 @@ export default function SelectionToolbar(props: Props) { return null; } - const colIndex = getColumnIndex( - (state.selection as unknown) as CellSelection - ); - const rowIndex = getRowIndex((state.selection as unknown) as CellSelection); + const colIndex = getColumnIndex(state.selection as unknown as CellSelection); + const rowIndex = getRowIndex(state.selection as unknown as CellSelection); const isTableSelection = colIndex !== undefined && rowIndex !== undefined; const link = isMarkActive(state.schema.marks.link)(state); const range = getMarkRange(selection.$from, state.schema.marks.link); diff --git a/app/editor/index.tsx b/app/editor/index.tsx index f8965cee10..c311c25cd0 100644 --- a/app/editor/index.tsx +++ b/app/editor/index.tsx @@ -464,9 +464,8 @@ export class Editor extends React.PureComponent< nodeViews: this.nodeViews, dispatchTransaction(transaction) { // callback is bound to have the view instance as its this binding - const { state, transactions } = this.state.applyTransaction( - transaction - ); + const { state, transactions } = + this.state.applyTransaction(transaction); this.updateState(state); @@ -520,9 +519,8 @@ export class Editor extends React.PureComponent< return trim ? content.trim() : content; } - return (trim - ? ProsemirrorHelper.trim(this.view.state.doc) - : this.view.state.doc + return ( + trim ? ProsemirrorHelper.trim(this.view.state.doc) : this.view.state.doc ).toJSON(); }; diff --git a/app/hooks/useComponentSize.ts b/app/hooks/useComponentSize.ts index 24d929ccc0..69e857b5d1 100644 --- a/app/hooks/useComponentSize.ts +++ b/app/hooks/useComponentSize.ts @@ -1,8 +1,9 @@ import { useState, useEffect } from "react"; -export default function useComponentSize( - ref: React.RefObject -): { width: number; height: number } { +export default function useComponentSize(ref: React.RefObject): { + width: number; + height: number; +} { const [size, setSize] = useState({ width: ref.current?.clientWidth || 0, height: ref.current?.clientHeight || 0, diff --git a/app/hooks/useMousePosition.ts b/app/hooks/useMousePosition.ts index 475ec2b3a9..55041bc6d1 100644 --- a/app/hooks/useMousePosition.ts +++ b/app/hooks/useMousePosition.ts @@ -16,8 +16,7 @@ type MousePosition = [number, number]; export const useMousePosition = () => { const isMounted = useIsMounted(); const [mousePosition, setMousePosition] = React.useState([ - 0, - 0, + 0, 0, ]); const updateMousePosition = React.useMemo( diff --git a/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx b/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx index 7869a5c341..1628161e66 100644 --- a/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx +++ b/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx @@ -25,20 +25,12 @@ type Props = { function AddGroupsToCollection(props: Props) { const { collection } = props; - const [ - newGroupModalOpen, - handleNewGroupModalOpen, - handleNewGroupModalClose, - ] = useBoolean(false); + const [newGroupModalOpen, handleNewGroupModalOpen, handleNewGroupModalClose] = + useBoolean(false); const [query, setQuery] = React.useState(""); - const { - auth, - collectionGroupMemberships, - groups, - policies, - toasts, - } = useStores(); + const { auth, collectionGroupMemberships, groups, policies, toasts } = + useStores(); const { fetchPage: fetchGroups } = groups; const { t } = useTranslation(); diff --git a/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx b/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx index 4807317221..7b4f908cc2 100644 --- a/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx +++ b/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx @@ -27,11 +27,8 @@ function AddPeopleToCollection({ collection }: Props) { const { showToast } = useToasts(); const team = useCurrentTeam(); const { t } = useTranslation(); - const [ - inviteModalOpen, - setInviteModalOpen, - setInviteModalClosed, - ] = useBoolean(); + const [inviteModalOpen, setInviteModalOpen, setInviteModalClosed] = + useBoolean(); const [query, setQuery] = React.useState(""); const handleFilter = (ev: React.ChangeEvent) => { diff --git a/app/scenes/CollectionPermissions/index.tsx b/app/scenes/CollectionPermissions/index.tsx index a840c4ca87..0aa09664e2 100644 --- a/app/scenes/CollectionPermissions/index.tsx +++ b/app/scenes/CollectionPermissions/index.tsx @@ -44,11 +44,8 @@ function CollectionPermissions({ collectionId }: Props) { const collection = collections.get(collectionId); invariant(collection, "Collection not found"); - const [ - addGroupModalOpen, - handleAddGroupModalOpen, - handleAddGroupModalClose, - ] = useBoolean(); + const [addGroupModalOpen, handleAddGroupModalOpen, handleAddGroupModalClose] = + useBoolean(); const [ addMemberModalOpen, diff --git a/app/scenes/Document/components/CommentThreadItem.tsx b/app/scenes/Document/components/CommentThreadItem.tsx index 11aef0b996..12ada79146 100644 --- a/app/scenes/Document/components/CommentThreadItem.tsx +++ b/app/scenes/Document/components/CommentThreadItem.tsx @@ -245,7 +245,7 @@ const Menu = styled(CommentMenu)<{ dir?: "rtl" | "ltr" }>` transition: opacity 100ms ease-in-out; color: ${s("textSecondary")}; - &: ${hover}, &[aria-expanded= "true" ] { + &: ${hover}, &[aria-expanded= "true"] { opacity: 1; background: ${s("sidebarActiveBackground")}; } diff --git a/app/scenes/Document/components/Document.tsx b/app/scenes/Document/components/Document.tsx index f76ad9affc..2d3c3ddd9e 100644 --- a/app/scenes/Document/components/Document.tsx +++ b/app/scenes/Document/components/Document.tsx @@ -376,16 +376,8 @@ class DocumentScene extends React.Component { }; render() { - const { - document, - revision, - readOnly, - abilities, - auth, - ui, - shareId, - t, - } = this.props; + const { document, revision, readOnly, abilities, auth, ui, shareId, t } = + this.props; const team = auth.team; const isShare = !!shareId; const embedsDisabled = diff --git a/app/scenes/Document/components/MultiplayerEditor.tsx b/app/scenes/Document/components/MultiplayerEditor.tsx index abbabad22c..91591daa55 100644 --- a/app/scenes/Document/components/MultiplayerEditor.tsx +++ b/app/scenes/Document/components/MultiplayerEditor.tsx @@ -48,10 +48,8 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) { const { presence, ui } = useStores(); const token = useCurrentToken(); const [showCursorNames, setShowCursorNames] = React.useState(false); - const [ - remoteProvider, - setRemoteProvider, - ] = React.useState(null); + const [remoteProvider, setRemoteProvider] = + React.useState(null); const [isLocalSynced, setLocalSynced] = React.useState(false); const [isRemoteSynced, setRemoteSynced] = React.useState(false); const [ydoc] = React.useState(() => new Y.Doc()); diff --git a/app/scenes/GroupMembers/AddPeopleToGroup.tsx b/app/scenes/GroupMembers/AddPeopleToGroup.tsx index e00fc92df4..42252f96ec 100644 --- a/app/scenes/GroupMembers/AddPeopleToGroup.tsx +++ b/app/scenes/GroupMembers/AddPeopleToGroup.tsx @@ -28,11 +28,8 @@ function AddPeopleToGroup(props: Props) { const { t } = useTranslation(); const [query, setQuery] = React.useState(""); - const [ - inviteModalOpen, - handleInviteModalOpen, - handleInviteModalClose, - ] = useBoolean(false); + const [inviteModalOpen, handleInviteModalOpen, handleInviteModalClose] = + useBoolean(false); const { fetchPage: fetchUsers } = users; const debouncedFetch = React.useMemo( diff --git a/app/scenes/Settings/Groups.tsx b/app/scenes/Settings/Groups.tsx index 86c1f51fdd..06c4b6d7b0 100644 --- a/app/scenes/Settings/Groups.tsx +++ b/app/scenes/Settings/Groups.tsx @@ -24,11 +24,8 @@ function Groups() { const { groups } = useStores(); const team = useCurrentTeam(); const can = usePolicy(team); - const [ - newGroupModalOpen, - handleNewGroupModalOpen, - handleNewGroupModalClose, - ] = useBoolean(); + const [newGroupModalOpen, handleNewGroupModalOpen, handleNewGroupModalClose] = + useBoolean(); return ( - authenticationProviders.fetchPage({}) - ); + const { + data: providers, + loading, + request, + } = useRequest(() => authenticationProviders.fetchPage({})); React.useEffect(() => { if (!providers && !loading) { diff --git a/app/scenes/Settings/components/DomainManagement.tsx b/app/scenes/Settings/components/DomainManagement.tsx index 158c6cda62..b192bc9bb9 100644 --- a/app/scenes/Settings/components/DomainManagement.tsx +++ b/app/scenes/Settings/components/DomainManagement.tsx @@ -31,9 +31,8 @@ function DomainManagement({ onSuccess }: Props) { allowedDomains.length ); - const [existingDomainsTouched, setExistingDomainsTouched] = React.useState( - false - ); + const [existingDomainsTouched, setExistingDomainsTouched] = + React.useState(false); const handleSaveDomains = React.useCallback(async () => { try { @@ -67,19 +66,18 @@ function DomainManagement({ onSuccess }: Props) { setAllowedDomains(newDomains); }; - const createOnDomainChangedHandler = (index: number) => ( - ev: React.ChangeEvent - ) => { - const newDomains = allowedDomains.slice(); + const createOnDomainChangedHandler = + (index: number) => (ev: React.ChangeEvent) => { + const newDomains = allowedDomains.slice(); - newDomains[index] = ev.currentTarget.value; - setAllowedDomains(newDomains); + newDomains[index] = ev.currentTarget.value; + setAllowedDomains(newDomains); - const touchedExistingDomain = index < lastKnownDomainCount; - if (touchedExistingDomain) { - setExistingDomainsTouched(true); - } - }; + const touchedExistingDomain = index < lastKnownDomainCount; + if (touchedExistingDomain) { + setExistingDomainsTouched(true); + } + }; const showSaveChanges = existingDomainsTouched || diff --git a/app/scenes/UserDelete.tsx b/app/scenes/UserDelete.tsx index a672f2d9c9..115d02cdff 100644 --- a/app/scenes/UserDelete.tsx +++ b/app/scenes/UserDelete.tsx @@ -19,9 +19,11 @@ function UserDelete() { const { auth } = useStores(); const { showToast } = useToasts(); const { t } = useTranslation(); - const { register, handleSubmit: formHandleSubmit, formState } = useForm< - FormData - >(); + const { + register, + handleSubmit: formHandleSubmit, + formState, + } = useForm(); const handleRequestDelete = React.useCallback( async (ev: React.SyntheticEvent) => { diff --git a/app/stores/AuthenticationProvidersStore.ts b/app/stores/AuthenticationProvidersStore.ts index 4e1ffb71eb..af5b1fdde3 100644 --- a/app/stores/AuthenticationProvidersStore.ts +++ b/app/stores/AuthenticationProvidersStore.ts @@ -2,9 +2,7 @@ import AuthenticationProvider from "~/models/AuthenticationProvider"; import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; -export default class AuthenticationProvidersStore extends BaseStore< - AuthenticationProvider -> { +export default class AuthenticationProvidersStore extends BaseStore { actions = [RPCAction.List, RPCAction.Update]; constructor(rootStore: RootStore) { diff --git a/app/stores/CollectionGroupMembershipsStore.ts b/app/stores/CollectionGroupMembershipsStore.ts index e18dc5af50..2dabf6ccdb 100644 --- a/app/stores/CollectionGroupMembershipsStore.ts +++ b/app/stores/CollectionGroupMembershipsStore.ts @@ -7,9 +7,7 @@ import { client } from "~/utils/ApiClient"; import BaseStore, { PAGINATION_SYMBOL, RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; -export default class CollectionGroupMembershipsStore extends BaseStore< - CollectionGroupMembership -> { +export default class CollectionGroupMembershipsStore extends BaseStore { actions = [RPCAction.Create, RPCAction.Delete]; constructor(rootStore: RootStore) { diff --git a/app/stores/WebhookSubscriptionStore.ts b/app/stores/WebhookSubscriptionStore.ts index e228273dc5..dc318ca955 100644 --- a/app/stores/WebhookSubscriptionStore.ts +++ b/app/stores/WebhookSubscriptionStore.ts @@ -3,9 +3,7 @@ import WebhookSubscription from "~/models/WebhookSubscription"; import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; -export default class WebhookSubscriptionsStore extends BaseStore< - WebhookSubscription -> { +export default class WebhookSubscriptionsStore extends BaseStore { actions = [ RPCAction.List, RPCAction.Create, diff --git a/package.json b/package.json index f1a8bced49..de7d0dd68f 100644 --- a/package.json +++ b/package.json @@ -328,7 +328,7 @@ "lint-staged": "^13.1.0", "nodemon": "^2.0.22", "postinstall-postinstall": "^2.1.0", - "prettier": "^2.0.5", + "prettier": "^2.8.8", "react-refresh": "^0.14.0", "rimraf": "^2.5.4", "rollup-plugin-webpack-stats": "^0.2.0", diff --git a/plugins/webhooks/client/components/WebhookSubscriptionListItem.tsx b/plugins/webhooks/client/components/WebhookSubscriptionListItem.tsx index b564570b3c..2e785682f0 100644 --- a/plugins/webhooks/client/components/WebhookSubscriptionListItem.tsx +++ b/plugins/webhooks/client/components/WebhookSubscriptionListItem.tsx @@ -19,11 +19,8 @@ type Props = { const WebhookSubscriptionListItem = ({ webhook }: Props) => { const { t } = useTranslation(); const { dialogs } = useStores(); - const [ - editModalOpen, - handleEditModalOpen, - handleEditModalClose, - ] = useBoolean(); + const [editModalOpen, handleEditModalOpen, handleEditModalClose] = + useBoolean(); const showDeletionConfirmation = React.useCallback(() => { dialogs.openModal({ diff --git a/server/__mocks__/dd-trace.ts b/server/__mocks__/dd-trace.ts index 9d67df805c..463b0888f1 100644 --- a/server/__mocks__/dd-trace.ts +++ b/server/__mocks__/dd-trace.ts @@ -7,7 +7,7 @@ const emptyFn = function () {}; const callableHandlers = { get(_target: T, _prop: P, _receiver: any): T[P] { const newMock = new Proxy(emptyFn, callableHandlers); - return (newMock as any) as T[P]; + return newMock as any as T[P]; }, apply any, A extends Parameters>( @@ -16,7 +16,7 @@ const callableHandlers = { _args: A ): ReturnType { const newMock = new Proxy(emptyFn, callableHandlers); - return (newMock as any) as ReturnType; + return newMock as any as ReturnType; }, }; diff --git a/server/collaboration/PersistenceExtension.ts b/server/collaboration/PersistenceExtension.ts index 48f3a24380..d0f9ddd39c 100644 --- a/server/collaboration/PersistenceExtension.ts +++ b/server/collaboration/PersistenceExtension.ts @@ -88,9 +88,8 @@ export default class PersistenceExtension implements Extension { // Find the collaborators that have modified the document since it was last // persisted and clear the map, if there's no collaborators then we don't // need to persist the document. - const documentCollaboratorIds = this.documentCollaboratorIds.get( - documentName - ); + const documentCollaboratorIds = + this.documentCollaboratorIds.get(documentName); if (!documentCollaboratorIds) { Logger.debug("multiplayer", `No changes for ${documentName}`); return; diff --git a/server/commands/documentImporter.ts b/server/commands/documentImporter.ts index 97667d61db..e2fbfcabc1 100644 --- a/server/commands/documentImporter.ts +++ b/server/commands/documentImporter.ts @@ -30,8 +30,7 @@ const importMapping: ImportableFile[] = [ getMarkdown: docxToMarkdown, }, { - type: - "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", getMarkdown: docxToMarkdown, }, { diff --git a/server/logging/Logger.ts b/server/logging/Logger.ts index 64ef4c0208..116029d103 100644 --- a/server/logging/Logger.ts +++ b/server/logging/Logger.ts @@ -171,12 +171,12 @@ class Logger { if (isString(input)) { if (sensitiveFields.some((field) => input.includes(field))) { - return ("[Filtered]" as any) as T; + return "[Filtered]" as any as T; } } if (isArray(input)) { - return (input.map(this.sanitize) as any) as T; + return input.map(this.sanitize) as any as T; } if (isObject(input)) { diff --git a/server/logging/tracing.ts b/server/logging/tracing.ts index d295225cfa..780fb5b825 100644 --- a/server/logging/tracing.ts +++ b/server/logging/tracing.ts @@ -26,7 +26,7 @@ import env from "@server/env"; import tracer from "./tracer"; import * as Tracing from "./tracer"; -type DDTag = typeof DDTags[keyof typeof DDTags]; +type DDTag = (typeof DDTags)[keyof typeof DDTags]; type Tags = { [tag in DDTag]?: any; @@ -55,72 +55,74 @@ interface TraceConfig { * * @param config Optional configuration for the span that will be created for this trace. */ -export const traceFunction = (config: TraceConfig) => < - F extends (...args: any[]) => any, - P extends Parameters, - R extends ReturnType ->( - target: F -): F => - env.ENVIRONMENT === "test" - ? target - : (function wrapperFn(this: any, ...args: P): R { - const { className, methodName = target.name, tags } = config; - const childOf = config.isRoot - ? undefined - : tracer.scope().active() || undefined; +export const traceFunction = + (config: TraceConfig) => + < + F extends (...args: any[]) => any, + P extends Parameters, + R extends ReturnType + >( + target: F + ): F => + env.ENVIRONMENT === "test" + ? target + : (function wrapperFn(this: any, ...args: P): R { + const { className, methodName = target.name, tags } = config; + const childOf = config.isRoot + ? undefined + : tracer.scope().active() || undefined; - const spanName = config.spanName || className || "DEFAULT_SPAN_NAME"; + const spanName = config.spanName || className || "DEFAULT_SPAN_NAME"; - const resourceName = config.resourceName - ? config.resourceName - : methodName; - const spanOptions: SpanOptions = { - childOf, - tags: { - [DDTags.RESOURCE_NAME]: resourceName, - ...tags, - }, - }; + const resourceName = config.resourceName + ? config.resourceName + : methodName; + const spanOptions: SpanOptions = { + childOf, + tags: { + [DDTags.RESOURCE_NAME]: resourceName, + ...tags, + }, + }; - const span = tracer.startSpan(spanName, spanOptions); + const span = tracer.startSpan(spanName, spanOptions); - if (!span) { - return target.call(this, ...args); - } - - if (config.serviceName) { - span.setTag( - DDTags.SERVICE_NAME, - `${env.DD_SERVICE}-${config.serviceName}` - ); - } - - if (config.makeSearchable) { - span.setTag(DDTags.ANALYTICS, true); - } - - // The callback fn needs to be wrapped in an arrow fn as the activate fn clobbers `this` - return tracer.scope().activate(span, () => { - const output = target.call(this, ...args); - - if (output && typeof output.then === "function") { - output - .catch((error: Error | undefined) => { - if (error instanceof Error) { - Tracing.setError(error, span); - } - }) - .finally(() => { - span.finish(); - }); - } else { - span.finish(); + if (!span) { + return target.call(this, ...args); } - return output; - }); - } as F); + if (config.serviceName) { + span.setTag( + DDTags.SERVICE_NAME, + `${env.DD_SERVICE}-${config.serviceName}` + ); + } + + if (config.makeSearchable) { + span.setTag(DDTags.ANALYTICS, true); + } + + // The callback fn needs to be wrapped in an arrow fn as the activate fn clobbers `this` + return tracer.scope().activate(span, () => { + const output = target.call(this, ...args); + + if (output && typeof output.then === "function") { + output + .catch((error: Error | undefined) => { + if (error instanceof Error) { + Tracing.setError(error, span); + } + }) + .finally(() => { + span.finish(); + }); + } else { + span.finish(); + } + + return output; + }); + } as F); const traceMethod = (config?: TraceConfig) => function R>( diff --git a/server/models/AuthenticationProvider.ts b/server/models/AuthenticationProvider.ts index c2981f996d..48288eec23 100644 --- a/server/models/AuthenticationProvider.ts +++ b/server/models/AuthenticationProvider.ts @@ -98,8 +98,9 @@ class AuthenticationProvider extends Model { } disable = async (options?: SaveOptions) => { - const res = await (this - .constructor as typeof AuthenticationProvider).findAndCountAll({ + const res = await ( + this.constructor as typeof AuthenticationProvider + ).findAndCountAll({ ...options, where: { teamId: this.teamId, diff --git a/server/models/Document.ts b/server/models/Document.ts index cf34df40f7..04b5249c38 100644 --- a/server/models/Document.ts +++ b/server/models/Document.ts @@ -527,8 +527,9 @@ class Document extends ParanoidModel { const getChildDocumentIds = async ( ...parentDocumentId: string[] ): Promise => { - const childDocuments = await (this - .constructor as typeof Document).findAll({ + const childDocuments = await ( + this.constructor as typeof Document + ).findAll({ attributes: ["id"], where: { parentDocumentId, @@ -560,8 +561,9 @@ class Document extends ParanoidModel { // Helper to archive all child documents for a document const archiveChildren = async (parentDocumentId: string) => { - const childDocuments = await (this - .constructor as typeof Document).findAll({ + const childDocuments = await ( + this.constructor as typeof Document + ).findAll({ where: { parentDocumentId, }, diff --git a/server/models/decorators/Deprecated.ts b/server/models/decorators/Deprecated.ts index b4a899549b..92b8a363ef 100644 --- a/server/models/decorators/Deprecated.ts +++ b/server/models/decorators/Deprecated.ts @@ -1,15 +1,13 @@ /* eslint-disable @typescript-eslint/ban-types */ -const Deprecated = (message?: string) => ( - target: Object, - propertyKey: string -) => { - if (process.env[propertyKey]) { - // eslint-disable-next-line no-console - console.warn( - `The environment variable ${propertyKey} is deprecated and will be removed in a future release. ${message}` - ); - } -}; +const Deprecated = + (message?: string) => (target: Object, propertyKey: string) => { + if (process.env[propertyKey]) { + // eslint-disable-next-line no-console + console.warn( + `The environment variable ${propertyKey} is deprecated and will be removed in a future release. ${message}` + ); + } + }; export default Deprecated; diff --git a/server/queues/tasks/CollectionCreatedNotificationsTask.ts b/server/queues/tasks/CollectionCreatedNotificationsTask.ts index 7542b9faa6..ff506d112e 100644 --- a/server/queues/tasks/CollectionCreatedNotificationsTask.ts +++ b/server/queues/tasks/CollectionCreatedNotificationsTask.ts @@ -4,9 +4,7 @@ import NotificationHelper from "@server/models/helpers/NotificationHelper"; import { CollectionEvent } from "@server/types"; import BaseTask, { TaskPriority } from "./BaseTask"; -export default class CollectionCreatedNotificationsTask extends BaseTask< - CollectionEvent -> { +export default class CollectionCreatedNotificationsTask extends BaseTask { public async perform(event: CollectionEvent) { const collection = await Collection.findByPk(event.collectionId); @@ -15,10 +13,11 @@ export default class CollectionCreatedNotificationsTask extends BaseTask< return; } - const recipients = await NotificationHelper.getCollectionNotificationRecipients( - collection, - NotificationEventType.CreateCollection - ); + const recipients = + await NotificationHelper.getCollectionNotificationRecipients( + collection, + NotificationEventType.CreateCollection + ); for (const recipient of recipients) { // Suppress notifications for suspended users diff --git a/server/queues/tasks/CommentCreatedNotificationsTask.ts b/server/queues/tasks/CommentCreatedNotificationsTask.ts index 1bcbae674e..6a4ebc88c0 100644 --- a/server/queues/tasks/CommentCreatedNotificationsTask.ts +++ b/server/queues/tasks/CommentCreatedNotificationsTask.ts @@ -7,9 +7,7 @@ import ProsemirrorHelper from "@server/models/helpers/ProsemirrorHelper"; import { CommentEvent } from "@server/types"; import BaseTask, { TaskPriority } from "./BaseTask"; -export default class CommentCreatedNotificationsTask extends BaseTask< - CommentEvent -> { +export default class CommentCreatedNotificationsTask extends BaseTask { public async perform(event: CommentEvent) { const [document, comment] = await Promise.all([ Document.scope("withCollection").findOne({ diff --git a/server/queues/tasks/CommentUpdatedNotificationsTask.ts b/server/queues/tasks/CommentUpdatedNotificationsTask.ts index 7853627d99..85acf21b06 100644 --- a/server/queues/tasks/CommentUpdatedNotificationsTask.ts +++ b/server/queues/tasks/CommentUpdatedNotificationsTask.ts @@ -4,9 +4,7 @@ import ProsemirrorHelper from "@server/models/helpers/ProsemirrorHelper"; import { CommentEvent, CommentUpdateEvent } from "@server/types"; import BaseTask, { TaskPriority } from "./BaseTask"; -export default class CommentUpdatedNotificationsTask extends BaseTask< - CommentEvent -> { +export default class CommentUpdatedNotificationsTask extends BaseTask { public async perform(event: CommentUpdateEvent) { const [document, comment] = await Promise.all([ Document.scope("withCollection").findOne({ diff --git a/server/queues/tasks/DocumentPublishedNotificationsTask.ts b/server/queues/tasks/DocumentPublishedNotificationsTask.ts index d27b66bb20..35486bb4c6 100644 --- a/server/queues/tasks/DocumentPublishedNotificationsTask.ts +++ b/server/queues/tasks/DocumentPublishedNotificationsTask.ts @@ -6,9 +6,7 @@ import NotificationHelper from "@server/models/helpers/NotificationHelper"; import { DocumentEvent } from "@server/types"; import BaseTask, { TaskPriority } from "./BaseTask"; -export default class DocumentPublishedNotificationsTask extends BaseTask< - DocumentEvent -> { +export default class DocumentPublishedNotificationsTask extends BaseTask { public async perform(event: DocumentEvent) { const document = await Document.findByPk(event.documentId, { includeState: true, diff --git a/server/queues/tasks/ImportNotionTask.ts b/server/queues/tasks/ImportNotionTask.ts index dfb099eb35..ada3e2d659 100644 --- a/server/queues/tasks/ImportNotionTask.ts +++ b/server/queues/tasks/ImportNotionTask.ts @@ -287,7 +287,8 @@ export default class ImportNotionTask extends ImportTask { /** * Regex to find markdown images of all types */ - private ImageRegex = /!\[(?[^\][]*?)]\((?[^\][]*?)(?=“|\))“?(?[^\][”]+)?”?\)/g; + private ImageRegex = + /!\[(?<alt>[^\][]*?)]\((?<filename>[^\][]*?)(?=“|\))“?(?<title>[^\][”]+)?”?\)/g; /** * Regex to find markdown links containing ID's that look like UUID's with the @@ -298,5 +299,6 @@ export default class ImportNotionTask extends ImportTask { /** * Regex to find Notion document UUID's in the title of a document. */ - private NotionUUIDRegex = /\s([0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}|[0-9a-fA-F]{32})$/; + private NotionUUIDRegex = + /\s([0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}|[0-9a-fA-F]{32})$/; } diff --git a/server/queues/tasks/RevisionCreatedNotificationsTask.ts b/server/queues/tasks/RevisionCreatedNotificationsTask.ts index fedf58cfd9..6377a815d3 100644 --- a/server/queues/tasks/RevisionCreatedNotificationsTask.ts +++ b/server/queues/tasks/RevisionCreatedNotificationsTask.ts @@ -11,9 +11,7 @@ import NotificationHelper from "@server/models/helpers/NotificationHelper"; import { RevisionEvent } from "@server/types"; import BaseTask, { TaskPriority } from "./BaseTask"; -export default class RevisionCreatedNotificationsTask extends BaseTask< - RevisionEvent -> { +export default class RevisionCreatedNotificationsTask extends BaseTask<RevisionEvent> { public async perform(event: RevisionEvent) { const [document, revision] = await Promise.all([ Document.findByPk(event.documentId, { includeState: true }), diff --git a/server/routes/api/auth.ts b/server/routes/api/auth.ts index 2696b39ef7..add39dabe0 100644 --- a/server/routes/api/auth.ts +++ b/server/routes/api/auth.ts @@ -133,14 +133,12 @@ router.post("auth.info", auth(), async (ctx: APIContext) => { includeDetails: true, }), team: presentTeam(team), - availableTeams: uniqBy( - [...signedInTeams, ...availableTeams], - "id" - ).map((team) => - presentAvailableTeam( - team, - signedInTeamIds.includes(team.id) || team.id === user.teamId - ) + availableTeams: uniqBy([...signedInTeams, ...availableTeams], "id").map( + (team) => + presentAvailableTeam( + team, + signedInTeamIds.includes(team.id) || team.id === user.teamId + ) ), }, policies: presentPolicies(user, [team]), diff --git a/server/routes/api/collections.ts b/server/routes/api/collections.ts index 5ffe88669a..16ef9b33f4 100644 --- a/server/routes/api/collections.ts +++ b/server/routes/api/collections.ts @@ -168,10 +168,8 @@ router.post( rateLimiter(RateLimiterStrategy.TenPerHour), auth(), async (ctx: APIContext) => { - const { - attachmentId, - format = FileOperationFormat.MarkdownZip, - } = ctx.request.body; + const { attachmentId, format = FileOperationFormat.MarkdownZip } = + ctx.request.body; assertUuid(attachmentId, "attachmentId is required"); const { user } = ctx.state.auth; @@ -630,16 +628,8 @@ router.post( ); router.post("collections.update", auth(), async (ctx: APIContext) => { - const { - id, - name, - description, - icon, - permission, - color, - sort, - sharing, - } = ctx.request.body; + const { id, name, description, icon, permission, color, sort, sharing } = + ctx.request.body; if (color) { assertHexColor(color, "Invalid hex value (please use format #FFFFFF)"); diff --git a/server/routes/api/documents/documents.ts b/server/routes/api/documents/documents.ts index 403f7e2313..7b4a63f784 100644 --- a/server/routes/api/documents/documents.ts +++ b/server/routes/api/documents/documents.ts @@ -995,21 +995,18 @@ router.post( } } - const { - documents, - collections, - collectionChanged, - } = await sequelize.transaction(async (transaction) => - documentMover({ - user, - document, - collectionId, - parentDocumentId, - index, - ip: ctx.request.ip, - transaction, - }) - ); + const { documents, collections, collectionChanged } = + await sequelize.transaction(async (transaction) => + documentMover({ + user, + document, + collectionId, + parentDocumentId, + index, + ip: ctx.request.ip, + transaction, + }) + ); ctx.body = { data: { diff --git a/server/services/collaboration.ts b/server/services/collaboration.ts index 271d2ba8ff..e407638329 100644 --- a/server/services/collaboration.ts +++ b/server/services/collaboration.ts @@ -39,50 +39,49 @@ export default function init( ], }); - server.on("upgrade", function ( - req: IncomingMessage, - socket: Duplex, - head: Buffer - ) { - if (req.url?.startsWith(path)) { - // parse document id and close connection if not present in request - const documentId = url - .parse(req.url) - .pathname?.replace(path, "") - .split("/") - .pop(); + server.on( + "upgrade", + function (req: IncomingMessage, socket: Duplex, head: Buffer) { + if (req.url?.startsWith(path)) { + // parse document id and close connection if not present in request + const documentId = url + .parse(req.url) + .pathname?.replace(path, "") + .split("/") + .pop(); - if (documentId) { - wss.handleUpgrade(req, socket, head, (client) => { - // Handle websocket connection errors as soon as the client is upgraded - client.on("error", (error) => { - Logger.error( - `Websocket error`, - error, - { - documentId, - }, - req - ); + if (documentId) { + wss.handleUpgrade(req, socket, head, (client) => { + // Handle websocket connection errors as soon as the client is upgraded + client.on("error", (error) => { + Logger.error( + `Websocket error`, + error, + { + documentId, + }, + req + ); + }); + + hocuspocus.handleConnection(client, req, documentId); }); + return; + } + } - hocuspocus.handleConnection(client, req, documentId); - }); + if ( + req.url?.startsWith("/realtime") && + serviceNames.includes("websockets") + ) { + // Nothing to do, the websockets service will handle this request return; } - } - if ( - req.url?.startsWith("/realtime") && - serviceNames.includes("websockets") - ) { - // Nothing to do, the websockets service will handle this request - return; + // If the collaboration service is running it will close the connection + socket.end(`HTTP/1.1 400 Bad Request\r\n`); } - - // If the collaboration service is running it will close the connection - socket.end(`HTTP/1.1 400 Bad Request\r\n`); - }); + ); ShutdownHelper.add("collaboration", ShutdownOrder.normal, () => hocuspocus.destroy() diff --git a/server/services/websockets.ts b/server/services/websockets.ts index 1619a2dd32..23180cd51a 100644 --- a/server/services/websockets.ts +++ b/server/services/websockets.ts @@ -53,25 +53,24 @@ export default function init( ); } - server.on("upgrade", function ( - req: IncomingMessage, - socket: Duplex, - head: Buffer - ) { - if (req.url?.startsWith(path)) { - invariant(ioHandleUpgrade, "Existing upgrade handler must exist"); - ioHandleUpgrade(req, socket, head); - return; - } + server.on( + "upgrade", + function (req: IncomingMessage, socket: Duplex, head: Buffer) { + if (req.url?.startsWith(path)) { + invariant(ioHandleUpgrade, "Existing upgrade handler must exist"); + ioHandleUpgrade(req, socket, head); + return; + } - if (serviceNames.includes("collaboration")) { - // Nothing to do, the collaboration service will handle this request - return; - } + if (serviceNames.includes("collaboration")) { + // Nothing to do, the collaboration service will handle this request + return; + } - // If the collaboration service isn't running then we need to close the connection - socket.end(`HTTP/1.1 400 Bad Request\r\n`); - }); + // If the collaboration service isn't running then we need to close the connection + socket.end(`HTTP/1.1 400 Bad Request\r\n`); + } + ); ShutdownHelper.add("websockets", ShutdownOrder.normal, async () => { Metrics.gaugePerInstance("websockets.count", 0); diff --git a/server/utils/parseAttachmentIds.test.ts b/server/utils/parseAttachmentIds.test.ts index acd4907604..041c780901 100644 --- a/server/utils/parseAttachmentIds.test.ts +++ b/server/utils/parseAttachmentIds.test.ts @@ -55,7 +55,8 @@ it("should parse attachment ID from markdown with title", () => { it("should parse multiple attachment IDs from markdown", () => { const uuid = uuidv4(); const uuid2 = uuidv4(); - const results = parseAttachmentIds(`![caption text](/api/attachments.redirect?id=${uuid}) + const results = + parseAttachmentIds(`![caption text](/api/attachments.redirect?id=${uuid}) some text diff --git a/server/utils/parseAttachmentIds.ts b/server/utils/parseAttachmentIds.ts index b646ea0e39..a97871d006 100644 --- a/server/utils/parseAttachmentIds.ts +++ b/server/utils/parseAttachmentIds.ts @@ -1,7 +1,9 @@ import { uniq, compact } from "lodash"; -const attachmentRedirectRegex = /\/api\/attachments\.redirect\?id=(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi; -const attachmentPublicRegex = /public\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi; +const attachmentRedirectRegex = + /\/api\/attachments\.redirect\?id=(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi; +const attachmentPublicRegex = + /public\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi; export default function parseAttachmentIds( text: string, diff --git a/server/utils/updates.ts b/server/utils/updates.ts index ea1435dc61..1ef9558f47 100644 --- a/server/utils/updates.ts +++ b/server/utils/updates.ts @@ -14,17 +14,13 @@ const UPDATES_KEY = "UPDATES_KEY"; export async function checkUpdates() { const secret = env.SECRET_KEY.slice(0, 6) + env.URL; const id = crypto.createHash("sha256").update(secret).digest("hex"); - const [ - userCount, - teamCount, - collectionCount, - documentCount, - ] = await Promise.all([ - User.count(), - Team.count(), - Collection.count(), - Document.count(), - ]); + const [userCount, teamCount, collectionCount, documentCount] = + await Promise.all([ + User.count(), + Team.count(), + Collection.count(), + Document.count(), + ]); const body = JSON.stringify({ id, version: 1, diff --git a/server/utils/validators.ts b/server/utils/validators.ts index f15787e8cc..4b85c542cd 100644 --- a/server/utils/validators.ts +++ b/server/utils/validators.ts @@ -18,7 +18,7 @@ export function CannotUseWithout( options: validationOptions, validator: { validate<T>(value: T, args: ValidationArguments) { - const object = (args.object as unknown) as T; + const object = args.object as unknown as T; const required = args.constraints[0] as string; return object[required] !== undefined; }, diff --git a/shared/editor/commands/codeFence.ts b/shared/editor/commands/codeFence.ts index a31478edde..2b46d91e64 100644 --- a/shared/editor/commands/codeFence.ts +++ b/shared/editor/commands/codeFence.ts @@ -96,9 +96,8 @@ export const newlineInCode = (state: EditorState, dispatch: Dispatch) => { if (text) { const splitByNewLine = text.split("\n"); - const numOfSpaces = splitByNewLine[splitByNewLine.length - 1].search( - /\S|$/ - ); + const numOfSpaces = + splitByNewLine[splitByNewLine.length - 1].search(/\S|$/); newText += " ".repeat(numOfSpaces); } diff --git a/shared/editor/components/Image.tsx b/shared/editor/components/Image.tsx index d5a0282578..c63453abf9 100644 --- a/shared/editor/components/Image.tsx +++ b/shared/editor/components/Image.tsx @@ -100,18 +100,18 @@ const Image = ( document.removeEventListener("mousemove", handlePointerMove); }; - const handlePointerDown = (dragging: "left" | "right") => ( - event: React.PointerEvent<HTMLDivElement> - ) => { - event.preventDefault(); - event.stopPropagation(); - setSizeAtDragStart({ - width: constrainWidth(size.width), - height: size.height, - }); - setOffset(event.pageX); - setDragging(dragging); - }; + const handlePointerDown = + (dragging: "left" | "right") => + (event: React.PointerEvent<HTMLDivElement>) => { + event.preventDefault(); + event.stopPropagation(); + setSizeAtDragStart({ + width: constrainWidth(size.width), + height: size.height, + }); + setOffset(event.pageX); + setDragging(dragging); + }; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { @@ -170,11 +170,14 @@ const Image = ( onClick={dragging ? undefined : props.onClick} style={widthStyle} > - {!dragging && size.width > 60 && size.height > 60 && props.onDownload && ( - <Button onClick={props.onDownload}> - <DownloadIcon /> - </Button> - )} + {!dragging && + size.width > 60 && + size.height > 60 && + props.onDownload && ( + <Button onClick={props.onDownload}> + <DownloadIcon /> + </Button> + )} <ImageZoom zoomMargin={24}> <img style={{ diff --git a/shared/editor/components/useComponentSize.ts b/shared/editor/components/useComponentSize.ts index 24d929ccc0..69e857b5d1 100644 --- a/shared/editor/components/useComponentSize.ts +++ b/shared/editor/components/useComponentSize.ts @@ -1,8 +1,9 @@ import { useState, useEffect } from "react"; -export default function useComponentSize( - ref: React.RefObject<HTMLElement> -): { width: number; height: number } { +export default function useComponentSize(ref: React.RefObject<HTMLElement>): { + width: number; + height: number; +} { const [size, setSize] = useState({ width: ref.current?.clientWidth || 0, height: ref.current?.clientHeight || 0, diff --git a/shared/editor/embeds/Bilibili.tsx b/shared/editor/embeds/Bilibili.tsx index ba78676366..ac9d921173 100644 --- a/shared/editor/embeds/Bilibili.tsx +++ b/shared/editor/embeds/Bilibili.tsx @@ -2,7 +2,8 @@ import * as React from "react"; import Frame from "../components/Frame"; import { EmbedProps as Props } from "."; -const URL_REGEX = /(?:https?:\/\/)?(www\.bilibili\.com)\/video\/([\w\d]+)?(\?\S+)?/i; +const URL_REGEX = + /(?:https?:\/\/)?(www\.bilibili\.com)\/video\/([\w\d]+)?(\?\S+)?/i; export default function Bilibili(props: Props) { const { matches } = props.attrs; diff --git a/shared/editor/embeds/InVision.tsx b/shared/editor/embeds/InVision.tsx index a5e259fbdc..92e2c33b7c 100644 --- a/shared/editor/embeds/InVision.tsx +++ b/shared/editor/embeds/InVision.tsx @@ -3,8 +3,10 @@ import Frame from "../components/Frame"; import ImageZoom from "../components/ImageZoom"; import { EmbedProps as Props } from "."; -const IFRAME_REGEX = /^https:\/\/(invis\.io\/.*)|(projects\.invisionapp\.com\/share\/.*)$/; -const IMAGE_REGEX = /^https:\/\/(opal\.invisionapp\.com\/static-signed\/live-embed\/.*)$/; +const IFRAME_REGEX = + /^https:\/\/(invis\.io\/.*)|(projects\.invisionapp\.com\/share\/.*)$/; +const IMAGE_REGEX = + /^https:\/\/(opal\.invisionapp\.com\/static-signed\/live-embed\/.*)$/; function InVision(props: Props) { if (IMAGE_REGEX.test(props.attrs.href)) { diff --git a/shared/editor/extensions/Mermaid.ts b/shared/editor/extensions/Mermaid.ts index 34b57400a1..f181a56fe3 100644 --- a/shared/editor/extensions/Mermaid.ts +++ b/shared/editor/extensions/Mermaid.ts @@ -164,9 +164,8 @@ export default function Mermaid({ } if (diagramToggled) { - pluginState.diagramVisibility[ - mermaidMeta.toggleDiagram - ] = !pluginState.diagramVisibility[mermaidMeta.toggleDiagram]; + pluginState.diagramVisibility[mermaidMeta.toggleDiagram] = + !pluginState.diagramVisibility[mermaidMeta.toggleDiagram]; } if ( diff --git a/shared/editor/nodes/Embed.tsx b/shared/editor/nodes/Embed.tsx index e63ae58b63..f8b3bf9a40 100644 --- a/shared/editor/nodes/Embed.tsx +++ b/shared/editor/nodes/Embed.tsx @@ -114,15 +114,13 @@ export default class Embed extends Node { } commands({ type }: { type: NodeType }) { - return (attrs: Record<string, any>) => ( - state: EditorState, - dispatch: Dispatch - ) => { - dispatch( - state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView() - ); - return true; - }; + return (attrs: Record<string, any>) => + (state: EditorState, dispatch: Dispatch) => { + dispatch( + state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView() + ); + return true; + }; } toMarkdown(state: MarkdownSerializerState, node: ProsemirrorNode) { diff --git a/shared/editor/nodes/Emoji.tsx b/shared/editor/nodes/Emoji.tsx index 3d964665f2..18ad791e1e 100644 --- a/shared/editor/nodes/Emoji.tsx +++ b/shared/editor/nodes/Emoji.tsx @@ -22,7 +22,8 @@ export default class Emoji extends Suggestion { return { type: SuggestionsMenuType.Emoji, openRegex: /(?:^|\s):([0-9a-zA-Z_+-]+)?$/, - closeRegex: /(?:^|\s):(([0-9a-zA-Z_+-]*\s+)|(\s+[0-9a-zA-Z_+-]+)|[^0-9a-zA-Z_+-]+)$/, + closeRegex: + /(?:^|\s):(([0-9a-zA-Z_+-]*\s+)|(\s+[0-9a-zA-Z_+-]+)|[^0-9a-zA-Z_+-]+)$/, enabledInTable: true, }; } @@ -77,24 +78,22 @@ export default class Emoji extends Suggestion { } commands({ type }: { type: NodeType; schema: Schema }) { - return (attrs: Record<string, string>) => ( - state: EditorState, - dispatch: Dispatch - ) => { - const { selection } = state; - const position = - selection instanceof TextSelection - ? selection.$cursor?.pos - : selection.$to.pos; - if (position === undefined) { - return false; - } + return (attrs: Record<string, string>) => + (state: EditorState, dispatch: Dispatch) => { + const { selection } = state; + const position = + selection instanceof TextSelection + ? selection.$cursor?.pos + : selection.$to.pos; + if (position === undefined) { + return false; + } - const node = type.create(attrs); - const transaction = state.tr.insert(position, node); - dispatch(transaction); - return true; - }; + const node = type.create(attrs); + const transaction = state.tr.insert(position, node); + dispatch(transaction); + return true; + }; } toMarkdown(state: MarkdownSerializerState, node: ProsemirrorNode) { diff --git a/shared/editor/nodes/HorizontalRule.ts b/shared/editor/nodes/HorizontalRule.ts index f23989bb8e..fe1d907f5c 100644 --- a/shared/editor/nodes/HorizontalRule.ts +++ b/shared/editor/nodes/HorizontalRule.ts @@ -28,15 +28,13 @@ export default class HorizontalRule extends Node { } commands({ type }: { type: NodeType }) { - return (attrs: Record<string, any>) => ( - state: EditorState, - dispatch: Dispatch - ) => { - dispatch( - state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView() - ); - return true; - }; + return (attrs: Record<string, any>) => + (state: EditorState, dispatch: Dispatch) => { + dispatch( + state.tr.replaceSelectionWith(type.create(attrs)).scrollIntoView() + ); + return true; + }; } keys({ type }: { type: NodeType }) { diff --git a/shared/editor/nodes/Image.tsx b/shared/editor/nodes/Image.tsx index 82a252abae..798db3135a 100644 --- a/shared/editor/nodes/Image.tsx +++ b/shared/editor/nodes/Image.tsx @@ -189,35 +189,31 @@ export default class Image extends SimpleImage { ]; } - handleChangeSize = ({ - node, - getPos, - }: { - node: ProsemirrorNode; - getPos: () => number; - }) => ({ width, height }: { width: number; height?: number }) => { - const { view } = this.editor; - const { tr } = view.state; + handleChangeSize = + ({ node, getPos }: { node: ProsemirrorNode; getPos: () => number }) => + ({ width, height }: { width: number; height?: number }) => { + const { view } = this.editor; + const { tr } = view.state; - const pos = getPos(); - const transaction = tr - .setNodeMarkup(pos, undefined, { - ...node.attrs, - width, - height, - }) - .setMeta("addToHistory", true); - const $pos = transaction.doc.resolve(getPos()); - view.dispatch(transaction.setSelection(new NodeSelection($pos))); - }; + const pos = getPos(); + const transaction = tr + .setNodeMarkup(pos, undefined, { + ...node.attrs, + width, + height, + }) + .setMeta("addToHistory", true); + const $pos = transaction.doc.resolve(getPos()); + view.dispatch(transaction.setSelection(new NodeSelection($pos))); + }; - handleDownload = ({ node }: { node: ProsemirrorNode }) => ( - event: React.MouseEvent - ) => { - event.preventDefault(); - event.stopPropagation(); - downloadImageNode(node); - }; + handleDownload = + ({ node }: { node: ProsemirrorNode }) => + (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + downloadImageNode(node); + }; component = (props: ComponentProps) => ( <ImageComponent @@ -360,7 +356,8 @@ export default class Image extends SimpleImage { * ![](image.jpg "class") -> [, "", "image.jpg", "small"] * ![Lorem](image.jpg "class") -> [, "Lorem", "image.jpg", "small"] */ - const IMAGE_INPUT_REGEX = /!\[(?<alt>[^\][]*?)]\((?<filename>[^\][]*?)(?=“|\))“?(?<layoutclass>[^\][”]+)?”?\)$/; + const IMAGE_INPUT_REGEX = + /!\[(?<alt>[^\][]*?)]\((?<filename>[^\][]*?)(?=“|\))“?(?<layoutclass>[^\][”]+)?”?\)$/; return [ new InputRule(IMAGE_INPUT_REGEX, (state, match, start, end) => { diff --git a/shared/editor/nodes/Mention.ts b/shared/editor/nodes/Mention.ts index 72d9d2a7e7..71ebf71d37 100644 --- a/shared/editor/nodes/Mention.ts +++ b/shared/editor/nodes/Mention.ts @@ -80,24 +80,22 @@ export default class Mention extends Suggestion { } commands({ type }: { type: NodeType; schema: Schema }) { - return (attrs: Record<string, string>) => ( - state: EditorState, - dispatch: Dispatch - ) => { - const { selection } = state; - const position = - selection instanceof TextSelection - ? selection.$cursor?.pos - : selection.$to.pos; - if (position === undefined) { - return false; - } + return (attrs: Record<string, string>) => + (state: EditorState, dispatch: Dispatch) => { + const { selection } = state; + const position = + selection instanceof TextSelection + ? selection.$cursor?.pos + : selection.$to.pos; + if (position === undefined) { + return false; + } - const node = type.create(attrs); - const transaction = state.tr.insert(position, node); - dispatch(transaction); - return true; - }; + const node = type.create(attrs); + const transaction = state.tr.insert(position, node); + dispatch(transaction); + return true; + }; } toMarkdown(state: MarkdownSerializerState, node: ProsemirrorNode) { diff --git a/shared/editor/nodes/SimpleImage.tsx b/shared/editor/nodes/SimpleImage.tsx index 4f956700d8..e9038513de 100644 --- a/shared/editor/nodes/SimpleImage.tsx +++ b/shared/editor/nodes/SimpleImage.tsx @@ -75,73 +75,65 @@ export default class SimpleImage extends Node { }; } - handleKeyDown = ({ - node, - getPos, - }: { - node: ProsemirrorNode; - getPos: () => number; - }) => (event: React.KeyboardEvent<HTMLSpanElement>) => { - // Pressing Enter in the caption field should move the cursor/selection - // below the image - if (event.key === "Enter") { + handleKeyDown = + ({ node, getPos }: { node: ProsemirrorNode; getPos: () => number }) => + (event: React.KeyboardEvent<HTMLSpanElement>) => { + // Pressing Enter in the caption field should move the cursor/selection + // below the image + if (event.key === "Enter") { + event.preventDefault(); + + const { view } = this.editor; + const $pos = view.state.doc.resolve(getPos() + node.nodeSize); + view.dispatch( + view.state.tr.setSelection(new TextSelection($pos)).split($pos.pos) + ); + view.focus(); + return; + } + + // Pressing Backspace in an an empty caption field should remove the entire + // image, leaving an empty paragraph + if (event.key === "Backspace" && event.currentTarget.innerText === "") { + const { view } = this.editor; + const $pos = view.state.doc.resolve(getPos()); + const tr = view.state.tr.setSelection(new NodeSelection($pos)); + view.dispatch(tr.deleteSelection()); + view.focus(); + return; + } + }; + + handleBlur = + ({ node, getPos }: { node: ProsemirrorNode; getPos: () => number }) => + (event: React.FocusEvent<HTMLSpanElement>) => { + const caption = event.currentTarget.innerText; + if (caption === node.attrs.alt) { + return; + } + + const { view } = this.editor; + const { tr } = view.state; + + // update meta on object + const pos = getPos(); + const transaction = tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + alt: caption, + }); + view.dispatch(transaction); + }; + + handleSelect = + ({ getPos }: { getPos: () => number }) => + (event: React.MouseEvent) => { event.preventDefault(); - const { view } = this.editor; - const $pos = view.state.doc.resolve(getPos() + node.nodeSize); - view.dispatch( - view.state.tr.setSelection(new TextSelection($pos)).split($pos.pos) - ); - view.focus(); - return; - } - - // Pressing Backspace in an an empty caption field should remove the entire - // image, leaving an empty paragraph - if (event.key === "Backspace" && event.currentTarget.innerText === "") { const { view } = this.editor; const $pos = view.state.doc.resolve(getPos()); - const tr = view.state.tr.setSelection(new NodeSelection($pos)); - view.dispatch(tr.deleteSelection()); - view.focus(); - return; - } - }; - - handleBlur = ({ - node, - getPos, - }: { - node: ProsemirrorNode; - getPos: () => number; - }) => (event: React.FocusEvent<HTMLSpanElement>) => { - const caption = event.currentTarget.innerText; - if (caption === node.attrs.alt) { - return; - } - - const { view } = this.editor; - const { tr } = view.state; - - // update meta on object - const pos = getPos(); - const transaction = tr.setNodeMarkup(pos, undefined, { - ...node.attrs, - alt: caption, - }); - view.dispatch(transaction); - }; - - handleSelect = ({ getPos }: { getPos: () => number }) => ( - event: React.MouseEvent - ) => { - event.preventDefault(); - - const { view } = this.editor; - const $pos = view.state.doc.resolve(getPos()); - const transaction = view.state.tr.setSelection(new NodeSelection($pos)); - view.dispatch(transaction); - }; + const transaction = view.state.tr.setSelection(new NodeSelection($pos)); + view.dispatch(transaction); + }; handleMouseDown = (ev: React.MouseEvent<HTMLParagraphElement>) => { if (document.activeElement !== ev.currentTarget) { @@ -189,12 +181,8 @@ export default class SimpleImage extends Node { } const { view } = this.editor; const { node } = state.selection; - const { - uploadFile, - onFileUploadStart, - onFileUploadStop, - onShowToast, - } = this.editor.props; + const { uploadFile, onFileUploadStart, onFileUploadStop, onShowToast } = + this.editor.props; if (!uploadFile) { throw new Error("uploadFile prop is required to replace images"); @@ -225,24 +213,23 @@ export default class SimpleImage extends Node { inputElement.click(); return true; }, - createImage: (attrs: Record<string, any>) => ( - state: EditorState, - dispatch: Dispatch - ) => { - const { selection } = state; - const position = - selection instanceof TextSelection - ? selection.$cursor?.pos - : selection.$to.pos; - if (position === undefined) { - return false; - } + createImage: + (attrs: Record<string, any>) => + (state: EditorState, dispatch: Dispatch) => { + const { selection } = state; + const position = + selection instanceof TextSelection + ? selection.$cursor?.pos + : selection.$to.pos; + if (position === undefined) { + return false; + } - const node = type.create(attrs); - const transaction = state.tr.insert(position, node); - dispatch(transaction); - return true; - }, + const node = type.create(attrs); + const transaction = state.tr.insert(position, node); + dispatch(transaction); + return true; + }, }; } @@ -255,7 +242,8 @@ export default class SimpleImage extends Node { * ![](image.jpg "class") -> [, "", "image.jpg", "small"] * ![Lorem](image.jpg "class") -> [, "Lorem", "image.jpg", "small"] */ - const IMAGE_INPUT_REGEX = /!\[(?<alt>[^\][]*?)]\((?<filename>[^\][]*?)(?=“|\))“?(?<layoutclass>[^\][”]+)?”?\)$/; + const IMAGE_INPUT_REGEX = + /!\[(?<alt>[^\][]*?)]\((?<filename>[^\][]*?)(?=“|\))“?(?<layoutclass>[^\][”]+)?”?\)$/; return [ new InputRule(IMAGE_INPUT_REGEX, (state, match, start, end) => { diff --git a/shared/editor/nodes/Table.ts b/shared/editor/nodes/Table.ts index 797e1abc55..9912010071 100644 --- a/shared/editor/nodes/Table.ts +++ b/shared/editor/nodes/Table.ts @@ -60,56 +60,47 @@ export default class Table extends Node { commands({ schema }: { schema: Schema }) { return { - createTable: ({ - rowsCount, - colsCount, - }: { - rowsCount: number; - colsCount: number; - }) => (state: EditorState, dispatch: Dispatch) => { - const offset = state.tr.selection.anchor + 1; - const nodes = createTable(schema, rowsCount, colsCount); - const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView(); - const resolvedPos = tr.doc.resolve(offset); + createTable: + ({ rowsCount, colsCount }: { rowsCount: number; colsCount: number }) => + (state: EditorState, dispatch: Dispatch) => { + const offset = state.tr.selection.anchor + 1; + const nodes = createTable(schema, rowsCount, colsCount); + const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView(); + const resolvedPos = tr.doc.resolve(offset); - tr.setSelection(TextSelection.near(resolvedPos)); - dispatch(tr); - return true; - }, - setColumnAttr: ({ - index, - alignment, - }: { - index: number; - alignment: string; - }) => (state: EditorState, dispatch: Dispatch) => { - const cells = getCellsInColumn(index)(state.selection) || []; - let transaction = state.tr; - cells.forEach(({ pos }) => { - transaction = transaction.setNodeMarkup(pos, undefined, { - alignment, + tr.setSelection(TextSelection.near(resolvedPos)); + dispatch(tr); + return true; + }, + setColumnAttr: + ({ index, alignment }: { index: number; alignment: string }) => + (state: EditorState, dispatch: Dispatch) => { + const cells = getCellsInColumn(index)(state.selection) || []; + let transaction = state.tr; + cells.forEach(({ pos }) => { + transaction = transaction.setNodeMarkup(pos, undefined, { + alignment, + }); }); - }); - dispatch(transaction); - return true; - }, + dispatch(transaction); + return true; + }, addColumnBefore: () => addColumnBefore, addColumnAfter: () => addColumnAfter, deleteColumn: () => deleteColumn, - addRowAfter: ({ index }: { index: number }) => ( - state: EditorState, - dispatch: Dispatch - ) => { - if (index === 0) { - // A little hack to avoid cloning the heading row by cloning the row - // beneath and then moving it to the right index. - const tr = addRowAt(index + 2, true)(state.tr); - dispatch(moveRow(index + 2, index + 1)(tr)); - } else { - dispatch(addRowAt(index + 1, true)(state.tr)); - } - return true; - }, + addRowAfter: + ({ index }: { index: number }) => + (state: EditorState, dispatch: Dispatch) => { + if (index === 0) { + // A little hack to avoid cloning the heading row by cloning the row + // beneath and then moving it to the right index. + const tr = addRowAt(index + 2, true)(state.tr); + dispatch(moveRow(index + 2, index + 1)(tr)); + } else { + dispatch(addRowAt(index + 1, true)(state.tr)); + } + return true; + }, deleteRow: () => deleteRow, deleteTable: () => deleteTable, toggleHeaderColumn: () => toggleHeaderColumn, @@ -127,7 +118,7 @@ export default class Table extends Node { return false; } const index = getRowIndexFromText( - (state.selection as unknown) as CellSelection + state.selection as unknown as CellSelection ); if (index === 0) { diff --git a/shared/editor/queries/isMarkActive.ts b/shared/editor/queries/isMarkActive.ts index 32df9d322b..01f69c0827 100644 --- a/shared/editor/queries/isMarkActive.ts +++ b/shared/editor/queries/isMarkActive.ts @@ -1,16 +1,18 @@ import { MarkType } from "prosemirror-model"; import { EditorState } from "prosemirror-state"; -const isMarkActive = (type: MarkType) => (state: EditorState): boolean => { - if (!type) { - return false; - } +const isMarkActive = + (type: MarkType) => + (state: EditorState): boolean => { + if (!type) { + return false; + } - const { from, $from, to, empty } = state.selection; + const { from, $from, to, empty } = state.selection; - return !!(empty - ? type.isInSet(state.storedMarks || $from.marks()) - : state.doc.rangeHasMark(from, to, type)); -}; + return !!(empty + ? type.isInSet(state.storedMarks || $from.marks()) + : state.doc.rangeHasMark(from, to, type)); + }; export default isMarkActive; diff --git a/shared/editor/queries/isNodeActive.ts b/shared/editor/queries/isNodeActive.ts index 3da1c24e4b..f7e93eee0a 100644 --- a/shared/editor/queries/isNodeActive.ts +++ b/shared/editor/queries/isNodeActive.ts @@ -2,22 +2,22 @@ import { NodeType } from "prosemirror-model"; import { EditorState } from "prosemirror-state"; import { findParentNode, findSelectedNodeOfType } from "prosemirror-utils"; -const isNodeActive = (type: NodeType, attrs: Record<string, any> = {}) => ( - state: EditorState -) => { - if (!type) { - return false; - } +const isNodeActive = + (type: NodeType, attrs: Record<string, any> = {}) => + (state: EditorState) => { + if (!type) { + return false; + } - const node = - findSelectedNodeOfType(type)(state.selection) || - findParentNode((node) => node.type === type)(state.selection); + const node = + findSelectedNodeOfType(type)(state.selection) || + findParentNode((node) => node.type === type)(state.selection); - if (!Object.keys(attrs).length || !node) { - return !!node; - } + if (!Object.keys(attrs).length || !node) { + return !!node; + } - return node.node.hasMarkup(type, { ...node.node.attrs, ...attrs }); -}; + return node.node.hasMarkup(type, { ...node.node.attrs, ...attrs }); + }; export default isNodeActive; diff --git a/shared/styles/index.ts b/shared/styles/index.ts index 495595e085..4b79ec5c02 100644 --- a/shared/styles/index.ts +++ b/shared/styles/index.ts @@ -20,9 +20,9 @@ export const ellipsis = () => ` * * @returns a theme value */ -export const s = (key: keyof DefaultTheme) => (props: { - theme: DefaultTheme; -}) => String(props.theme[key]); +export const s = + (key: keyof DefaultTheme) => (props: { theme: DefaultTheme }) => + String(props.theme[key]); /** * Mixin to hide scrollbars. diff --git a/yarn.lock b/yarn.lock index b2a2eb894b..2467d93e22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10568,10 +10568,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.0.5: - version "2.1.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5" - integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg== +prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-bytes@^5.3.0: version "5.6.0"