Handle promise linting (#5488)
This commit is contained in:
@@ -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",
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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 <Redirect to="/" />;
|
||||
};
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -71,9 +71,9 @@ function CollectionDescription({ collection }: Props) {
|
||||
);
|
||||
|
||||
const handleChange = React.useCallback(
|
||||
(getValue) => {
|
||||
async (getValue) => {
|
||||
setDirty(true);
|
||||
handleSave(getValue);
|
||||
await handleSave(getValue);
|
||||
},
|
||||
[handleSave]
|
||||
);
|
||||
|
||||
@@ -38,13 +38,13 @@ const MenuItem = (
|
||||
ref: React.Ref<HTMLAnchorElement>
|
||||
) => {
|
||||
const handleClick = React.useCallback(
|
||||
(ev) => {
|
||||
async (ev) => {
|
||||
hide?.();
|
||||
|
||||
if (onClick) {
|
||||
ev.preventDefault();
|
||||
onClick(ev);
|
||||
await onClick(ev);
|
||||
}
|
||||
|
||||
hide?.();
|
||||
},
|
||||
[onClick, hide]
|
||||
);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function DesktopEventHandler() {
|
||||
action: {
|
||||
text: "Install now",
|
||||
onClick: () => {
|
||||
Desktop.bridge?.restartAndInstall();
|
||||
void Desktop.bridge?.restartAndInstall();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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]
|
||||
);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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<ReturnType<typeof setTimeout>>();
|
||||
@@ -57,10 +55,6 @@ function HoverPreviewInternal({ element, id, onClose }: Props) {
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (slug) {
|
||||
documents.prefetchDocument(slug);
|
||||
}
|
||||
|
||||
startOpenTimer();
|
||||
|
||||
if (cardRef.current) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -73,11 +73,11 @@ export default function LanguagePrompt() {
|
||||
</Trans>
|
||||
<br />
|
||||
<Link
|
||||
onClick={() => {
|
||||
auth.updateUser({
|
||||
onClick={async () => {
|
||||
ui.setLanguagePromptDismissed();
|
||||
await auth.updateUser({
|
||||
language,
|
||||
});
|
||||
ui.setLanguagePromptDismissed();
|
||||
}}
|
||||
>
|
||||
{t("Change Language")}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class PaginatedList<T extends PaginatedItem> extends React.Component<Props<T>> {
|
||||
allowLoadMore = true;
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchResults();
|
||||
void this.fetchResults();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props<T>) {
|
||||
@@ -79,7 +79,7 @@ class PaginatedList<T extends PaginatedItem> extends React.Component<Props<T>> {
|
||||
!isEqual(prevProps.options, this.props.options)
|
||||
) {
|
||||
this.reset();
|
||||
this.fetchResults();
|
||||
void this.fetchResults();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function SearchActions() {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!searches.isLoaded) {
|
||||
searches.fetchPage({});
|
||||
void searches.fetchPage({});
|
||||
}
|
||||
}, [searches]);
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ function AppSidebar() {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!user.isViewer) {
|
||||
documents.fetchDrafts();
|
||||
documents.fetchTemplates();
|
||||
void documents.fetchDrafts();
|
||||
void documents.fetchTemplates();
|
||||
}
|
||||
}, [documents, user.isViewer]);
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -51,7 +51,7 @@ function DraggableCollectionLink({
|
||||
] = useDrop({
|
||||
accept: "collection",
|
||||
drop: (item: DragObject) => {
|
||||
collections.move(
|
||||
void collections.move(
|
||||
item.id,
|
||||
fractionalIndex(collection.index, belowCollectionIndex)
|
||||
);
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
},
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function Version() {
|
||||
}
|
||||
}
|
||||
|
||||
loadReleases();
|
||||
void loadReleases();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -380,9 +380,9 @@ class WebsocketProvider extends React.Component<Props> {
|
||||
// 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<Props> {
|
||||
documents.inCollection(event.collectionId).forEach((document) => {
|
||||
policies.remove(document.id);
|
||||
});
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
// received when a user is removed from having access to a collection
|
||||
|
||||
@@ -139,7 +139,7 @@ class LinkEditor extends React.Component<Props, State> {
|
||||
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<Props, State> {
|
||||
subtitle={dictionary.createNewDoc}
|
||||
icon={<PlusIcon />}
|
||||
onPointerMove={() => this.handleFocusLink(results.length)}
|
||||
onClick={() => {
|
||||
this.handleCreateLink(suggestedLinkTitle);
|
||||
onClick={async () => {
|
||||
await this.handleCreateLink(suggestedLinkTitle);
|
||||
|
||||
if (this.initialSelectionLength) {
|
||||
this.moveSelectionToEnd();
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -82,7 +82,7 @@ export default function LinkToolbar({
|
||||
)
|
||||
);
|
||||
|
||||
createAndInsertLink(view, title, href, {
|
||||
return createAndInsertLink(view, title, href, {
|
||||
onCreateLink,
|
||||
onShowToast: showToast,
|
||||
dictionary,
|
||||
|
||||
@@ -54,7 +54,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isActive) {
|
||||
request();
|
||||
void request();
|
||||
}
|
||||
}, [request, isActive]);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function useEmbeds(loadIfMissing = false) {
|
||||
}
|
||||
|
||||
if (!integrations.isLoaded && !integrations.isFetching && loadIfMissing) {
|
||||
fetchEmbedIntegrations();
|
||||
void fetchEmbedIntegrations();
|
||||
}
|
||||
}, [integrations, loadIfMissing]);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}}
|
||||
/>
|
||||
</Style>
|
||||
|
||||
@@ -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]
|
||||
);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -53,7 +53,7 @@ const MembershipPreview = ({ collection, limit = 8 }: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
void fetchData();
|
||||
}, [
|
||||
isMobile,
|
||||
collection.permission,
|
||||
|
||||
@@ -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<HTMLInputElement>) => {
|
||||
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,
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -114,7 +114,7 @@ function CommentThread({
|
||||
if (!topRef.current) {
|
||||
return;
|
||||
}
|
||||
scrollIntoView(topRef.current, {
|
||||
return scrollIntoView(topRef.current, {
|
||||
scrollMode: "if-needed",
|
||||
behavior: "smooth",
|
||||
block: "end",
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
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<Props> {
|
||||
|
||||
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<Props> {
|
||||
});
|
||||
|
||||
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<Props> {
|
||||
}
|
||||
|
||||
if (document?.collectionId) {
|
||||
this.onSave({
|
||||
void this.onSave({
|
||||
publish: true,
|
||||
done: true,
|
||||
});
|
||||
@@ -324,12 +325,14 @@ class DocumentScene extends React.Component<Props> {
|
||||
}
|
||||
};
|
||||
|
||||
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<Props> {
|
||||
this.title = value;
|
||||
this.props.document.title = value;
|
||||
this.updateIsDirty();
|
||||
this.autosave();
|
||||
void this.autosave();
|
||||
});
|
||||
|
||||
goBack = () => {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -76,7 +76,7 @@ function SharePopover({
|
||||
|
||||
React.useEffect(() => {
|
||||
if (visible && team.sharing) {
|
||||
document.share();
|
||||
void document.share();
|
||||
buttonRef.current?.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ function DocumentNew() {
|
||||
}
|
||||
}
|
||||
|
||||
createDocument();
|
||||
void createDocument();
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -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<HTMLInputElement>) => {
|
||||
const updatedQuery = ev.target.value;
|
||||
setQuery(updatedQuery);
|
||||
debouncedFetch(updatedQuery);
|
||||
void debouncedFetch(updatedQuery);
|
||||
},
|
||||
[debouncedFetch]
|
||||
);
|
||||
|
||||
@@ -29,7 +29,7 @@ function Home() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
React.useEffect(() => {
|
||||
pins.fetchPage();
|
||||
void pins.fetchPage();
|
||||
}, [pins]);
|
||||
|
||||
const canManageTeam = usePolicy(team).manage;
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import useStores from "~/hooks/useStores";
|
||||
|
||||
const Logout = () => {
|
||||
const { auth } = useStores();
|
||||
auth.logout();
|
||||
void auth.logout();
|
||||
return <Redirect to="/" />;
|
||||
};
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ class Search extends React.Component<Props> {
|
||||
handleKeyDown = (ev: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (ev.key === "Enter") {
|
||||
this.updateLocation(ev.currentTarget.value);
|
||||
this.fetchResults();
|
||||
void this.fetchResults();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ class Search extends React.Component<Props> {
|
||||
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<Props> {
|
||||
this.allowLoadMore = true;
|
||||
// To prevent "no results" showing before debounce kicks in
|
||||
this.isLoading = true;
|
||||
this.fetchResults();
|
||||
void this.fetchResults();
|
||||
};
|
||||
|
||||
handleFilterChange = (search: {
|
||||
|
||||
@@ -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() {
|
||||
<Tooltip tooltip={t("Remove search")} delay={150}>
|
||||
<RemoveButton
|
||||
aria-label={t("Remove search")}
|
||||
onClick={(ev) => {
|
||||
onClick={async (ev) => {
|
||||
ev.preventDefault();
|
||||
searchQuery.delete();
|
||||
await searchQuery.delete();
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
|
||||
@@ -18,7 +18,7 @@ function UserFilter(props: Props) {
|
||||
const { users } = useStores();
|
||||
|
||||
React.useEffect(() => {
|
||||
users.fetchPage({
|
||||
void users.fetchPage({
|
||||
limit: 100,
|
||||
});
|
||||
}, [users]);
|
||||
|
||||
@@ -42,7 +42,7 @@ function GoogleAnalytics() {
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
integrations.fetchPage({
|
||||
void integrations.fetchPage({
|
||||
type: IntegrationType.Analytics,
|
||||
});
|
||||
}, [integrations]);
|
||||
|
||||
@@ -68,7 +68,7 @@ function Members() {
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
void fetchData();
|
||||
}, [query, sort, filter, page, direction, users, users.counts.all]);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -46,7 +46,7 @@ function Security() {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!providers && !loading) {
|
||||
request();
|
||||
void request();
|
||||
}
|
||||
}, [loading, providers, request]);
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ function SelfHosted() {
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
integrations.fetchPage({
|
||||
void integrations.fetchPage({
|
||||
type: IntegrationType.Embed,
|
||||
});
|
||||
}, [integrations]);
|
||||
|
||||
@@ -53,7 +53,7 @@ function Shares() {
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
void fetchData();
|
||||
}, [query, sort, page, direction, shares]);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -71,7 +71,7 @@ class ImageUpload extends React.Component<RootStore & Props> {
|
||||
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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -232,8 +232,8 @@ export default class CollectionsStore extends BaseStore<Collection> {
|
||||
|
||||
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) =>
|
||||
|
||||
@@ -179,7 +179,7 @@ export default class UsersStore extends BaseStore<User> {
|
||||
|
||||
@action
|
||||
async delete(user: User, options: Record<string, any> = {}) {
|
||||
super.delete(user, options);
|
||||
await super.delete(user, options);
|
||||
|
||||
if (!user.isSuspended && user.lastActiveAt) {
|
||||
this.counts.active -= 1;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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`,
|
||||
{
|
||||
|
||||
@@ -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(
|
||||
{
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -64,7 +64,7 @@ class View extends IdModel {
|
||||
|
||||
if (!created) {
|
||||
model.count += 1;
|
||||
model.save(options);
|
||||
await model.save(options);
|
||||
}
|
||||
|
||||
return model;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -338,7 +338,7 @@ describe("revisions.create", () => {
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
subscription.destroy();
|
||||
await subscription.destroy();
|
||||
|
||||
const task = new RevisionCreatedNotificationsTask();
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@ export default async function main(exit = false) {
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
main(true);
|
||||
void main(true);
|
||||
}
|
||||
|
||||
@@ -81,5 +81,5 @@ export default async function main(exit = false) {
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
main(true);
|
||||
void main(true);
|
||||
}
|
||||
|
||||
@@ -67,5 +67,5 @@ export default async function main(exit = false) {
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
main(true);
|
||||
void main(true);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -48,5 +48,5 @@ export default async function main(exit = false) {
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
main(true);
|
||||
void main(true);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user