diff --git a/.eslintrc b/.eslintrc index ae13cce26..4357f6946 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,6 +3,7 @@ "parserOptions": { "sourceType": "module", "extraFileExtensions": [".json"], + "project": "./tsconfig.json", "ecmaFeatures": { "jsx": true } @@ -36,6 +37,14 @@ "component": true, "html": true }], + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/no-misused-promises": [ + "error", + { + "checksVoidReturn": false + } + ], "@typescript-eslint/no-unused-vars": [ "error", { diff --git a/app/actions/definitions/collections.tsx b/app/actions/definitions/collections.tsx index 6733ccecc..bc58398d8 100644 --- a/app/actions/definitions/collections.tsx +++ b/app/actions/definitions/collections.tsx @@ -124,13 +124,13 @@ export const starCollection = createAction({ stores.policies.abilities(activeCollectionId).star ); }, - perform: ({ activeCollectionId, stores }) => { + perform: async ({ activeCollectionId, stores }) => { if (!activeCollectionId) { return; } const collection = stores.collections.get(activeCollectionId); - collection?.star(); + await collection?.star(); }, }); @@ -150,13 +150,13 @@ export const unstarCollection = createAction({ stores.policies.abilities(activeCollectionId).unstar ); }, - perform: ({ activeCollectionId, stores }) => { + perform: async ({ activeCollectionId, stores }) => { if (!activeCollectionId) { return; } const collection = stores.collections.get(activeCollectionId); - collection?.unstar(); + await collection?.unstar(); }, }); diff --git a/app/actions/definitions/documents.tsx b/app/actions/definitions/documents.tsx index fe69dfa71..fe1037e80 100644 --- a/app/actions/definitions/documents.tsx +++ b/app/actions/definitions/documents.tsx @@ -98,13 +98,13 @@ export const starDocument = createAction({ !document?.isStarred && stores.policies.abilities(activeDocumentId).star ); }, - perform: ({ activeDocumentId, stores }) => { + perform: async ({ activeDocumentId, stores }) => { if (!activeDocumentId) { return; } const document = stores.documents.get(activeDocumentId); - document?.star(); + await document?.star(); }, }); @@ -124,13 +124,13 @@ export const unstarDocument = createAction({ stores.policies.abilities(activeDocumentId).unstar ); }, - perform: ({ activeDocumentId, stores }) => { + perform: async ({ activeDocumentId, stores }) => { if (!activeDocumentId) { return; } const document = stores.documents.get(activeDocumentId); - document?.unstar(); + await document?.unstar(); }, }); @@ -186,14 +186,14 @@ export const unpublishDocument = createAction({ } return stores.policies.abilities(activeDocumentId).unpublish; }, - perform: ({ activeDocumentId, stores, t }) => { + perform: async ({ activeDocumentId, stores, t }) => { if (!activeDocumentId) { return; } const document = stores.documents.get(activeDocumentId); - document?.unpublish(); + await document?.unpublish(); stores.toasts.showToast(t("Document unpublished"), { type: "success", @@ -218,14 +218,14 @@ export const subscribeDocument = createAction({ stores.policies.abilities(activeDocumentId).subscribe ); }, - perform: ({ activeDocumentId, stores, t }) => { + perform: async ({ activeDocumentId, stores, t }) => { if (!activeDocumentId) { return; } const document = stores.documents.get(activeDocumentId); - document?.subscribe(); + await document?.subscribe(); stores.toasts.showToast(t("Subscribed to document notifications"), { type: "success", @@ -250,14 +250,14 @@ export const unsubscribeDocument = createAction({ stores.policies.abilities(activeDocumentId).unsubscribe ); }, - perform: ({ activeDocumentId, stores, currentUserId, t }) => { + perform: async ({ activeDocumentId, stores, currentUserId, t }) => { if (!activeDocumentId || !currentUserId) { return; } const document = stores.documents.get(activeDocumentId); - document?.unsubscribe(currentUserId); + await document?.unsubscribe(currentUserId); stores.toasts.showToast(t("Unsubscribed from document notifications"), { type: "success", @@ -274,13 +274,13 @@ export const downloadDocumentAsHTML = createAction({ iconInContextMenu: false, visible: ({ activeDocumentId, stores }) => !!activeDocumentId && stores.policies.abilities(activeDocumentId).download, - perform: ({ activeDocumentId, stores }) => { + perform: async ({ activeDocumentId, stores }) => { if (!activeDocumentId) { return; } const document = stores.documents.get(activeDocumentId); - document?.download(ExportContentType.Html); + await document?.download(ExportContentType.Html); }, }); @@ -321,13 +321,13 @@ export const downloadDocumentAsMarkdown = createAction({ iconInContextMenu: false, visible: ({ activeDocumentId, stores }) => !!activeDocumentId && stores.policies.abilities(activeDocumentId).download, - perform: ({ activeDocumentId, stores }) => { + perform: async ({ activeDocumentId, stores }) => { if (!activeDocumentId) { return; } const document = stores.documents.get(activeDocumentId); - document?.download(ExportContentType.Markdown); + await document?.download(ExportContentType.Markdown); }, }); diff --git a/app/components/Authenticated.tsx b/app/components/Authenticated.tsx index ce0ab787f..2f148ec9e 100644 --- a/app/components/Authenticated.tsx +++ b/app/components/Authenticated.tsx @@ -18,7 +18,7 @@ const Authenticated = ({ children }: Props) => { // Watching for language changes here as this is the earliest point we have // the user available and means we can start loading translations faster React.useEffect(() => { - changeLanguage(language, i18n); + void changeLanguage(language, i18n); }, [i18n, language]); if (auth.authenticated) { @@ -31,7 +31,7 @@ const Authenticated = ({ children }: Props) => { return children; } - auth.logout(true); + void auth.logout(true); return ; }; diff --git a/app/components/Collaborators.tsx b/app/components/Collaborators.tsx index 59ed15993..7075ce1ae 100644 --- a/app/components/Collaborators.tsx +++ b/app/components/Collaborators.tsx @@ -57,7 +57,7 @@ function Collaborators(props: Props) { if (!isEqual(requestedUserIds, ids) && ids.length > 0) { setRequestedUserIds(ids); - users.fetchPage({ ids, limit: 100 }); + void users.fetchPage({ ids, limit: 100 }); } }, [document, users, presentIds, document.collaboratorIds, requestedUserIds]); diff --git a/app/components/CollectionDescription.tsx b/app/components/CollectionDescription.tsx index e26baa637..b5e98e2d9 100644 --- a/app/components/CollectionDescription.tsx +++ b/app/components/CollectionDescription.tsx @@ -71,9 +71,9 @@ function CollectionDescription({ collection }: Props) { ); const handleChange = React.useCallback( - (getValue) => { + async (getValue) => { setDirty(true); - handleSave(getValue); + await handleSave(getValue); }, [handleSave] ); diff --git a/app/components/ContextMenu/MenuItem.tsx b/app/components/ContextMenu/MenuItem.tsx index b58d33c8f..1136af981 100644 --- a/app/components/ContextMenu/MenuItem.tsx +++ b/app/components/ContextMenu/MenuItem.tsx @@ -38,13 +38,13 @@ const MenuItem = ( ref: React.Ref ) => { const handleClick = React.useCallback( - (ev) => { + async (ev) => { + hide?.(); + if (onClick) { ev.preventDefault(); - onClick(ev); + await onClick(ev); } - - hide?.(); }, [onClick, hide] ); diff --git a/app/components/DefaultCollectionInputSelect.tsx b/app/components/DefaultCollectionInputSelect.tsx index 9a9637eef..68afca659 100644 --- a/app/components/DefaultCollectionInputSelect.tsx +++ b/app/components/DefaultCollectionInputSelect.tsx @@ -28,7 +28,7 @@ const DefaultCollectionInputSelect = ({ const { showToast } = useToasts(); React.useEffect(() => { - async function load() { + async function fetchData() { if (!collections.isLoaded && !fetching && !fetchError) { try { setFetching(true); @@ -48,7 +48,7 @@ const DefaultCollectionInputSelect = ({ } } } - load(); + void fetchData(); }, [showToast, fetchError, t, fetching, collections]); const options = React.useMemo( diff --git a/app/components/DesktopEventHandler.tsx b/app/components/DesktopEventHandler.tsx index 2e4fec7b6..0bfeb0d3b 100644 --- a/app/components/DesktopEventHandler.tsx +++ b/app/components/DesktopEventHandler.tsx @@ -30,7 +30,7 @@ export default function DesktopEventHandler() { action: { text: "Install now", onClick: () => { - Desktop.bridge?.restartAndInstall(); + void Desktop.bridge?.restartAndInstall(); }, }, }); diff --git a/app/components/DocumentCard.tsx b/app/components/DocumentCard.tsx index 64e412bf1..1355fad5e 100644 --- a/app/components/DocumentCard.tsx +++ b/app/components/DocumentCard.tsx @@ -58,10 +58,10 @@ function DocumentCard(props: Props) { }; const handleUnpin = React.useCallback( - (ev) => { + async (ev) => { ev.preventDefault(); ev.stopPropagation(); - pin?.delete(); + await pin?.delete(); }, [pin] ); diff --git a/app/components/DocumentExplorer.tsx b/app/components/DocumentExplorer.tsx index cc5c44cb2..ae18b31c8 100644 --- a/app/components/DocumentExplorer.tsx +++ b/app/components/DocumentExplorer.tsx @@ -164,7 +164,7 @@ function DocumentExplorer({ onSubmit, onSelect, items }: Props) { (collection) => expandedNodes.includes(collection.id) || searchTerm ) .forEach((collection) => { - collection.fetchDocuments(); + void collection.fetchDocuments(); }); }, [collections, expandedNodes, searchTerm]); diff --git a/app/components/EventListItem.tsx b/app/components/EventListItem.tsx index 77b85c938..b82354e0a 100644 --- a/app/components/EventListItem.tsx +++ b/app/components/EventListItem.tsx @@ -51,9 +51,9 @@ const EventListItem = ({ event, latest, document, ...rest }: Props) => { ref.current?.focus(); }; - const prefetchRevision = () => { + const prefetchRevision = async () => { if (event.name === "revisions.create" && event.modelId) { - revisions.fetch(event.modelId); + await revisions.fetch(event.modelId); } }; diff --git a/app/components/HoverPreview.tsx b/app/components/HoverPreview.tsx index 2c7fbdbab..ca2a7dc6f 100644 --- a/app/components/HoverPreview.tsx +++ b/app/components/HoverPreview.tsx @@ -7,7 +7,6 @@ import parseDocumentSlug from "@shared/utils/parseDocumentSlug"; import { isExternalUrl } from "@shared/utils/urls"; import HoverPreviewDocument from "~/components/HoverPreviewDocument"; import useMobile from "~/hooks/useMobile"; -import useStores from "~/hooks/useStores"; import { fadeAndSlideDown } from "~/styles/animations"; const DELAY_OPEN = 300; @@ -23,7 +22,6 @@ type Props = { }; function HoverPreviewInternal({ element, id, onClose }: Props) { - const { documents } = useStores(); const slug = parseDocumentSlug(element.href); const [isVisible, setVisible] = React.useState(false); const timerClose = React.useRef>(); @@ -57,10 +55,6 @@ function HoverPreviewInternal({ element, id, onClose }: Props) { }; React.useEffect(() => { - if (slug) { - documents.prefetchDocument(slug); - } - startOpenTimer(); if (cardRef.current) { diff --git a/app/components/HoverPreviewDocument.tsx b/app/components/HoverPreviewDocument.tsx index 874a5cb1e..f85aad991 100644 --- a/app/components/HoverPreviewDocument.tsx +++ b/app/components/HoverPreviewDocument.tsx @@ -20,9 +20,11 @@ function HoverPreviewDocument({ url, id, children }: Props) { const { documents } = useStores(); const slug = parseDocumentSlug(url); - if (slug) { - documents.prefetchDocument(slug); - } + React.useEffect(() => { + if (slug) { + void documents.prefetchDocument(slug); + } + }, [documents, slug]); const document = slug ? documents.getByUrl(slug) : undefined; if (!document || document.id === id) { diff --git a/app/components/InputSelect.tsx b/app/components/InputSelect.tsx index 3833dad8c..d926ea04f 100644 --- a/app/components/InputSelect.tsx +++ b/app/components/InputSelect.tsx @@ -108,11 +108,7 @@ const InputSelect = (props: Props) => { } previousValue.current = select.selectedValue; - async function load() { - await onChange?.(select.selectedValue); - } - - load(); + onChange?.(select.selectedValue); }, [onChange, select.selectedValue]); React.useLayoutEffect(() => { diff --git a/app/components/LanguagePrompt.tsx b/app/components/LanguagePrompt.tsx index c236a1633..f070077bf 100644 --- a/app/components/LanguagePrompt.tsx +++ b/app/components/LanguagePrompt.tsx @@ -73,11 +73,11 @@ export default function LanguagePrompt() {
{ - auth.updateUser({ + onClick={async () => { + ui.setLanguagePromptDismissed(); + await auth.updateUser({ language, }); - ui.setLanguagePromptDismissed(); }} > {t("Change Language")} diff --git a/app/components/LazyPolyfills.tsx b/app/components/LazyPolyfills.tsx index 20601c96d..41c6c2a3a 100644 --- a/app/components/LazyPolyfills.tsx +++ b/app/components/LazyPolyfills.tsx @@ -1,4 +1,5 @@ import * as React from "react"; +import Logger from "~/utils/Logger"; import { loadPolyfills } from "~/utils/polyfills"; /** @@ -8,9 +9,13 @@ export const LazyPolyfill: React.FC = ({ children }) => { const [isLoaded, setIsLoaded] = React.useState(false); React.useEffect(() => { - loadPolyfills().then(() => { - setIsLoaded(true); - }); + loadPolyfills() + .then(() => { + setIsLoaded(true); + }) + .catch((error) => { + Logger.error("Polyfills failed to load", error); + }); }, []); if (!isLoaded) { diff --git a/app/components/Notifications/NotificationListItem.tsx b/app/components/Notifications/NotificationListItem.tsx index a8b6219a7..dffe9e55e 100644 --- a/app/components/Notifications/NotificationListItem.tsx +++ b/app/components/Notifications/NotificationListItem.tsx @@ -30,11 +30,11 @@ function NotificationListItem({ notification, onNavigate }: Props) { if (event.altKey) { event.preventDefault(); event.stopPropagation(); - notification.toggleRead(); + void notification.toggleRead(); return; } - notification.markAsRead(); + void notification.markAsRead(); onNavigate(); }; diff --git a/app/components/Notifications/Notifications.tsx b/app/components/Notifications/Notifications.tsx index e2cc4da3d..7e15e5002 100644 --- a/app/components/Notifications/Notifications.tsx +++ b/app/components/Notifications/Notifications.tsx @@ -43,7 +43,9 @@ function Notifications( // Account for old versions of the desktop app that don't have the // setNotificationCount method on the bridge. if (Desktop.bridge && "setNotificationCount" in Desktop.bridge) { - Desktop.bridge.setNotificationCount(notifications.approximateUnreadCount); + void Desktop.bridge.setNotificationCount( + notifications.approximateUnreadCount + ); } }, [notifications.approximateUnreadCount]); diff --git a/app/components/PaginatedList.tsx b/app/components/PaginatedList.tsx index 493e6acfa..ac2bb9d7a 100644 --- a/app/components/PaginatedList.tsx +++ b/app/components/PaginatedList.tsx @@ -70,7 +70,7 @@ class PaginatedList extends React.Component> { allowLoadMore = true; componentDidMount() { - this.fetchResults(); + void this.fetchResults(); } componentDidUpdate(prevProps: Props) { @@ -79,7 +79,7 @@ class PaginatedList extends React.Component> { !isEqual(prevProps.options, this.props.options) ) { this.reset(); - this.fetchResults(); + void this.fetchResults(); } } diff --git a/app/components/SearchActions.ts b/app/components/SearchActions.ts index b2bf843d1..62577b55a 100644 --- a/app/components/SearchActions.ts +++ b/app/components/SearchActions.ts @@ -11,7 +11,7 @@ export default function SearchActions() { React.useEffect(() => { if (!searches.isLoaded) { - searches.fetchPage({}); + void searches.fetchPage({}); } }, [searches]); diff --git a/app/components/Sidebar/App.tsx b/app/components/Sidebar/App.tsx index a2a276969..618f7cd64 100644 --- a/app/components/Sidebar/App.tsx +++ b/app/components/Sidebar/App.tsx @@ -43,8 +43,8 @@ function AppSidebar() { React.useEffect(() => { if (!user.isViewer) { - documents.fetchDrafts(); - documents.fetchTemplates(); + void documents.fetchDrafts(); + void documents.fetchTemplates(); } }, [documents, user.isViewer]); diff --git a/app/components/Sidebar/components/CollectionLinkChildren.tsx b/app/components/Sidebar/components/CollectionLinkChildren.tsx index e40638cb4..dcf1d8a6c 100644 --- a/app/components/Sidebar/components/CollectionLinkChildren.tsx +++ b/app/components/Sidebar/components/CollectionLinkChildren.tsx @@ -57,7 +57,7 @@ function CollectionLinkChildren({ if (!collection) { return; } - documents.move(item.id, collection.id, undefined, 0); + void documents.move(item.id, collection.id, undefined, 0); }, collect: (monitor) => ({ isOverReorder: !!monitor.isOver(), diff --git a/app/components/Sidebar/components/Collections.tsx b/app/components/Sidebar/components/Collections.tsx index 8da3ac815..0c3d3b77e 100644 --- a/app/components/Sidebar/components/Collections.tsx +++ b/app/components/Sidebar/components/Collections.tsx @@ -36,7 +36,7 @@ function Collections() { ] = useDrop({ accept: "collection", drop: async (item: DragObject) => { - collections.move( + void collections.move( item.id, fractionalIndex(null, orderedCollections[0].index) ); diff --git a/app/components/Sidebar/components/DocumentLink.tsx b/app/components/Sidebar/components/DocumentLink.tsx index 937332382..7e21d145f 100644 --- a/app/components/Sidebar/components/DocumentLink.tsx +++ b/app/components/Sidebar/components/DocumentLink.tsx @@ -67,7 +67,7 @@ function InnerDocumentLink( React.useEffect(() => { if (isActiveDocument && hasChildDocuments) { - fetchChildDocuments(node.id); + void fetchChildDocuments(node.id); } }, [fetchChildDocuments, node, hasChildDocuments, isActiveDocument]); @@ -115,7 +115,7 @@ function InnerDocumentLink( ); const handlePrefetch = React.useCallback(() => { - prefetchDocument?.(node.id); + void prefetchDocument?.(node.id); }, [prefetchDocument, node]); const handleTitleChange = React.useCallback( @@ -242,11 +242,11 @@ function InnerDocumentLink( } if (expanded) { - documents.move(item.id, collection.id, node.id, 0); + void documents.move(item.id, collection.id, node.id, 0); return; } - documents.move(item.id, collection.id, parentId, index + 1); + void documents.move(item.id, collection.id, parentId, index + 1); }, collect: (monitor) => ({ isOverReorder: monitor.isOver(), diff --git a/app/components/Sidebar/components/DraggableCollectionLink.tsx b/app/components/Sidebar/components/DraggableCollectionLink.tsx index ad85fcd48..2116a21b9 100644 --- a/app/components/Sidebar/components/DraggableCollectionLink.tsx +++ b/app/components/Sidebar/components/DraggableCollectionLink.tsx @@ -51,7 +51,7 @@ function DraggableCollectionLink({ ] = useDrop({ accept: "collection", drop: (item: DragObject) => { - collections.move( + void collections.move( item.id, fractionalIndex(collection.index, belowCollectionIndex) ); diff --git a/app/components/Sidebar/components/Starred.tsx b/app/components/Sidebar/components/Starred.tsx index 78dbdd7a9..f4c230fea 100644 --- a/app/components/Sidebar/components/Starred.tsx +++ b/app/components/Sidebar/components/Starred.tsx @@ -40,7 +40,7 @@ function Starred() { ); React.useEffect(() => { - fetchResults(); + void fetchResults(); }, []); const handleShowMore = async () => { @@ -52,7 +52,7 @@ function Starred() { const [{ isOverReorder, isDraggingAnyStar }, dropToReorder] = useDrop({ accept: "star", drop: async (item: { star: Star }) => { - item.star.save({ + void item.star.save({ index: fractionalIndex(null, stars.orderedData[0].index), }); }, diff --git a/app/components/Sidebar/components/StarredLink.tsx b/app/components/Sidebar/components/StarredLink.tsx index 250f15592..c0b891216 100644 --- a/app/components/Sidebar/components/StarredLink.tsx +++ b/app/components/Sidebar/components/StarredLink.tsx @@ -90,13 +90,9 @@ function StarredLink({ star }: Props) { }, [star.collectionId, ui.activeCollectionId, locationStateStarred]); useEffect(() => { - async function load() { - if (documentId) { - await documents.fetch(documentId); - } + if (documentId) { + void documents.fetch(documentId); } - - load(); }, [documentId, documents]); const handleDisclosureClick = React.useCallback( @@ -134,7 +130,7 @@ function StarredLink({ star }: Props) { drop: (item: { star: Star }) => { const next = star?.next(); - item.star.save({ + void item.star.save({ index: fractionalIndex(star?.index || null, next?.index || null), }); }, diff --git a/app/components/Sidebar/components/Version.tsx b/app/components/Sidebar/components/Version.tsx index 772ee7656..609928580 100644 --- a/app/components/Sidebar/components/Version.tsx +++ b/app/components/Sidebar/components/Version.tsx @@ -36,7 +36,7 @@ export default function Version() { } } - loadReleases(); + void loadReleases(); }, []); return ( diff --git a/app/components/WebsocketProvider.tsx b/app/components/WebsocketProvider.tsx index 90f64c5e9..abd606d6d 100644 --- a/app/components/WebsocketProvider.tsx +++ b/app/components/WebsocketProvider.tsx @@ -380,9 +380,9 @@ class WebsocketProvider extends React.Component { // if the user is us then we go ahead and load the collection from API. this.socket.on( "collections.add_user", - action((event: WebsocketCollectionUserEvent) => { + async (event: WebsocketCollectionUserEvent) => { if (auth.user && event.userId === auth.user.id) { - collections.fetch(event.collectionId, { + await collections.fetch(event.collectionId, { force: true, }); } @@ -391,7 +391,7 @@ class WebsocketProvider extends React.Component { documents.inCollection(event.collectionId).forEach((document) => { policies.remove(document.id); }); - }) + } ); // received when a user is removed from having access to a collection diff --git a/app/editor/components/LinkEditor.tsx b/app/editor/components/LinkEditor.tsx index 11fd8248d..09c17657f 100644 --- a/app/editor/components/LinkEditor.tsx +++ b/app/editor/components/LinkEditor.tsx @@ -139,7 +139,7 @@ class LinkEditor extends React.Component { if (result) { this.save(result.url, result.title); } else if (onCreateLink && selectedIndex === results.length) { - this.handleCreateLink(this.suggestedLinkTitle); + void this.handleCreateLink(this.suggestedLinkTitle); } } else { // saves the raw input as href @@ -376,8 +376,8 @@ class LinkEditor extends React.Component { subtitle={dictionary.createNewDoc} icon={} onPointerMove={() => this.handleFocusLink(results.length)} - onClick={() => { - this.handleCreateLink(suggestedLinkTitle); + onClick={async () => { + await this.handleCreateLink(suggestedLinkTitle); if (this.initialSelectionLength) { this.moveSelectionToEnd(); diff --git a/app/editor/components/LinkSearchResult.tsx b/app/editor/components/LinkSearchResult.tsx index 5c60d1da2..4328476ef 100644 --- a/app/editor/components/LinkSearchResult.tsx +++ b/app/editor/components/LinkSearchResult.tsx @@ -22,7 +22,7 @@ function LinkSearchResult({ const ref = React.useCallback( (node: HTMLElement | null) => { if (selected && node) { - scrollIntoView(node, { + void scrollIntoView(node, { scrollMode: "if-needed", block: "center", boundary: (parent) => diff --git a/app/editor/components/LinkToolbar.tsx b/app/editor/components/LinkToolbar.tsx index 6bb2046fe..e46b83213 100644 --- a/app/editor/components/LinkToolbar.tsx +++ b/app/editor/components/LinkToolbar.tsx @@ -82,7 +82,7 @@ export default function LinkToolbar({ ) ); - createAndInsertLink(view, title, href, { + return createAndInsertLink(view, title, href, { onCreateLink, onShowToast: showToast, dictionary, diff --git a/app/editor/components/MentionMenu.tsx b/app/editor/components/MentionMenu.tsx index 68fe24ad1..ed9ed384c 100644 --- a/app/editor/components/MentionMenu.tsx +++ b/app/editor/components/MentionMenu.tsx @@ -54,7 +54,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) { React.useEffect(() => { if (isActive) { - request(); + void request(); } }, [request, isActive]); diff --git a/app/editor/components/SelectionToolbar.tsx b/app/editor/components/SelectionToolbar.tsx index 143efbafb..380d79719 100644 --- a/app/editor/components/SelectionToolbar.tsx +++ b/app/editor/components/SelectionToolbar.tsx @@ -159,7 +159,7 @@ export default function SelectionToolbar(props: Props) { .addMark(from, to, markType.create({ href })) ); - createAndInsertLink(view, title, href, { + return createAndInsertLink(view, title, href, { onCreateLink, onShowToast, dictionary, diff --git a/app/editor/components/SuggestionsMenuItem.tsx b/app/editor/components/SuggestionsMenuItem.tsx index b45fab884..20a25ff3e 100644 --- a/app/editor/components/SuggestionsMenuItem.tsx +++ b/app/editor/components/SuggestionsMenuItem.tsx @@ -25,7 +25,7 @@ function SuggestionsMenuItem({ const ref = React.useCallback( (node) => { if (selected && node) { - scrollIntoView(node, { + void scrollIntoView(node, { scrollMode: "if-needed", block: "nearest", boundary: (parent) => diff --git a/app/editor/index.tsx b/app/editor/index.tsx index 4a0ee6efa..cab6dcf48 100644 --- a/app/editor/index.tsx +++ b/app/editor/index.tsx @@ -233,7 +233,7 @@ export class Editor extends React.PureComponent< window.addEventListener("theme-changed", this.dispatchThemeChanged); if (this.props.scrollTo) { - this.scrollToAnchor(this.props.scrollTo); + void this.scrollToAnchor(this.props.scrollTo); } this.calculateDir(); @@ -263,7 +263,7 @@ export class Editor extends React.PureComponent< } if (this.props.scrollTo && this.props.scrollTo !== prevProps.scrollTo) { - this.scrollToAnchor(this.props.scrollTo); + void this.scrollToAnchor(this.props.scrollTo); } // Focus at the end of the document if switching from readOnly and autoFocus diff --git a/app/hooks/useDesktopTitlebar.ts b/app/hooks/useDesktopTitlebar.ts index b327f9c8e..e745f0a2b 100644 --- a/app/hooks/useDesktopTitlebar.ts +++ b/app/hooks/useDesktopTitlebar.ts @@ -7,7 +7,7 @@ export const useDesktopTitlebar = () => { return; } - const handleDoubleClick = (event: MouseEvent) => { + const handleDoubleClick = async (event: MouseEvent) => { // Ignore double clicks on interactive elements such as inputs and buttons if (event.composedPath().some(elementIsInteractive)) { return; @@ -19,7 +19,7 @@ export const useDesktopTitlebar = () => { } event.preventDefault(); - Desktop.bridge.onTitlebarDoubleClick(); + await Desktop.bridge.onTitlebarDoubleClick(); }; window.addEventListener("dblclick", handleDoubleClick); diff --git a/app/hooks/useEmbeds.ts b/app/hooks/useEmbeds.ts index d1dc8bd48..5c55668eb 100644 --- a/app/hooks/useEmbeds.ts +++ b/app/hooks/useEmbeds.ts @@ -29,7 +29,7 @@ export default function useEmbeds(loadIfMissing = false) { } if (!integrations.isLoaded && !integrations.isFetching && loadIfMissing) { - fetchEmbedIntegrations(); + void fetchEmbedIntegrations(); } }, [integrations, loadIfMissing]); diff --git a/app/menus/DocumentMenu.tsx b/app/menus/DocumentMenu.tsx index 2c6117706..bf1d62984 100644 --- a/app/menus/DocumentMenu.tsx +++ b/app/menus/DocumentMenu.tsx @@ -100,7 +100,7 @@ function DocumentMenu({ const handleOpen = React.useCallback(async () => { if (!data && !loading) { - request(); + await request(); } if (onOpen) { @@ -325,7 +325,7 @@ function DocumentMenu({ checked={document.fullWidth} onChange={(ev) => { document.fullWidth = ev.currentTarget.checked; - document.save(); + void document.save(); }} /> diff --git a/app/menus/UserMenu.tsx b/app/menus/UserMenu.tsx index 76fbd91bb..0f42ec84d 100644 --- a/app/menus/UserMenu.tsx +++ b/app/menus/UserMenu.tsx @@ -116,9 +116,9 @@ function UserMenu({ user }: Props) { ); const handleRevoke = React.useCallback( - (ev: React.SyntheticEvent) => { + async (ev: React.SyntheticEvent) => { ev.preventDefault(); - users.delete(user); + await users.delete(user); }, [users, user] ); @@ -143,9 +143,9 @@ function UserMenu({ user }: Props) { ); const handleActivate = React.useCallback( - (ev: React.SyntheticEvent) => { + async (ev: React.SyntheticEvent) => { ev.preventDefault(); - users.activate(user); + await users.activate(user); }, [users, user] ); diff --git a/app/scenes/Collection.tsx b/app/scenes/Collection.tsx index 2585aad0c..5914f0c35 100644 --- a/app/scenes/Collection.tsx +++ b/app/scenes/Collection.tsx @@ -84,14 +84,14 @@ function CollectionScene() { setError(undefined); if (collection) { - pins.fetchPage({ + void pins.fetchPage({ collectionId: collection.id, }); } }, [pins, collection]); React.useEffect(() => { - async function load() { + async function fetchData() { if ((!can || !collection) && !error && !isFetching) { try { setError(undefined); @@ -105,7 +105,7 @@ function CollectionScene() { } } - load(); + void fetchData(); }, [collections, isFetching, collection, error, id, can]); useCommandBarActions( diff --git a/app/scenes/Collection/MembershipPreview.tsx b/app/scenes/Collection/MembershipPreview.tsx index dd0ba8fac..8276ddaed 100644 --- a/app/scenes/Collection/MembershipPreview.tsx +++ b/app/scenes/Collection/MembershipPreview.tsx @@ -53,7 +53,7 @@ const MembershipPreview = ({ collection, limit = 8 }: Props) => { } }; - fetchData(); + void fetchData(); }, [ isMobile, collection.permission, diff --git a/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx b/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx index 1628161e6..f9a875f16 100644 --- a/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx +++ b/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx @@ -36,10 +36,7 @@ function AddGroupsToCollection(props: Props) { const { t } = useTranslation(); const debouncedFetch = React.useMemo( - () => - debounce((query) => { - fetchGroups({ query }); - }, 250), + () => debounce((query) => fetchGroups({ query }), 250), [fetchGroups] ); @@ -47,14 +44,14 @@ function AddGroupsToCollection(props: Props) { (ev: React.ChangeEvent) => { const updatedQuery = ev.target.value; setQuery(updatedQuery); - debouncedFetch(updatedQuery); + void debouncedFetch(updatedQuery); }, [debouncedFetch] ); - const handleAddGroup = (group: Group) => { + const handleAddGroup = async (group: Group) => { try { - collectionGroupMemberships.create({ + await collectionGroupMemberships.create({ collectionId: collection.id, groupId: group.id, }); diff --git a/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx b/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx index 7b4f908cc..2387fd1b2 100644 --- a/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx +++ b/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx @@ -44,9 +44,9 @@ function AddPeopleToCollection({ collection }: Props) { 250 ); - const handleAddUser = (user: User) => { + const handleAddUser = async (user: User) => { try { - memberships.create({ + await memberships.create({ collectionId: collection.id, userId: user.id, }); diff --git a/app/scenes/Document/Shared.tsx b/app/scenes/Document/Shared.tsx index 4ddddd7ac..564cfec0f 100644 --- a/app/scenes/Document/Shared.tsx +++ b/app/scenes/Document/Shared.tsx @@ -99,7 +99,7 @@ function SharedDocumentScene(props: Props) { React.useEffect(() => { if (!auth.user) { - changeLanguage(detectLanguage(), i18n); + void changeLanguage(detectLanguage(), i18n); } }, [auth, i18n]); @@ -125,7 +125,7 @@ function SharedDocumentScene(props: Props) { setError(err); } } - fetchData(); + void fetchData(); }, [documents, documentSlug, shareId, ui]); if (error) { diff --git a/app/scenes/Document/components/CommentForm.tsx b/app/scenes/Document/components/CommentForm.tsx index 858b903a4..f44076e14 100644 --- a/app/scenes/Document/components/CommentForm.tsx +++ b/app/scenes/Document/components/CommentForm.tsx @@ -69,14 +69,14 @@ function CommentForm({ const { comments } = useStores(); const user = useCurrentUser(); - const reset = React.useCallback(() => { + const reset = React.useCallback(async () => { const isEmpty = editorRef.current?.isEmpty() ?? true; if (isEmpty && thread?.isNew) { if (thread.id) { editor?.removeComment(thread.id); } - thread.delete(); + await thread.delete(); } }, [editor, thread]); @@ -173,11 +173,11 @@ function CommentForm({ } }; - const handleCancel = () => { + const handleCancel = async () => { setData(undefined); setForceRender((s) => ++s); setInputFocused(false); - reset(); + await reset(); }; const handleFocus = () => { diff --git a/app/scenes/Document/components/CommentThread.tsx b/app/scenes/Document/components/CommentThread.tsx index 1c8deffd8..d34aa4043 100644 --- a/app/scenes/Document/components/CommentThread.tsx +++ b/app/scenes/Document/components/CommentThread.tsx @@ -114,7 +114,7 @@ function CommentThread({ if (!topRef.current) { return; } - scrollIntoView(topRef.current, { + return scrollIntoView(topRef.current, { scrollMode: "if-needed", behavior: "smooth", block: "end", diff --git a/app/scenes/Document/components/DataLoader.tsx b/app/scenes/Document/components/DataLoader.tsx index ef370be29..08b4bd63a 100644 --- a/app/scenes/Document/components/DataLoader.tsx +++ b/app/scenes/Document/components/DataLoader.tsx @@ -86,7 +86,7 @@ function DataLoader({ match, children }: Props) { setError(err); } } - fetchDocument(); + void fetchDocument(); }, [ui, documents, document, shareId, documentSlug]); React.useEffect(() => { @@ -99,7 +99,7 @@ function DataLoader({ match, children }: Props) { } } } - fetchRevision(); + void fetchRevision(); }, [revisions, revisionId]); React.useEffect(() => { @@ -112,7 +112,7 @@ function DataLoader({ match, children }: Props) { } } } - fetchRevision(); + void fetchRevision(); }, [document, revisionId, revisions]); React.useEffect(() => { @@ -128,7 +128,7 @@ function DataLoader({ match, children }: Props) { } } } - fetchSubscription(); + void fetchSubscription(); }, [document?.id, subscriptions, revisionId]); React.useEffect(() => { @@ -143,7 +143,7 @@ function DataLoader({ match, children }: Props) { } } } - fetchViews(); + void fetchViews(); }, [document?.id, document?.isDeleted, revisionId, views]); const onCreateLink = React.useCallback( @@ -180,7 +180,7 @@ function DataLoader({ match, children }: Props) { // when viewing a public share link if (can.read) { if (team?.getPreference(TeamPreference.Commenting)) { - comments.fetchDocumentComments(document.id, { + void comments.fetchDocumentComments(document.id, { limit: 100, }); } diff --git a/app/scenes/Document/components/Document.tsx b/app/scenes/Document/components/Document.tsx index fac830c75..fab2ef8ad 100644 --- a/app/scenes/Document/components/Document.tsx +++ b/app/scenes/Document/components/Document.tsx @@ -128,7 +128,7 @@ class DocumentScene extends React.Component { this.props.document.hasEmptyTitle && this.props.document.isPersistedOnce ) { - this.props.document.delete(); + void this.props.document.delete(); } } @@ -167,7 +167,8 @@ class DocumentScene extends React.Component { this.props.document.text = template.text; this.updateIsDirty(); - this.onSave({ + + return this.onSave({ autosave: true, publish: false, done: false, @@ -189,7 +190,7 @@ class DocumentScene extends React.Component { }); if (response) { - this.replaceDocument(response.data); + await this.replaceDocument(response.data); toasts.showToast(t("Document restored")); history.replace(this.props.document.url, history.location.state); } @@ -244,7 +245,7 @@ class DocumentScene extends React.Component { } if (document?.collectionId) { - this.onSave({ + void this.onSave({ publish: true, done: true, }); @@ -324,12 +325,14 @@ class DocumentScene extends React.Component { } }; - autosave = debounce(() => { - this.onSave({ - done: false, - autosave: true, - }); - }, AUTOSAVE_DELAY); + autosave = debounce( + () => + this.onSave({ + done: false, + autosave: true, + }), + AUTOSAVE_DELAY + ); updateIsDirty = () => { const { document } = this.props; @@ -370,7 +373,7 @@ class DocumentScene extends React.Component { this.title = value; this.props.document.title = value; this.updateIsDirty(); - this.autosave(); + void this.autosave(); }); goBack = () => { diff --git a/app/scenes/Document/components/MultiplayerEditor.tsx b/app/scenes/Document/components/MultiplayerEditor.tsx index 342756781..e46c7e404 100644 --- a/app/scenes/Document/components/MultiplayerEditor.tsx +++ b/app/scenes/Document/components/MultiplayerEditor.tsx @@ -220,7 +220,7 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) { React.useEffect(() => { if (isLocalSynced && isRemoteSynced) { - onSynced?.(); + void onSynced?.(); } }, [onSynced, isLocalSynced, isRemoteSynced]); @@ -236,14 +236,14 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) { !isVisible && remoteProvider.status === WebSocketStatus.Connected ) { - remoteProvider.disconnect(); + void remoteProvider.disconnect(); } if ( (!isIdle || isVisible) && remoteProvider.status === WebSocketStatus.Disconnected ) { - remoteProvider.connect(); + void remoteProvider.connect(); } }, [remoteProvider, isIdle, isVisible]); diff --git a/app/scenes/Document/components/References.tsx b/app/scenes/Document/components/References.tsx index 6c7fee58a..fe507d367 100644 --- a/app/scenes/Document/components/References.tsx +++ b/app/scenes/Document/components/References.tsx @@ -19,7 +19,7 @@ function References({ document }: Props) { const location = useLocation(); React.useEffect(() => { - documents.fetchBacklinks(document.id); + void documents.fetchBacklinks(document.id); }, [documents, document.id]); const backlinks = documents.getBacklinkedDocuments(document.id); diff --git a/app/scenes/Document/components/SharePopover.tsx b/app/scenes/Document/components/SharePopover.tsx index 6d87dd893..5fb905837 100644 --- a/app/scenes/Document/components/SharePopover.tsx +++ b/app/scenes/Document/components/SharePopover.tsx @@ -76,7 +76,7 @@ function SharePopover({ React.useEffect(() => { if (visible && team.sharing) { - document.share(); + void document.share(); buttonRef.current?.focus(); } diff --git a/app/scenes/DocumentNew.tsx b/app/scenes/DocumentNew.tsx index 0d75c0c77..1b629f014 100644 --- a/app/scenes/DocumentNew.tsx +++ b/app/scenes/DocumentNew.tsx @@ -46,7 +46,7 @@ function DocumentNew() { } } - createDocument(); + void createDocument(); }); return ( diff --git a/app/scenes/GroupMembers/AddPeopleToGroup.tsx b/app/scenes/GroupMembers/AddPeopleToGroup.tsx index 42252f96e..b6a3e1582 100644 --- a/app/scenes/GroupMembers/AddPeopleToGroup.tsx +++ b/app/scenes/GroupMembers/AddPeopleToGroup.tsx @@ -33,10 +33,7 @@ function AddPeopleToGroup(props: Props) { const { fetchPage: fetchUsers } = users; const debouncedFetch = React.useMemo( - () => - debounce((query) => { - fetchUsers({ query }); - }, 250), + () => debounce((query) => fetchUsers({ query }), 250), [fetchUsers] ); @@ -44,7 +41,7 @@ function AddPeopleToGroup(props: Props) { (ev: React.ChangeEvent) => { const updatedQuery = ev.target.value; setQuery(updatedQuery); - debouncedFetch(updatedQuery); + void debouncedFetch(updatedQuery); }, [debouncedFetch] ); diff --git a/app/scenes/Home.tsx b/app/scenes/Home.tsx index 789b1d3f2..034a30067 100644 --- a/app/scenes/Home.tsx +++ b/app/scenes/Home.tsx @@ -29,7 +29,7 @@ function Home() { const { t } = useTranslation(); React.useEffect(() => { - pins.fetchPage(); + void pins.fetchPage(); }, [pins]); const canManageTeam = usePolicy(team).manage; diff --git a/app/scenes/Login/index.tsx b/app/scenes/Login/index.tsx index eec0fa0ed..e62cb6dfa 100644 --- a/app/scenes/Login/index.tsx +++ b/app/scenes/Login/index.tsx @@ -101,7 +101,7 @@ function Login({ children }: Props) { // Try to detect the user's language and show the login page on its idiom // if translation is available React.useEffect(() => { - changeLanguage(detectLanguage(), i18n); + void changeLanguage(detectLanguage(), i18n); }, [i18n]); React.useEffect(() => { diff --git a/app/scenes/Logout.tsx b/app/scenes/Logout.tsx index 646d5e6e9..2f21dc099 100644 --- a/app/scenes/Logout.tsx +++ b/app/scenes/Logout.tsx @@ -4,7 +4,7 @@ import useStores from "~/hooks/useStores"; const Logout = () => { const { auth } = useStores(); - auth.logout(); + void auth.logout(); return ; }; diff --git a/app/scenes/Search/Search.tsx b/app/scenes/Search/Search.tsx index 624af84b5..304e96175 100644 --- a/app/scenes/Search/Search.tsx +++ b/app/scenes/Search/Search.tsx @@ -94,7 +94,7 @@ class Search extends React.Component { handleKeyDown = (ev: React.KeyboardEvent) => { if (ev.key === "Enter") { this.updateLocation(ev.currentTarget.value); - this.fetchResults(); + void this.fetchResults(); return; } @@ -143,7 +143,7 @@ class Search extends React.Component { this.allowLoadMore = true; // To prevent "no results" showing before debounce kicks in this.isLoading = true; - this.fetchResults(); + void this.fetchResults(); }; handleTermChange = () => { @@ -153,7 +153,7 @@ class Search extends React.Component { this.allowLoadMore = true; // To prevent "no results" showing before debounce kicks in this.isLoading = true; - this.fetchResults(); + void this.fetchResults(); }; handleFilterChange = (search: { diff --git a/app/scenes/Search/components/RecentSearches.tsx b/app/scenes/Search/components/RecentSearches.tsx index 22d01afba..cb8e8a9a5 100644 --- a/app/scenes/Search/components/RecentSearches.tsx +++ b/app/scenes/Search/components/RecentSearches.tsx @@ -18,7 +18,7 @@ function RecentSearches() { const [isPreloaded] = React.useState(searches.recent.length > 0); React.useEffect(() => { - searches.fetchPage({}); + void searches.fetchPage({}); }, [searches]); const content = searches.recent.length ? ( @@ -32,9 +32,9 @@ function RecentSearches() { { + onClick={async (ev) => { ev.preventDefault(); - searchQuery.delete(); + await searchQuery.delete(); }} > diff --git a/app/scenes/Search/components/UserFilter.tsx b/app/scenes/Search/components/UserFilter.tsx index 581603c4f..d10695b25 100644 --- a/app/scenes/Search/components/UserFilter.tsx +++ b/app/scenes/Search/components/UserFilter.tsx @@ -18,7 +18,7 @@ function UserFilter(props: Props) { const { users } = useStores(); React.useEffect(() => { - users.fetchPage({ + void users.fetchPage({ limit: 100, }); }, [users]); diff --git a/app/scenes/Settings/GoogleAnalytics.tsx b/app/scenes/Settings/GoogleAnalytics.tsx index d99bee7f4..b1fc64122 100644 --- a/app/scenes/Settings/GoogleAnalytics.tsx +++ b/app/scenes/Settings/GoogleAnalytics.tsx @@ -42,7 +42,7 @@ function GoogleAnalytics() { }); React.useEffect(() => { - integrations.fetchPage({ + void integrations.fetchPage({ type: IntegrationType.Analytics, }); }, [integrations]); diff --git a/app/scenes/Settings/Members.tsx b/app/scenes/Settings/Members.tsx index c99bbb34b..5697601c7 100644 --- a/app/scenes/Settings/Members.tsx +++ b/app/scenes/Settings/Members.tsx @@ -68,7 +68,7 @@ function Members() { } }; - fetchData(); + void fetchData(); }, [query, sort, filter, page, direction, users, users.counts.all]); React.useEffect(() => { diff --git a/app/scenes/Settings/Security.tsx b/app/scenes/Settings/Security.tsx index 1e731d109..d8b242f56 100644 --- a/app/scenes/Settings/Security.tsx +++ b/app/scenes/Settings/Security.tsx @@ -46,7 +46,7 @@ function Security() { React.useEffect(() => { if (!providers && !loading) { - request(); + void request(); } }, [loading, providers, request]); diff --git a/app/scenes/Settings/SelfHosted.tsx b/app/scenes/Settings/SelfHosted.tsx index cd6bd544a..41fd07926 100644 --- a/app/scenes/Settings/SelfHosted.tsx +++ b/app/scenes/Settings/SelfHosted.tsx @@ -41,7 +41,7 @@ function SelfHosted() { }); React.useEffect(() => { - integrations.fetchPage({ + void integrations.fetchPage({ type: IntegrationType.Embed, }); }, [integrations]); diff --git a/app/scenes/Settings/Shares.tsx b/app/scenes/Settings/Shares.tsx index e450f1326..df29e09d0 100644 --- a/app/scenes/Settings/Shares.tsx +++ b/app/scenes/Settings/Shares.tsx @@ -53,7 +53,7 @@ function Shares() { } }; - fetchData(); + void fetchData(); }, [query, sort, page, direction, shares]); React.useEffect(() => { diff --git a/app/scenes/Settings/components/ImageUpload.tsx b/app/scenes/Settings/components/ImageUpload.tsx index 53ea73ebe..9c8345db3 100644 --- a/app/scenes/Settings/components/ImageUpload.tsx +++ b/app/scenes/Settings/components/ImageUpload.tsx @@ -71,7 +71,7 @@ class ImageUpload extends React.Component { name: this.file.name, preset: AttachmentPreset.Avatar, }); - this.props.onSuccess(attachment.url); + void this.props.onSuccess(attachment.url); } catch (err) { this.props.onError(err.message); } finally { diff --git a/app/stores/AuthStore.ts b/app/stores/AuthStore.ts index 6e6131624..ac6467aa0 100644 --- a/app/stores/AuthStore.ts +++ b/app/stores/AuthStore.ts @@ -111,7 +111,7 @@ export default class AuthStore { // we are signed in and the received data contains no user then sign out if (this.authenticated) { if (data.user === null) { - this.logout(); + void this.logout(); } } else { this.rehydrate(data); @@ -348,7 +348,7 @@ export default class AuthStore { this.token = null; // Tell the host application we logged out, if any – allows window cleanup. - Desktop.bridge?.onLogout?.(); + void Desktop.bridge?.onLogout?.(); this.rootStore.logout(); try { diff --git a/app/stores/CollectionsStore.ts b/app/stores/CollectionsStore.ts index 0cc0d7e15..3b80d0b66 100644 --- a/app/stores/CollectionsStore.ts +++ b/app/stores/CollectionsStore.ts @@ -232,8 +232,8 @@ export default class CollectionsStore extends BaseStore { delete = async (collection: Collection) => { await super.delete(collection); - this.rootStore.documents.fetchRecentlyUpdated(); - this.rootStore.documents.fetchRecentlyViewed(); + await this.rootStore.documents.fetchRecentlyUpdated(); + await this.rootStore.documents.fetchRecentlyViewed(); }; export = (format: FileOperationFormat, includeAttachments: boolean) => diff --git a/app/stores/UsersStore.ts b/app/stores/UsersStore.ts index 911b9078f..8d7ed923f 100644 --- a/app/stores/UsersStore.ts +++ b/app/stores/UsersStore.ts @@ -179,7 +179,7 @@ export default class UsersStore extends BaseStore { @action async delete(user: User, options: Record = {}) { - super.delete(user, options); + await super.delete(user, options); if (!user.isSuspended && user.lastActiveAt) { this.counts.active -= 1; diff --git a/app/utils/developer.ts b/app/utils/developer.ts index 74fbd799d..a6c27010c 100644 --- a/app/utils/developer.ts +++ b/app/utils/developer.ts @@ -4,7 +4,7 @@ export async function deleteAllDatabases() { for (const database of databases) { if (database.name) { - await window.indexedDB.deleteDatabase(database.name); + window.indexedDB.deleteDatabase(database.name); } } } diff --git a/app/utils/i18n.test.ts b/app/utils/i18n.test.ts index 12752c0ce..1783ea0fd 100644 --- a/app/utils/i18n.test.ts +++ b/app/utils/i18n.test.ts @@ -15,13 +15,13 @@ describe("i18n env is unset", () => { it("translation of key should match", () => expect(i18n.t("Saving")).toBe("Saving")); - it("translation if changed to de-DE", () => { - i18n.changeLanguage("de-DE"); + it("translation if changed to de-DE", async () => { + await i18n.changeLanguage("de-DE"); expect(i18n.t("Saving")).toBe("Speichert"); }); - it("translation if changed to pt-PT", () => { - i18n.changeLanguage("pt-PT"); + it("translation if changed to pt-PT", async () => { + await i18n.changeLanguage("pt-PT"); expect(i18n.t("Saving")).toBe("A guardar"); }); }); @@ -36,13 +36,13 @@ describe("i18n env is en-US", () => { it("translation of key should match", () => expect(i18n.t("Saving")).toBe("Saving")); - it("translation if changed to de-DE", () => { - i18n.changeLanguage("de-DE"); + it("translation if changed to de-DE", async () => { + await i18n.changeLanguage("de-DE"); expect(i18n.t("Saving")).toBe("Speichert"); }); - it("translation if changed to pt-PT", () => { - i18n.changeLanguage("pt-PT"); + it("translation if changed to pt-PT", async () => { + await i18n.changeLanguage("pt-PT"); expect(i18n.t("Saving")).toBe("A guardar"); }); }); @@ -58,13 +58,13 @@ describe("i18n env is de-DE", () => { it("translation of key should match", () => expect(i18n.t("Saving")).toBe("Speichert")); - it("translation if changed to en-US", () => { - i18n.changeLanguage("en-US"); + it("translation if changed to en-US", async () => { + await i18n.changeLanguage("en-US"); expect(i18n.t("Saving")).toBe("Saving"); }); - it("translation if changed to pt-PT", () => { - i18n.changeLanguage("pt-PT"); + it("translation if changed to pt-PT", async () => { + await i18n.changeLanguage("pt-PT"); expect(i18n.t("Saving")).toBe("A guardar"); }); }); @@ -80,13 +80,13 @@ describe("i18n env is pt-PT", () => { it("translation of key should match", () => expect(i18n.t("Saving")).toBe("A guardar")); - it("translation if changed to en-US", () => { - i18n.changeLanguage("en-US"); + it("translation if changed to en-US", async () => { + await i18n.changeLanguage("en-US"); expect(i18n.t("Saving")).toBe("Saving"); }); - it("translation if changed to de-DE", () => { - i18n.changeLanguage("de-DE"); + it("translation if changed to de-DE", async () => { + await i18n.changeLanguage("de-DE"); expect(i18n.t("Saving")).toBe("Speichert"); }); }); diff --git a/app/utils/i18n.ts b/app/utils/i18n.ts index 6add90954..50da4e401 100644 --- a/app/utils/i18n.ts +++ b/app/utils/i18n.ts @@ -22,6 +22,7 @@ import backend from "i18next-http-backend"; import { initReactI18next } from "react-i18next"; import { languages } from "@shared/i18n"; import { unicodeCLDRtoBCP47, unicodeBCP47toCLDR } from "@shared/utils/date"; +import Logger from "./Logger"; const locales = { de_DE: de, @@ -59,11 +60,12 @@ export function dateLocale(language: string | null | undefined) { * * @param defaultLanguage The default language to use if the user's language * is not supported. - * @returns i18n instance + * @returns A promise resolving to the i18n instance */ export function initI18n(defaultLanguage = "en_US") { const lng = unicodeCLDRtoBCP47(defaultLanguage); - i18n + + void i18n .use(backend) .use(initReactI18next) .init({ @@ -85,7 +87,11 @@ export function initI18n(defaultLanguage = "en_US") { supportedLngs: languages.map(unicodeCLDRtoBCP47), keySeparator: false, returnNull: false, + }) + .catch((err) => { + Logger.error("Failed to initialize i18n", err); }); + return i18n; } diff --git a/app/utils/language.ts b/app/utils/language.ts index 1c2330170..28768545c 100644 --- a/app/utils/language.ts +++ b/app/utils/language.ts @@ -8,7 +8,7 @@ export function detectLanguage() { return `${ln}_${region}`; } -export function changeLanguage( +export async function changeLanguage( toLanguageString: string | null | undefined, i18n: i18n ) { @@ -19,7 +19,7 @@ export function changeLanguage( if (locale && i18n.languages?.[0] !== locale) { // Languages are stored in en_US format in the database, however the // frontend translation framework (i18next) expects en-US - i18n.changeLanguage(locale); - Desktop.bridge?.setSpellCheckerLanguages(["en-US", locale]); + await i18n.changeLanguage(locale); + await Desktop.bridge?.setSpellCheckerLanguages(["en-US", locale]); } } diff --git a/plugins/slack/client/Settings.tsx b/plugins/slack/client/Settings.tsx index 36580e540..11d3da844 100644 --- a/plugins/slack/client/Settings.tsx +++ b/plugins/slack/client/Settings.tsx @@ -30,10 +30,10 @@ function Slack() { const error = query.get("error"); React.useEffect(() => { - collections.fetchPage({ + void collections.fetchPage({ limit: 100, }); - integrations.fetchPage({ + void integrations.fetchPage({ limit: 100, }); }, [collections, integrations]); diff --git a/plugins/slack/server/api/hooks.ts b/plugins/slack/server/api/hooks.ts index f77efea59..671b702d7 100644 --- a/plugins/slack/server/api/hooks.ts +++ b/plugins/slack/server/api/hooks.ts @@ -153,6 +153,7 @@ router.post("hooks.slack", async (ctx: APIContext) => { verifySlackToken(token); let user, team; + // attempt to find the corresponding team for this request based on the team_id team = await Team.findOne({ include: [ @@ -299,13 +300,17 @@ router.post("hooks.slack", async (ctx: APIContext) => { const { results, totalCount } = user ? await SearchHelper.searchForUser(user, text, options) : await SearchHelper.searchForTeam(team, text, options); - SearchQuery.create({ + + void SearchQuery.create({ userId: user ? user.id : null, teamId: team.id, source: "slack", query: text, results: totalCount, + }).catch((err) => { + Logger.error("Failed to create search query", err); }); + const haventSignedIn = t( `It looks like you haven’t signed in to {{ appName }} yet, so results may be limited`, { diff --git a/server/commands/subscriptionCreator.ts b/server/commands/subscriptionCreator.ts index 1fa9adeed..c5fe15804 100644 --- a/server/commands/subscriptionCreator.ts +++ b/server/commands/subscriptionCreator.ts @@ -43,7 +43,7 @@ export default async function subscriptionCreator({ // If the subscription was deleted, then just restore the existing row. if (subscription.deletedAt && resubscribe) { - subscription.restore({ transaction }); + await subscription.restore({ transaction }); await Event.create( { diff --git a/server/emails/mailer.tsx b/server/emails/mailer.tsx index 3584f03b4..f8f7eb8da 100644 --- a/server/emails/mailer.tsx +++ b/server/emails/mailer.tsx @@ -41,7 +41,7 @@ export class Mailer { "SMTP_USERNAME not provided, generating test account…" ); - this.getTestTransportOptions().then((options) => { + void this.getTestTransportOptions().then((options) => { if (!options) { Logger.info( "email", diff --git a/server/index.ts b/server/index.ts index 0c950a299..f8d24cf15 100644 --- a/server/index.ts +++ b/server/index.ts @@ -53,7 +53,7 @@ async function master() { await checkPendingMigrations(); if (env.TELEMETRY && env.ENVIRONMENT === "production") { - checkUpdates(); + void checkUpdates(); setInterval(checkUpdates, 24 * 3600 * 1000); } } @@ -166,7 +166,7 @@ async function start(id: number, disconnect: () => void) { process.once("SIGINT", () => ShutdownHelper.execute()); } -throng({ +void throng({ master, worker: start, count: processCount, diff --git a/server/logging/Logger.ts b/server/logging/Logger.ts index 0e3a93647..dfbe0c756 100644 --- a/server/logging/Logger.ts +++ b/server/logging/Logger.ts @@ -6,6 +6,7 @@ import winston from "winston"; import env from "@server/env"; import Metrics from "@server/logging/Metrics"; import Sentry from "@server/logging/sentry"; +import ShutdownHelper from "@server/utils/ShutdownHelper"; import * as Tracing from "./tracer"; const isProduction = env.ENVIRONMENT === "production"; @@ -162,6 +163,18 @@ class Logger { } } + /** + * Report a fatal error and shut down the server + * + * @param message A description of the error + * @param error The error that occurred + * @param extra Arbitrary data to be logged that will appear in prod logs + */ + public fatal(message: string, error: Error, extra?: Extra) { + this.error(message, error, extra); + void ShutdownHelper.execute(); + } + /** * Sanitize data attached to logs and errors to remove sensitive information. * diff --git a/server/models/View.ts b/server/models/View.ts index 13e47d87f..0137fabab 100644 --- a/server/models/View.ts +++ b/server/models/View.ts @@ -64,7 +64,7 @@ class View extends IdModel { if (!created) { model.count += 1; - model.save(options); + await model.save(options); } return model; diff --git a/server/models/helpers/DocumentHelper.test.ts b/server/models/helpers/DocumentHelper.test.ts index c0aa37340..e0092a26d 100644 --- a/server/models/helpers/DocumentHelper.test.ts +++ b/server/models/helpers/DocumentHelper.test.ts @@ -174,7 +174,7 @@ This is a new paragraph. | | | |`, }); - const text = await DocumentHelper.toPlainText(revision); + const text = DocumentHelper.toPlainText(revision); // Strip all formatting expect(text).toEqual(`This is a test paragraph diff --git a/server/queues/processors/DebounceProcessor.ts b/server/queues/processors/DebounceProcessor.ts index 13f2dc75b..9b496b087 100644 --- a/server/queues/processors/DebounceProcessor.ts +++ b/server/queues/processors/DebounceProcessor.ts @@ -13,7 +13,7 @@ export default class DebounceProcessor extends BaseProcessor { async perform(event: Event) { switch (event.name) { case "documents.update": { - globalEventQueue.add( + await globalEventQueue.add( { ...event, name: "documents.update.delayed" }, { // speed up revision creation in development, we don't have all the @@ -41,7 +41,10 @@ export default class DebounceProcessor extends BaseProcessor { return; } - globalEventQueue.add({ ...event, name: "documents.update.debounced" }); + await globalEventQueue.add({ + ...event, + name: "documents.update.debounced", + }); break; } diff --git a/server/queues/tasks/ExportDocumentTreeTask.ts b/server/queues/tasks/ExportDocumentTreeTask.ts index c61bd63ff..e597fb0c0 100644 --- a/server/queues/tasks/ExportDocumentTreeTask.ts +++ b/server/queues/tasks/ExportDocumentTreeTask.ts @@ -44,7 +44,7 @@ export default abstract class ExportDocumentTreeTask extends ExportTask { let text = format === FileOperationFormat.HTMLZip ? await DocumentHelper.toHTML(document, { centered: true }) - : await DocumentHelper.toMarkdown(document); + : DocumentHelper.toMarkdown(document); const attachmentIds = includeAttachments ? parseAttachmentIds(document.text) diff --git a/server/queues/tasks/ImportJSONTask.ts b/server/queues/tasks/ImportJSONTask.ts index 3b576b261..b0c15d453 100644 --- a/server/queues/tasks/ImportJSONTask.ts +++ b/server/queues/tasks/ImportJSONTask.ts @@ -135,7 +135,7 @@ export default class ImportJSONTask extends ImportTask { }); if (Object.values(item.documents).length) { - await mapDocuments(item.documents, collectionId); + mapDocuments(item.documents, collectionId); } if (Object.values(item.attachments).length) { diff --git a/server/queues/tasks/RevisionCreatedNotificationsTask.test.ts b/server/queues/tasks/RevisionCreatedNotificationsTask.test.ts index 7a0823c05..09043ee31 100644 --- a/server/queues/tasks/RevisionCreatedNotificationsTask.test.ts +++ b/server/queues/tasks/RevisionCreatedNotificationsTask.test.ts @@ -338,7 +338,7 @@ describe("revisions.create", () => { enabled: true, }); - subscription.destroy(); + await subscription.destroy(); const task = new RevisionCreatedNotificationsTask(); diff --git a/server/routes/api/documents/documents.ts b/server/routes/api/documents/documents.ts index 3fc58327c..63756fb22 100644 --- a/server/routes/api/documents/documents.ts +++ b/server/routes/api/documents/documents.ts @@ -21,6 +21,7 @@ import { ValidationError, IncorrectEditionError, } from "@server/errors"; +import Logger from "@server/logging/Logger"; import auth from "@server/middlewares/authentication"; import { rateLimiter } from "@server/middlewares/rateLimiter"; import validate from "@server/middlewares/validate"; @@ -815,13 +816,15 @@ router.post( // When requesting subsequent pages of search results we don't want to record // duplicate search query records if (offset === 0) { - SearchQuery.create({ + void SearchQuery.create({ userId: user?.id, teamId, shareId, source: ctx.state.auth.type || "app", // we'll consider anything that isn't "api" to be "app" query, results: totalCount, + }).catch((err) => { + Logger.error("Failed to create search query", err); }); } diff --git a/server/scripts/20210716000000-backfill-revisions.ts b/server/scripts/20210716000000-backfill-revisions.ts index f1aeb2f6a..2c1eb2e6a 100644 --- a/server/scripts/20210716000000-backfill-revisions.ts +++ b/server/scripts/20210716000000-backfill-revisions.ts @@ -55,5 +55,5 @@ export default async function main(exit = false) { } // In the test suite we import the script rather than run via node CLI if (process.env.NODE_ENV !== "test") { - main(true); + void main(true); } diff --git a/server/scripts/20220722000000-backfill-subscriptions.ts b/server/scripts/20220722000000-backfill-subscriptions.ts index 05e62d401..610fd1756 100644 --- a/server/scripts/20220722000000-backfill-subscriptions.ts +++ b/server/scripts/20220722000000-backfill-subscriptions.ts @@ -49,5 +49,5 @@ export default async function main(exit = false) { } if (process.env.NODE_ENV !== "test") { - main(true); + void main(true); } diff --git a/server/scripts/20221008000000-backfill-crdt.ts b/server/scripts/20221008000000-backfill-crdt.ts index f76f5eff3..48cd99c97 100644 --- a/server/scripts/20221008000000-backfill-crdt.ts +++ b/server/scripts/20221008000000-backfill-crdt.ts @@ -81,5 +81,5 @@ export default async function main(exit = false) { } if (process.env.NODE_ENV !== "test") { - main(true); + void main(true); } diff --git a/server/scripts/20221029000000-crdt-to-text.ts b/server/scripts/20221029000000-crdt-to-text.ts index 9c3ecc5c3..e1b9c5777 100644 --- a/server/scripts/20221029000000-crdt-to-text.ts +++ b/server/scripts/20221029000000-crdt-to-text.ts @@ -67,5 +67,5 @@ export default async function main(exit = false) { } if (process.env.NODE_ENV !== "test") { - main(true); + void main(true); } diff --git a/server/scripts/20230313000000-migrate-notification-settings.ts b/server/scripts/20230313000000-migrate-notification-settings.ts index 6fe730770..872907a7e 100644 --- a/server/scripts/20230313000000-migrate-notification-settings.ts +++ b/server/scripts/20230313000000-migrate-notification-settings.ts @@ -58,5 +58,5 @@ export default async function main(exit = false) { } // In the test suite we import the script rather than run via node CLI if (process.env.NODE_ENV !== "test") { - main(true); + void main(true); } diff --git a/server/scripts/seed.ts b/server/scripts/seed.ts index d2942b37e..47eb9e803 100644 --- a/server/scripts/seed.ts +++ b/server/scripts/seed.ts @@ -48,5 +48,5 @@ export default async function main(exit = false) { } if (process.env.NODE_ENV !== "test") { - main(true); + void main(true); } diff --git a/server/services/cron.ts b/server/services/cron.ts index 929012b0f..1e0e95709 100644 --- a/server/services/cron.ts +++ b/server/services/cron.ts @@ -18,7 +18,7 @@ export default function init() { // Just give everything time to startup before running the first time. Not // _technically_ required to function. setTimeout(() => { - run(TaskSchedule.Daily); - run(TaskSchedule.Hourly); + void run(TaskSchedule.Daily); + void run(TaskSchedule.Hourly); }, 30 * Second); } diff --git a/server/services/web.ts b/server/services/web.ts index a33a9c40b..48b90a3a6 100644 --- a/server/services/web.ts +++ b/server/services/web.ts @@ -57,8 +57,8 @@ if (env.CDN_URL) { defaultSrc.push(env.CDN_URL); } -export default function init(app: Koa = new Koa(), server?: Server): Koa { - initI18n(); +export default function init(app: Koa = new Koa(), server?: Server) { + void initI18n(); if (isProduction) { // Force redirect to HTTPS protocol unless explicitly disabled diff --git a/server/services/websockets.ts b/server/services/websockets.ts index 23180cd51..e47d0a2ca 100644 --- a/server/services/websockets.ts +++ b/server/services/websockets.ts @@ -130,23 +130,27 @@ export default function init( // Handle events from event queue that should be sent to the clients down ws const websockets = new WebsocketsProcessor(); - websocketQueue.process( - traceFunction({ - serviceName: "websockets", - spanName: "process", - isRoot: true, - })(async function (job) { - const event = job.data; + websocketQueue + .process( + traceFunction({ + serviceName: "websockets", + spanName: "process", + isRoot: true, + })(async function (job) { + const event = job.data; - Tracing.setResource(`Processor.WebsocketsProcessor`); + Tracing.setResource(`Processor.WebsocketsProcessor`); - websockets.perform(event, io).catch((error) => { - Logger.error("Error processing websocket event", error, { - event, + websockets.perform(event, io).catch((error) => { + Logger.error("Error processing websocket event", error, { + event, + }); }); - }); - }) - ); + }) + ) + .catch((err) => { + Logger.fatal("Error starting websocketQueue", err); + }); } async function authenticated(io: IO.Server, socket: SocketWithAuth) { @@ -168,9 +172,6 @@ async function authenticated(io: IO.Server, socket: SocketWithAuth) { rooms.push(`collection-${collectionId}`) ); - // join all of the rooms at once - socket.join(rooms); - // allow the client to request to join rooms socket.on("join", async (event) => { // user is joining a collection channel, because their permissions have @@ -194,6 +195,9 @@ async function authenticated(io: IO.Server, socket: SocketWithAuth) { Metrics.increment("websockets.collections.leave"); } }); + + // join all of the rooms at once + await socket.join(rooms); } /** diff --git a/server/services/worker.ts b/server/services/worker.ts index 736659b3e..60521336a 100644 --- a/server/services/worker.ts +++ b/server/services/worker.ts @@ -12,128 +12,144 @@ import processors from "../queues/processors"; import tasks from "../queues/tasks"; export default function init() { - initI18n(); + void initI18n(); // This queue processes the global event bus - globalEventQueue.process( - traceFunction({ - serviceName: "worker", - spanName: "process", - isRoot: true, - })(async function (job) { - const event = job.data; - let err; + globalEventQueue + .process( + traceFunction({ + serviceName: "worker", + spanName: "process", + isRoot: true, + })(async function (job) { + const event = job.data; + let err; - setResource(`Event.${event.name}`); + setResource(`Event.${event.name}`); - Logger.info("worker", `Processing ${event.name}`, { - event, - attempt: job.attemptsMade, - }); + Logger.info("worker", `Processing ${event.name}`, { + event, + attempt: job.attemptsMade, + }); - // For each registered processor we check to see if it wants to handle the - // event (applicableEvents), and if so add a new queued job specifically - // for that processor. - for (const name in processors) { + // For each registered processor we check to see if it wants to handle the + // event (applicableEvents), and if so add a new queued job specifically + // for that processor. + for (const name in processors) { + const ProcessorClass = processors[name]; + + if (!ProcessorClass) { + throw new Error( + `Received event "${event.name}" for processor (${name}) that isn't registered. Check the file name matches the class name.` + ); + } + + try { + if (name === "WebsocketsProcessor") { + // websockets are a special case on their own queue because they must + // only be consumed by the websockets service rather than workers. + await websocketQueue.add(job.data); + } else if ( + ProcessorClass.applicableEvents.includes(event.name) || + ProcessorClass.applicableEvents.includes("*") + ) { + await processorEventQueue.add({ event, name }); + } + } catch (error) { + Logger.error( + `Error adding ${event.name} to ${name} queue`, + error, + event + ); + err = error; + } + } + + if (err) { + throw err; + } + }) + ) + .catch((err) => { + Logger.fatal("Error starting globalEventQueue", err); + }); + + // Jobs for individual processors are processed here. Only applicable events + // as unapplicable events were filtered in the global event queue above. + processorEventQueue + .process( + traceFunction({ + serviceName: "worker", + spanName: "process", + isRoot: true, + })(async function (job) { + const { event, name } = job.data; const ProcessorClass = processors[name]; + setResource(`Processor.${name}`); + if (!ProcessorClass) { throw new Error( `Received event "${event.name}" for processor (${name}) that isn't registered. Check the file name matches the class name.` ); } - try { - if (name === "WebsocketsProcessor") { - // websockets are a special case on their own queue because they must - // only be consumed by the websockets service rather than workers. - await websocketQueue.add(job.data); - } else if ( - ProcessorClass.applicableEvents.includes(event.name) || - ProcessorClass.applicableEvents.includes("*") - ) { - await processorEventQueue.add({ event, name }); + const processor = new ProcessorClass(); + + if (processor.perform) { + Logger.info("worker", `${name} running ${event.name}`, { + event, + }); + + try { + await processor.perform(event); + } catch (err) { + Logger.error( + `Error processing ${event.name} in ${name}`, + err, + event + ); + throw err; } - } catch (error) { - Logger.error( - `Error adding ${event.name} to ${name} queue`, - error, - event - ); - err = error; } - } - - if (err) { - throw err; - } - }) - ); - - // Jobs for individual processors are processed here. Only applicable events - // as unapplicable events were filtered in the global event queue above. - processorEventQueue.process( - traceFunction({ - serviceName: "worker", - spanName: "process", - isRoot: true, - })(async function (job) { - const { event, name } = job.data; - const ProcessorClass = processors[name]; - - setResource(`Processor.${name}`); - - if (!ProcessorClass) { - throw new Error( - `Received event "${event.name}" for processor (${name}) that isn't registered. Check the file name matches the class name.` - ); - } - - const processor = new ProcessorClass(); - - if (processor.perform) { - Logger.info("worker", `${name} running ${event.name}`, { - event, - }); - - try { - await processor.perform(event); - } catch (err) { - Logger.error(`Error processing ${event.name} in ${name}`, err, event); - throw err; - } - } - }) - ); + }) + ) + .catch((err) => { + Logger.fatal("Error starting processorEventQueue", err); + }); // Jobs for async tasks are processed here. - taskQueue.process( - traceFunction({ - serviceName: "worker", - spanName: "process", - isRoot: true, - })(async function (job) { - const { name, props } = job.data; - const TaskClass = tasks[name]; + taskQueue + .process( + traceFunction({ + serviceName: "worker", + spanName: "process", + isRoot: true, + })(async function (job) { + const { name, props } = job.data; + const TaskClass = tasks[name]; - setResource(`Task.${name}`); + setResource(`Task.${name}`); - if (!TaskClass) { - throw new Error( - `Task "${name}" is not registered. Check the file name matches the class name.` - ); - } + if (!TaskClass) { + throw new Error( + `Task "${name}" is not registered. Check the file name matches the class name.` + ); + } - Logger.info("worker", `${name} running`, props); + Logger.info("worker", `${name} running`, props); - const task = new TaskClass(); + const task = new TaskClass(); - try { - await task.perform(props); - } catch (err) { - Logger.error(`Error processing task in ${name}`, err, props); - throw err; - } - }) - ); + try { + await task.perform(props); + } catch (err) { + Logger.error(`Error processing task in ${name}`, err, props); + throw err; + } + }) + ) + .catch((err) => { + Logger.fatal("Error starting taskQueue", err); + }); } diff --git a/server/utils/authentication.ts b/server/utils/authentication.ts index c528f3bdf..f8d6a49f6 100644 --- a/server/utils/authentication.ts +++ b/server/utils/authentication.ts @@ -60,7 +60,7 @@ export async function signIn( await user.updateSignedIn(ctx.request.ip); // don't await event creation for a faster sign-in - Event.create({ + void Event.create({ name: "users.signin", actorId: user.id, userId: user.id, diff --git a/server/utils/i18n.ts b/server/utils/i18n.ts index 3cb03a86e..6581b3016 100644 --- a/server/utils/i18n.ts +++ b/server/utils/i18n.ts @@ -25,9 +25,10 @@ export function opts(user?: User | null) { * * @returns i18n instance */ -export function initI18n() { +export async function initI18n() { const lng = unicodeCLDRtoBCP47(env.DEFAULT_LANGUAGE); - i18n.use(backend).init({ + i18n.use(backend); + await i18n.init({ compatibilityJSON: "v3", backend: { loadPath: (language: string) => diff --git a/shared/editor/extensions/Mermaid.ts b/shared/editor/extensions/Mermaid.ts index c602f7d37..cef6d0b37 100644 --- a/shared/editor/extensions/Mermaid.ts +++ b/shared/editor/extensions/Mermaid.ts @@ -62,7 +62,7 @@ function getNewState({ element.classList.remove("diagram-hidden"); } - import("mermaid").then((module) => { + void import("mermaid").then((module) => { module.default.initialize({ startOnLoad: true, // TODO: Make dynamic based on the width of the editor or remove in diff --git a/shared/editor/nodes/Image.tsx b/shared/editor/nodes/Image.tsx index 356f1ed94..c2186a95c 100644 --- a/shared/editor/nodes/Image.tsx +++ b/shared/editor/nodes/Image.tsx @@ -212,7 +212,7 @@ export default class Image extends SimpleImage { (event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); - downloadImageNode(node); + void downloadImageNode(node); }; component = (props: ComponentProps) => ( @@ -292,7 +292,7 @@ export default class Image extends SimpleImage { return false; } - downloadImageNode(node); + void downloadImageNode(node); return true; },