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;
},