Move toasts to sonner (#6053)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { ToolsIcon, TrashIcon, UserIcon } from "outline-icons";
|
import { ToolsIcon, TrashIcon, UserIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import stores from "~/stores";
|
import { toast } from "sonner";
|
||||||
import { createAction } from "~/actions";
|
import { createAction } from "~/actions";
|
||||||
import { DeveloperSection } from "~/actions/sections";
|
import { DeveloperSection } from "~/actions/sections";
|
||||||
import env from "~/env";
|
import env from "~/env";
|
||||||
@@ -15,7 +15,7 @@ export const clearIndexedDB = createAction({
|
|||||||
section: DeveloperSection,
|
section: DeveloperSection,
|
||||||
perform: async ({ t }) => {
|
perform: async ({ t }) => {
|
||||||
await deleteAllDatabases();
|
await deleteAllDatabases();
|
||||||
stores.toasts.showToast(t("IndexedDB cache deleted"));
|
toast.message(t("IndexedDB cache deleted"));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -29,9 +29,9 @@ export const createTestUsers = createAction({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await client.post("/developer.create_test_users", { count });
|
await client.post("/developer.create_test_users", { count });
|
||||||
stores.toasts.showToast(`${count} test users created`);
|
toast.message(`${count} test users created`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
stores.toasts.showToast(err.message, { type: "error" });
|
toast.error(err.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -41,15 +41,8 @@ export const createToast = createAction({
|
|||||||
section: DeveloperSection,
|
section: DeveloperSection,
|
||||||
visible: () => env.ENVIRONMENT === "development",
|
visible: () => env.ENVIRONMENT === "development",
|
||||||
perform: async () => {
|
perform: async () => {
|
||||||
stores.toasts.showToast("Hello world", {
|
toast.message("Hello world", {
|
||||||
type: "info",
|
duration: 30000,
|
||||||
timeout: 30000,
|
|
||||||
action: {
|
|
||||||
text: "Click me",
|
|
||||||
onClick: () => {
|
|
||||||
stores.toasts.showToast("Clicked!");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -60,7 +53,7 @@ export const toggleDebugLogging = createAction({
|
|||||||
section: DeveloperSection,
|
section: DeveloperSection,
|
||||||
perform: async ({ t }) => {
|
perform: async ({ t }) => {
|
||||||
Logger.debugLoggingEnabled = !Logger.debugLoggingEnabled;
|
Logger.debugLoggingEnabled = !Logger.debugLoggingEnabled;
|
||||||
stores.toasts.showToast(
|
toast.message(
|
||||||
Logger.debugLoggingEnabled
|
Logger.debugLoggingEnabled
|
||||||
? t("Debug logging enabled")
|
? t("Debug logging enabled")
|
||||||
: t("Debug logging disabled")
|
: t("Debug logging disabled")
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
CommentIcon,
|
CommentIcon,
|
||||||
} from "outline-icons";
|
} from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { ExportContentType, TeamPreference } from "@shared/types";
|
import { ExportContentType, TeamPreference } from "@shared/types";
|
||||||
import { getEventFiles } from "@shared/utils/files";
|
import { getEventFiles } from "@shared/utils/files";
|
||||||
import DocumentDelete from "~/scenes/DocumentDelete";
|
import DocumentDelete from "~/scenes/DocumentDelete";
|
||||||
@@ -209,13 +210,10 @@ export const publishDocument = createAction({
|
|||||||
await document.save(undefined, {
|
await document.save(undefined, {
|
||||||
publish: true,
|
publish: true,
|
||||||
});
|
});
|
||||||
stores.toasts.showToast(
|
toast.success(
|
||||||
t("Published {{ documentName }}", {
|
t("Published {{ documentName }}", {
|
||||||
documentName: document.noun,
|
documentName: document.noun,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
type: "success",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} else if (document) {
|
} else if (document) {
|
||||||
stores.dialogs.openModal({
|
stores.dialogs.openModal({
|
||||||
@@ -250,13 +248,10 @@ export const unpublishDocument = createAction({
|
|||||||
|
|
||||||
await document.unpublish();
|
await document.unpublish();
|
||||||
|
|
||||||
stores.toasts.showToast(
|
toast.message(
|
||||||
t("Unpublished {{ documentName }}", {
|
t("Unpublished {{ documentName }}", {
|
||||||
documentName: document.noun,
|
documentName: document.noun,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
type: "success",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -287,9 +282,7 @@ export const subscribeDocument = createAction({
|
|||||||
|
|
||||||
await document?.subscribe();
|
await document?.subscribe();
|
||||||
|
|
||||||
stores.toasts.showToast(t("Subscribed to document notifications"), {
|
toast.success(t("Subscribed to document notifications"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -319,9 +312,7 @@ export const unsubscribeDocument = createAction({
|
|||||||
|
|
||||||
await document?.unsubscribe(currentUserId);
|
await document?.unsubscribe(currentUserId);
|
||||||
|
|
||||||
stores.toasts.showToast(t("Unsubscribed from document notifications"), {
|
toast.success(t("Unsubscribed from document notifications"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -360,15 +351,11 @@ export const downloadDocumentAsPDF = createAction({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = stores.toasts.showToast(`${t("Exporting")}…`, {
|
const id = toast.loading(`${t("Exporting")}…`);
|
||||||
type: "loading",
|
|
||||||
timeout: 30 * 1000,
|
|
||||||
});
|
|
||||||
|
|
||||||
const document = stores.documents.get(activeDocumentId);
|
const document = stores.documents.get(activeDocumentId);
|
||||||
document
|
document
|
||||||
?.download(ExportContentType.Pdf)
|
?.download(ExportContentType.Pdf)
|
||||||
.finally(() => id && stores.toasts.hideToast(id));
|
.finally(() => id && toast.dismiss(id));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -479,12 +466,10 @@ export const pinDocumentToCollection = createAction({
|
|||||||
const collection = stores.collections.get(activeCollectionId);
|
const collection = stores.collections.get(activeCollectionId);
|
||||||
|
|
||||||
if (!collection || !location.pathname.startsWith(collection?.url)) {
|
if (!collection || !location.pathname.startsWith(collection?.url)) {
|
||||||
stores.toasts.showToast(t("Pinned to collection"));
|
toast.success(t("Pinned to collection"));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
stores.toasts.showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -521,12 +506,10 @@ export const pinDocumentToHome = createAction({
|
|||||||
await document?.pin();
|
await document?.pin();
|
||||||
|
|
||||||
if (location.pathname !== homePath()) {
|
if (location.pathname !== homePath()) {
|
||||||
stores.toasts.showToast(t("Pinned to home"));
|
toast.success(t("Pinned to home"));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
stores.toasts.showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -569,7 +552,7 @@ export const importDocument = createAction({
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
perform: ({ activeCollectionId, activeDocumentId, stores }) => {
|
perform: ({ activeCollectionId, activeDocumentId, stores }) => {
|
||||||
const { documents, toasts } = stores;
|
const { documents } = stores;
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "file";
|
input.type = "file";
|
||||||
input.accept = documents.importFileTypes.join(", ");
|
input.accept = documents.importFileTypes.join(", ");
|
||||||
@@ -589,9 +572,7 @@ export const importDocument = createAction({
|
|||||||
);
|
);
|
||||||
history.push(document.url);
|
history.push(document.url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toasts.showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -712,9 +693,7 @@ export const archiveDocument = createAction({
|
|||||||
}
|
}
|
||||||
|
|
||||||
await document.archive();
|
await document.archive();
|
||||||
stores.toasts.showToast(t("Document archived"), {
|
toast.success(t("Document archived"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import copy from "copy-to-clipboard";
|
|||||||
import { LinkIcon, RestoreIcon } from "outline-icons";
|
import { LinkIcon, RestoreIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { matchPath } from "react-router-dom";
|
import { matchPath } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import stores from "~/stores";
|
import stores from "~/stores";
|
||||||
import { createAction } from "~/actions";
|
import { createAction } from "~/actions";
|
||||||
import { RevisionSection } from "~/actions/sections";
|
import { RevisionSection } from "~/actions/sections";
|
||||||
@@ -68,9 +69,7 @@ export const copyLinkToRevision = createAction({
|
|||||||
copy(url, {
|
copy(url, {
|
||||||
format: "text/plain",
|
format: "text/plain",
|
||||||
onCopy: () => {
|
onCopy: () => {
|
||||||
stores.toasts.showToast(t("Link copied"), {
|
toast.message(t("Link copied"));
|
||||||
type: "info",
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import flattenDeep from "lodash/flattenDeep";
|
import flattenDeep from "lodash/flattenDeep";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { Optional } from "utility-types";
|
import { Optional } from "utility-types";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import {
|
import {
|
||||||
@@ -77,9 +78,7 @@ export function actionToMenuItem(
|
|||||||
try {
|
try {
|
||||||
action.perform?.(context);
|
action.perform?.(context);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
context.stores.toasts.showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selected: action.selected?.(context),
|
selected: action.selected?.(context),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Initials from "./Initials";
|
|||||||
|
|
||||||
export enum AvatarSize {
|
export enum AvatarSize {
|
||||||
Small = 16,
|
Small = 16,
|
||||||
|
Toast = 18,
|
||||||
Medium = 24,
|
Medium = 24,
|
||||||
Large = 32,
|
Large = 32,
|
||||||
XLarge = 48,
|
XLarge = 48,
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Collection from "~/models/Collection";
|
import Collection from "~/models/Collection";
|
||||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { homePath } from "~/utils/routeHelpers";
|
import { homePath } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -18,7 +18,6 @@ type Props = {
|
|||||||
function CollectionDeleteDialog({ collection, onSubmit }: Props) {
|
function CollectionDeleteDialog({ collection, onSubmit }: Props) {
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const { ui } = useStores();
|
const { ui } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ function CollectionDeleteDialog({ collection, onSubmit }: Props) {
|
|||||||
|
|
||||||
await collection.delete();
|
await collection.delete();
|
||||||
onSubmit();
|
onSubmit();
|
||||||
showToast(t("Collection deleted"), { type: "success" });
|
toast.success(t("Collection deleted"));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||||||
import { transparentize } from "polished";
|
import { transparentize } from "polished";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { s } from "@shared/styles";
|
import { s } from "@shared/styles";
|
||||||
import Collection from "~/models/Collection";
|
import Collection from "~/models/Collection";
|
||||||
@@ -13,7 +14,6 @@ import LoadingIndicator from "~/components/LoadingIndicator";
|
|||||||
import NudeButton from "~/components/NudeButton";
|
import NudeButton from "~/components/NudeButton";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
collection: Collection;
|
collection: Collection;
|
||||||
@@ -21,7 +21,6 @@ type Props = {
|
|||||||
|
|
||||||
function CollectionDescription({ collection }: Props) {
|
function CollectionDescription({ collection }: Props) {
|
||||||
const { collections } = useStores();
|
const { collections } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isExpanded, setExpanded] = React.useState(false);
|
const [isExpanded, setExpanded] = React.useState(false);
|
||||||
const [isEditing, setEditing] = React.useState(false);
|
const [isEditing, setEditing] = React.useState(false);
|
||||||
@@ -59,15 +58,11 @@ function CollectionDescription({ collection }: Props) {
|
|||||||
});
|
});
|
||||||
setDirty(false);
|
setDirty(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(
|
toast.error(t("Sorry, an error occurred saving the collection"));
|
||||||
t("Sorry, an error occurred saving the collection", {
|
|
||||||
type: "error",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}, 1000),
|
}, 1000),
|
||||||
[collection, showToast, t]
|
[collection, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChange = React.useCallback(
|
const handleChange = React.useCallback(
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Comment from "~/models/Comment";
|
import Comment from "~/models/Comment";
|
||||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
comment: Comment;
|
comment: Comment;
|
||||||
@@ -14,7 +14,6 @@ type Props = {
|
|||||||
|
|
||||||
function CommentDeleteDialog({ comment, onSubmit }: Props) {
|
function CommentDeleteDialog({ comment, onSubmit }: Props) {
|
||||||
const { comments } = useStores();
|
const { comments } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const hasChildComments = comments.inThread(comment.id).length > 1;
|
const hasChildComments = comments.inThread(comment.id).length > 1;
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ function CommentDeleteDialog({ comment, onSubmit }: Props) {
|
|||||||
await comment.delete();
|
await comment.delete();
|
||||||
onSubmit?.();
|
onSubmit?.();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, { type: "error" });
|
toast.error(err.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/** Callback when the dialog is submitted */
|
/** Callback when the dialog is submitted */
|
||||||
@@ -30,7 +30,6 @@ const ConfirmationDialog: React.FC<Props> = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [isSaving, setIsSaving] = React.useState(false);
|
const [isSaving, setIsSaving] = React.useState(false);
|
||||||
const { dialogs } = useStores();
|
const { dialogs } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = React.useCallback(
|
||||||
async (ev: React.SyntheticEvent) => {
|
async (ev: React.SyntheticEvent) => {
|
||||||
@@ -40,14 +39,12 @@ const ConfirmationDialog: React.FC<Props> = ({
|
|||||||
await onSubmit();
|
await onSubmit();
|
||||||
dialogs.closeAllModals();
|
dialogs.closeAllModals();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onSubmit, dialogs, showToast]
|
[onSubmit, dialogs]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { HomeIcon } from "outline-icons";
|
import { HomeIcon } from "outline-icons";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { Optional } from "utility-types";
|
import { Optional } from "utility-types";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import CollectionIcon from "~/components/Icons/CollectionIcon";
|
import CollectionIcon from "~/components/Icons/CollectionIcon";
|
||||||
import InputSelect from "~/components/InputSelect";
|
import InputSelect from "~/components/InputSelect";
|
||||||
import { IconWrapper } from "~/components/Sidebar/components/SidebarLink";
|
import { IconWrapper } from "~/components/Sidebar/components/SidebarLink";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type DefaultCollectionInputSelectProps = Optional<
|
type DefaultCollectionInputSelectProps = Optional<
|
||||||
React.ComponentProps<typeof InputSelect>
|
React.ComponentProps<typeof InputSelect>
|
||||||
@@ -25,7 +25,6 @@ const DefaultCollectionInputSelect = ({
|
|||||||
const { collections } = useStores();
|
const { collections } = useStores();
|
||||||
const [fetching, setFetching] = useState(false);
|
const [fetching, setFetching] = useState(false);
|
||||||
const [fetchError, setFetchError] = useState();
|
const [fetchError, setFetchError] = useState();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
@@ -36,11 +35,8 @@ const DefaultCollectionInputSelect = ({
|
|||||||
limit: 100,
|
limit: 100,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showToast(
|
toast.error(
|
||||||
t("Collections could not be loaded, please reload the app"),
|
t("Collections could not be loaded, please reload the app")
|
||||||
{
|
|
||||||
type: "error",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
setFetchError(error);
|
setFetchError(error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -49,7 +45,7 @@ const DefaultCollectionInputSelect = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void fetchData();
|
void fetchData();
|
||||||
}, [showToast, fetchError, t, fetching, collections]);
|
}, [fetchError, t, fetching, collections]);
|
||||||
|
|
||||||
const options = React.useMemo(
|
const options = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import KeyboardShortcuts from "~/scenes/KeyboardShortcuts";
|
import KeyboardShortcuts from "~/scenes/KeyboardShortcuts";
|
||||||
import { useDesktopTitlebar } from "~/hooks/useDesktopTitlebar";
|
import { useDesktopTitlebar } from "~/hooks/useDesktopTitlebar";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import Desktop from "~/utils/Desktop";
|
import Desktop from "~/utils/Desktop";
|
||||||
|
|
||||||
export default function DesktopEventHandler() {
|
export default function DesktopEventHandler() {
|
||||||
@@ -12,7 +12,6 @@ export default function DesktopEventHandler() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { dialogs } = useStores();
|
const { dialogs } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
Desktop.bridge?.redirect((path: string, replace = false) => {
|
Desktop.bridge?.redirect((path: string, replace = false) => {
|
||||||
@@ -24,11 +23,11 @@ export default function DesktopEventHandler() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Desktop.bridge?.updateDownloaded(() => {
|
Desktop.bridge?.updateDownloaded(() => {
|
||||||
showToast("An update is ready to install.", {
|
toast.message("An update is ready to install.", {
|
||||||
type: "info",
|
duration: Infinity,
|
||||||
timeout: Infinity,
|
dismissible: true,
|
||||||
action: {
|
action: {
|
||||||
text: "Install now",
|
label: t("Install now"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
void Desktop.bridge?.restartAndInstall();
|
void Desktop.bridge?.restartAndInstall();
|
||||||
},
|
},
|
||||||
@@ -50,7 +49,7 @@ export default function DesktopEventHandler() {
|
|||||||
content: <KeyboardShortcuts />,
|
content: <KeyboardShortcuts />,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, [t, history, dialogs, showToast]);
|
}, [t, history, dialogs]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { documentPath } from "~/utils/routeHelpers";
|
import { documentPath } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -14,7 +14,6 @@ type Props = {
|
|||||||
|
|
||||||
function DocumentTemplatizeDialog({ documentId }: Props) {
|
function DocumentTemplatizeDialog({ documentId }: Props) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { documents } = useStores();
|
const { documents } = useStores();
|
||||||
const document = documents.get(documentId);
|
const document = documents.get(documentId);
|
||||||
@@ -24,11 +23,9 @@ function DocumentTemplatizeDialog({ documentId }: Props) {
|
|||||||
const template = await document?.templatize();
|
const template = await document?.templatize();
|
||||||
if (template) {
|
if (template) {
|
||||||
history.push(documentPath(template));
|
history.push(documentPath(template));
|
||||||
showToast(t("Template created, go ahead and customize it"), {
|
toast.message(t("Template created, go ahead and customize it"));
|
||||||
type: "info",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [document, showToast, history, t]);
|
}, [document, history, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfirmationDialog
|
<ConfirmationDialog
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import type { Props as EditorProps, Editor as SharedEditor } from "~/editor";
|
|||||||
import useDictionary from "~/hooks/useDictionary";
|
import useDictionary from "~/hooks/useDictionary";
|
||||||
import useEmbeds from "~/hooks/useEmbeds";
|
import useEmbeds from "~/hooks/useEmbeds";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import useUserLocale from "~/hooks/useUserLocale";
|
import useUserLocale from "~/hooks/useUserLocale";
|
||||||
import { NotFoundError } from "~/utils/errors";
|
import { NotFoundError } from "~/utils/errors";
|
||||||
import { uploadFile } from "~/utils/files";
|
import { uploadFile } from "~/utils/files";
|
||||||
@@ -43,7 +42,6 @@ export type Props = Optional<
|
|||||||
| "onClickLink"
|
| "onClickLink"
|
||||||
| "embeds"
|
| "embeds"
|
||||||
| "dictionary"
|
| "dictionary"
|
||||||
| "onShowToast"
|
|
||||||
| "extensions"
|
| "extensions"
|
||||||
> & {
|
> & {
|
||||||
shareId?: string | undefined;
|
shareId?: string | undefined;
|
||||||
@@ -68,7 +66,6 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
|||||||
const userLocale = useUserLocale();
|
const userLocale = useUserLocale();
|
||||||
const locale = dateLocale(userLocale);
|
const locale = dateLocale(userLocale);
|
||||||
const { auth, comments, documents } = useStores();
|
const { auth, comments, documents } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const dictionary = useDictionary();
|
const dictionary = useDictionary();
|
||||||
const embeds = useEmbeds(!shareId);
|
const embeds = useEmbeds(!shareId);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -241,7 +238,6 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
|||||||
uploadFile: handleUploadFile,
|
uploadFile: handleUploadFile,
|
||||||
onFileUploadStart: props.onFileUploadStart,
|
onFileUploadStart: props.onFileUploadStart,
|
||||||
onFileUploadStop: props.onFileUploadStop,
|
onFileUploadStop: props.onFileUploadStop,
|
||||||
onShowToast: showToast,
|
|
||||||
dictionary,
|
dictionary,
|
||||||
isAttachment,
|
isAttachment,
|
||||||
});
|
});
|
||||||
@@ -252,7 +248,6 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
|||||||
props.onFileUploadStop,
|
props.onFileUploadStop,
|
||||||
dictionary,
|
dictionary,
|
||||||
handleUploadFile,
|
handleUploadFile,
|
||||||
showToast,
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -336,7 +331,6 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
|||||||
<LazyLoadedEditor
|
<LazyLoadedEditor
|
||||||
ref={mergeRefs([ref, localRef, handleRefChanged])}
|
ref={mergeRefs([ref, localRef, handleRefChanged])}
|
||||||
uploadFile={handleUploadFile}
|
uploadFile={handleUploadFile}
|
||||||
onShowToast={showToast}
|
|
||||||
embeds={embeds}
|
embeds={embeds}
|
||||||
userPreferences={preferences}
|
userPreferences={preferences}
|
||||||
dictionary={dictionary}
|
dictionary={dictionary}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { FileOperationFormat, NotificationEventType } from "@shared/types";
|
import { FileOperationFormat, NotificationEventType } from "@shared/types";
|
||||||
import Collection from "~/models/Collection";
|
import Collection from "~/models/Collection";
|
||||||
@@ -10,7 +11,6 @@ import Text from "~/components/Text";
|
|||||||
import env from "~/env";
|
import env from "~/env";
|
||||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import history from "~/utils/history";
|
import history from "~/utils/history";
|
||||||
import { settingsPath } from "~/utils/routeHelpers";
|
import { settingsPath } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
@@ -26,7 +26,6 @@ function ExportDialog({ collection, onSubmit }: Props) {
|
|||||||
const [includeAttachments, setIncludeAttachments] =
|
const [includeAttachments, setIncludeAttachments] =
|
||||||
React.useState<boolean>(true);
|
React.useState<boolean>(true);
|
||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { collections } = useStores();
|
const { collections } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const appName = env.APP_NAME;
|
const appName = env.APP_NAME;
|
||||||
@@ -48,23 +47,20 @@ function ExportDialog({ collection, onSubmit }: Props) {
|
|||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (collection) {
|
if (collection) {
|
||||||
await collection.export(format, includeAttachments);
|
await collection.export(format, includeAttachments);
|
||||||
showToast(
|
toast.success(t("Export started"), {
|
||||||
t(`Your file will be available in {{ location }} soon`, {
|
description: t(`Your file will be available in {{ location }} soon`, {
|
||||||
location: `"${t("Settings")} > ${t("Export")}"`,
|
location: `"${t("Settings")} > ${t("Export")}"`,
|
||||||
}),
|
}),
|
||||||
{
|
action: {
|
||||||
type: "success",
|
label: t("View"),
|
||||||
action: {
|
onClick: () => {
|
||||||
text: t("Go to exports"),
|
history.push(settingsPath("export"));
|
||||||
onClick: () => {
|
|
||||||
history.push(settingsPath("export"));
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
});
|
||||||
} else {
|
} else {
|
||||||
await collections.export(format, includeAttachments);
|
await collections.export(format, includeAttachments);
|
||||||
showToast(t("Export started"), { type: "success" });
|
toast.success(t("Export started"));
|
||||||
}
|
}
|
||||||
onSubmit();
|
onSubmit();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,24 +3,21 @@ import { ArchiveIcon } from "outline-icons";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useDrop } from "react-dnd";
|
import { useDrop } from "react-dnd";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { archivePath } from "~/utils/routeHelpers";
|
import { archivePath } from "~/utils/routeHelpers";
|
||||||
import SidebarLink, { DragObject } from "./SidebarLink";
|
import SidebarLink, { DragObject } from "./SidebarLink";
|
||||||
|
|
||||||
function ArchiveLink() {
|
function ArchiveLink() {
|
||||||
const { policies, documents } = useStores();
|
const { policies, documents } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
const [{ isDocumentDropping }, dropToArchiveDocument] = useDrop({
|
const [{ isDocumentDropping }, dropToArchiveDocument] = useDrop({
|
||||||
accept: "document",
|
accept: "document",
|
||||||
drop: async (item: DragObject) => {
|
drop: async (item: DragObject) => {
|
||||||
const document = documents.get(item.id);
|
const document = documents.get(item.id);
|
||||||
await document?.archive();
|
await document?.archive();
|
||||||
showToast(t("Document archived"), {
|
toast.success(t("Document archived"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
canDrop: (item) => policies.abilities(item.id).archive,
|
canDrop: (item) => policies.abilities(item.id).archive,
|
||||||
collect: (monitor) => ({
|
collect: (monitor) => ({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useDrop } from "react-dnd";
|
import { useDrop } from "react-dnd";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import Collection from "~/models/Collection";
|
import Collection from "~/models/Collection";
|
||||||
import Document from "~/models/Document";
|
import Document from "~/models/Document";
|
||||||
@@ -9,7 +10,6 @@ import DocumentsLoader from "~/components/DocumentsLoader";
|
|||||||
import { ResizingHeightContainer } from "~/components/ResizingHeightContainer";
|
import { ResizingHeightContainer } from "~/components/ResizingHeightContainer";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import DocumentLink from "./DocumentLink";
|
import DocumentLink from "./DocumentLink";
|
||||||
import DropCursor from "./DropCursor";
|
import DropCursor from "./DropCursor";
|
||||||
import EmptyCollectionPlaceholder from "./EmptyCollectionPlaceholder";
|
import EmptyCollectionPlaceholder from "./EmptyCollectionPlaceholder";
|
||||||
@@ -30,7 +30,6 @@ function CollectionLinkChildren({
|
|||||||
prefetchDocument,
|
prefetchDocument,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const can = usePolicy(collection);
|
const can = usePolicy(collection);
|
||||||
const { showToast } = useToasts();
|
|
||||||
const manualSort = collection.sort.field === "index";
|
const manualSort = collection.sort.field === "index";
|
||||||
const { documents } = useStores();
|
const { documents } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -42,14 +41,10 @@ function CollectionLinkChildren({
|
|||||||
accept: "document",
|
accept: "document",
|
||||||
drop: (item: DragObject) => {
|
drop: (item: DragObject) => {
|
||||||
if (!manualSort && item.collectionId === collection?.id) {
|
if (!manualSort && item.collectionId === collection?.id) {
|
||||||
showToast(
|
toast.message(
|
||||||
t(
|
t(
|
||||||
"You can't reorder documents in an alphabetically sorted collection"
|
"You can't reorder documents in an alphabetically sorted collection"
|
||||||
),
|
)
|
||||||
{
|
|
||||||
type: "info",
|
|
||||||
timeout: 5000,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useDrag, useDrop } from "react-dnd";
|
|||||||
import { getEmptyImage } from "react-dnd-html5-backend";
|
import { getEmptyImage } from "react-dnd-html5-backend";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { NavigationNode } from "@shared/types";
|
import { NavigationNode } from "@shared/types";
|
||||||
import { sortNavigationNodes } from "@shared/utils/collections";
|
import { sortNavigationNodes } from "@shared/utils/collections";
|
||||||
@@ -18,7 +19,6 @@ import Tooltip from "~/components/Tooltip";
|
|||||||
import useBoolean from "~/hooks/useBoolean";
|
import useBoolean from "~/hooks/useBoolean";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import DocumentMenu from "~/menus/DocumentMenu";
|
import DocumentMenu from "~/menus/DocumentMenu";
|
||||||
import { newDocumentPath } from "~/utils/routeHelpers";
|
import { newDocumentPath } from "~/utils/routeHelpers";
|
||||||
import DropCursor from "./DropCursor";
|
import DropCursor from "./DropCursor";
|
||||||
@@ -53,7 +53,6 @@ function InnerDocumentLink(
|
|||||||
}: Props,
|
}: Props,
|
||||||
ref: React.RefObject<HTMLAnchorElement>
|
ref: React.RefObject<HTMLAnchorElement>
|
||||||
) {
|
) {
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { documents, policies } = useStores();
|
const { documents, policies } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const canUpdate = usePolicy(node.id).update;
|
const canUpdate = usePolicy(node.id).update;
|
||||||
@@ -222,14 +221,10 @@ function InnerDocumentLink(
|
|||||||
accept: "document",
|
accept: "document",
|
||||||
drop: (item: DragObject) => {
|
drop: (item: DragObject) => {
|
||||||
if (!manualSort) {
|
if (!manualSort) {
|
||||||
showToast(
|
toast.message(
|
||||||
t(
|
t(
|
||||||
"You can't reorder documents in an alphabetically sorted collection"
|
"You can't reorder documents in an alphabetically sorted collection"
|
||||||
),
|
)
|
||||||
{
|
|
||||||
type: "info",
|
|
||||||
timeout: 5000,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import Dropzone from "react-dropzone";
|
import Dropzone from "react-dropzone";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
import LoadingIndicator from "~/components/LoadingIndicator";
|
import LoadingIndicator from "~/components/LoadingIndicator";
|
||||||
import useImportDocument from "~/hooks/useImportDocument";
|
import useImportDocument from "~/hooks/useImportDocument";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
@@ -21,7 +21,6 @@ type Props = {
|
|||||||
function DropToImport({ disabled, children, collectionId, documentId }: Props) {
|
function DropToImport({ disabled, children, collectionId, documentId }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { documents } = useStores();
|
const { documents } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { handleFiles, isImporting } = useImportDocument(
|
const { handleFiles, isImporting } = useImportDocument(
|
||||||
collectionId,
|
collectionId,
|
||||||
documentId
|
documentId
|
||||||
@@ -35,13 +34,10 @@ function DropToImport({ disabled, children, collectionId, documentId }: Props) {
|
|||||||
const canDocument = usePolicy(documentId);
|
const canDocument = usePolicy(documentId);
|
||||||
|
|
||||||
const handleRejection = React.useCallback(() => {
|
const handleRejection = React.useCallback(() => {
|
||||||
showToast(
|
toast.error(
|
||||||
t("Document not supported – try Markdown, Plain text, HTML, or Word"),
|
t("Document not supported – try Markdown, Plain text, HTML, or Word")
|
||||||
{
|
|
||||||
type: "error",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}, [t, showToast]);
|
}, [t]);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
disabled ||
|
disabled ||
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { s } from "@shared/styles";
|
import { s } from "@shared/styles";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSubmit: (title: string) => Promise<void>;
|
onSubmit: (title: string) => Promise<void>;
|
||||||
@@ -22,7 +22,6 @@ function EditableTitle(
|
|||||||
const [isEditing, setIsEditing] = React.useState(false);
|
const [isEditing, setIsEditing] = React.useState(false);
|
||||||
const [originalValue, setOriginalValue] = React.useState(title);
|
const [originalValue, setOriginalValue] = React.useState(title);
|
||||||
const [value, setValue] = React.useState(title);
|
const [value, setValue] = React.useState(title);
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
React.useImperativeHandle(ref, () => ({
|
React.useImperativeHandle(ref, () => ({
|
||||||
setIsEditing,
|
setIsEditing,
|
||||||
@@ -78,14 +77,12 @@ function EditableTitle(
|
|||||||
setOriginalValue(trimmedValue);
|
setOriginalValue(trimmedValue);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setValue(originalValue);
|
setValue(originalValue);
|
||||||
showToast(error.message, {
|
toast.error(error.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[originalValue, showToast, value, onSubmit]
|
[originalValue, value, onSubmit]
|
||||||
);
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
import { CheckboxIcon, InfoIcon, WarningIcon } from "outline-icons";
|
|
||||||
import { darken } from "polished";
|
|
||||||
import * as React from "react";
|
|
||||||
import styled, { css } from "styled-components";
|
|
||||||
import { s } from "@shared/styles";
|
|
||||||
import { fadeAndScaleIn, pulse } from "~/styles/animations";
|
|
||||||
import { Toast as TToast } from "~/types";
|
|
||||||
import Spinner from "./Spinner";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
onRequestClose: () => void;
|
|
||||||
closeAfterMs?: number;
|
|
||||||
toast: TToast;
|
|
||||||
};
|
|
||||||
|
|
||||||
function Toast({ closeAfterMs = 3000, onRequestClose, toast }: Props) {
|
|
||||||
const timeout = React.useRef<ReturnType<typeof setTimeout>>();
|
|
||||||
const [pulse, setPulse] = React.useState(false);
|
|
||||||
const { action, type = "info", reoccurring } = toast;
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (toast.timeout !== 0) {
|
|
||||||
timeout.current = setTimeout(
|
|
||||||
onRequestClose,
|
|
||||||
toast.timeout || closeAfterMs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return () => timeout.current && clearTimeout(timeout.current);
|
|
||||||
}, [onRequestClose, toast, closeAfterMs]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (reoccurring) {
|
|
||||||
setPulse(!!reoccurring);
|
|
||||||
// must match animation time in css below vvv
|
|
||||||
setTimeout(() => setPulse(false), 250);
|
|
||||||
}
|
|
||||||
}, [reoccurring]);
|
|
||||||
|
|
||||||
const handlePause = React.useCallback(() => {
|
|
||||||
if (timeout.current) {
|
|
||||||
clearTimeout(timeout.current);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleResume = React.useCallback(() => {
|
|
||||||
if (timeout.current && toast.timeout !== 0) {
|
|
||||||
timeout.current = setTimeout(
|
|
||||||
onRequestClose,
|
|
||||||
toast.timeout || closeAfterMs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [onRequestClose, toast, closeAfterMs]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ListItem
|
|
||||||
$pulse={pulse}
|
|
||||||
onMouseEnter={handlePause}
|
|
||||||
onMouseLeave={handleResume}
|
|
||||||
>
|
|
||||||
<Container onClick={action ? undefined : onRequestClose}>
|
|
||||||
{type === "loading" && <Spinner />}
|
|
||||||
{type === "info" && <InfoIcon />}
|
|
||||||
{type === "success" && <CheckboxIcon checked />}
|
|
||||||
{(type === "warning" || type === "error") && <WarningIcon />}
|
|
||||||
<Message>{toast.message}</Message>
|
|
||||||
{action && <Action onClick={action.onClick}>{action.text}</Action>}
|
|
||||||
</Container>
|
|
||||||
</ListItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Action = styled.span`
|
|
||||||
display: inline-block;
|
|
||||||
padding: 4px 8px;
|
|
||||||
color: ${s("toastText")};
|
|
||||||
background: ${(props) => darken(0.05, props.theme.toastBackground)};
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-left: 8px;
|
|
||||||
margin-right: -4px;
|
|
||||||
font-weight: 500;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: ${(props) => darken(0.1, props.theme.toastBackground)};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ListItem = styled.li<{ $pulse?: boolean }>`
|
|
||||||
${(props) =>
|
|
||||||
props.$pulse &&
|
|
||||||
css`
|
|
||||||
animation: ${pulse} 250ms;
|
|
||||||
`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Container = styled.div`
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
animation: ${fadeAndScaleIn} 100ms ease;
|
|
||||||
margin: 8px 0;
|
|
||||||
padding: 0 12px;
|
|
||||||
color: ${s("toastText")};
|
|
||||||
background: ${s("toastBackground")};
|
|
||||||
font-size: 15px;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: ${(props) => darken(0.05, props.theme.toastBackground)};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Message = styled.div`
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 10px 4px;
|
|
||||||
user-select: none;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default Toast;
|
|
||||||
@@ -1,34 +1,28 @@
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import styled from "styled-components";
|
import { Toaster } from "sonner";
|
||||||
import { depths } from "@shared/styles";
|
import { useTheme } from "styled-components";
|
||||||
import Toast from "~/components/Toast";
|
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import { Toast as TToast } from "~/types";
|
|
||||||
|
|
||||||
function Toasts() {
|
function Toasts() {
|
||||||
const { toasts } = useStores();
|
const { ui } = useStores();
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List>
|
<Toaster
|
||||||
{toasts.orderedData.map((toast: TToast) => (
|
theme={ui.resolvedTheme}
|
||||||
<Toast
|
toastOptions={{
|
||||||
key={toast.id}
|
duration: 5000,
|
||||||
toast={toast}
|
style: {
|
||||||
onRequestClose={() => toasts.hideToast(toast.id)}
|
color: theme.toastText,
|
||||||
/>
|
background: theme.toastBackground,
|
||||||
))}
|
border: `1px solid ${theme.divider}`,
|
||||||
</List>
|
fontFamily: theme.fontFamily,
|
||||||
|
fontSize: "14px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const List = styled.ol`
|
|
||||||
position: fixed;
|
|
||||||
left: 16px;
|
|
||||||
bottom: 16px;
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
z-index: ${depths.toasts};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default observer(Toasts);
|
export default observer(Toasts);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { action, observable } from "mobx";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { io, Socket } from "socket.io-client";
|
import { io, Socket } from "socket.io-client";
|
||||||
|
import { toast } from "sonner";
|
||||||
import RootStore from "~/stores/RootStore";
|
import RootStore from "~/stores/RootStore";
|
||||||
import Collection from "~/models/Collection";
|
import Collection from "~/models/Collection";
|
||||||
import Comment from "~/models/Comment";
|
import Comment from "~/models/Comment";
|
||||||
@@ -77,7 +78,6 @@ class WebsocketProvider extends React.Component<Props> {
|
|||||||
this.socket.authenticated = false;
|
this.socket.authenticated = false;
|
||||||
const {
|
const {
|
||||||
auth,
|
auth,
|
||||||
toasts,
|
|
||||||
documents,
|
documents,
|
||||||
collections,
|
collections,
|
||||||
groups,
|
groups,
|
||||||
@@ -111,9 +111,7 @@ class WebsocketProvider extends React.Component<Props> {
|
|||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
this.socket.authenticated = false;
|
this.socket.authenticated = false;
|
||||||
}
|
}
|
||||||
toasts.showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Mark } from "prosemirror-model";
|
|||||||
import { Selection } from "prosemirror-state";
|
import { Selection } from "prosemirror-state";
|
||||||
import { EditorView } from "prosemirror-view";
|
import { EditorView } from "prosemirror-view";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { s, hideScrollbars } from "@shared/styles";
|
import { s, hideScrollbars } from "@shared/styles";
|
||||||
import { isInternalUrl, sanitizeUrl } from "@shared/utils/urls";
|
import { isInternalUrl, sanitizeUrl } from "@shared/utils/urls";
|
||||||
@@ -16,7 +17,6 @@ import Flex from "~/components/Flex";
|
|||||||
import { ResizingHeightContainer } from "~/components/ResizingHeightContainer";
|
import { ResizingHeightContainer } from "~/components/ResizingHeightContainer";
|
||||||
import Scrollable from "~/components/Scrollable";
|
import Scrollable from "~/components/Scrollable";
|
||||||
import { Dictionary } from "~/hooks/useDictionary";
|
import { Dictionary } from "~/hooks/useDictionary";
|
||||||
import { ToastOptions } from "~/types";
|
|
||||||
import Logger from "~/utils/Logger";
|
import Logger from "~/utils/Logger";
|
||||||
import Input from "./Input";
|
import Input from "./Input";
|
||||||
import LinkSearchResult from "./LinkSearchResult";
|
import LinkSearchResult from "./LinkSearchResult";
|
||||||
@@ -47,7 +47,6 @@ type Props = {
|
|||||||
href: string,
|
href: string,
|
||||||
event: React.MouseEvent<HTMLButtonElement>
|
event: React.MouseEvent<HTMLButtonElement>
|
||||||
) => void;
|
) => void;
|
||||||
onShowToast: (message: string, options?: ToastOptions) => void;
|
|
||||||
view: EditorView;
|
view: EditorView;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -240,7 +239,7 @@ class LinkEditor extends React.Component<Props, State> {
|
|||||||
try {
|
try {
|
||||||
this.props.onClickLink(this.href, event);
|
this.props.onClickLink(this.href, event);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.props.onShowToast(this.props.dictionary.openLinkError);
|
toast.error(this.props.dictionary.openLinkError);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import createAndInsertLink from "@shared/editor/commands/createAndInsertLink";
|
|||||||
import { creatingUrlPrefix } from "@shared/utils/urls";
|
import { creatingUrlPrefix } from "@shared/utils/urls";
|
||||||
import useDictionary from "~/hooks/useDictionary";
|
import useDictionary from "~/hooks/useDictionary";
|
||||||
import useEventListener from "~/hooks/useEventListener";
|
import useEventListener from "~/hooks/useEventListener";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { useEditor } from "./EditorContext";
|
import { useEditor } from "./EditorContext";
|
||||||
import FloatingToolbar from "./FloatingToolbar";
|
import FloatingToolbar from "./FloatingToolbar";
|
||||||
import LinkEditor, { SearchResult } from "./LinkEditor";
|
import LinkEditor, { SearchResult } from "./LinkEditor";
|
||||||
@@ -39,7 +38,6 @@ export default function LinkToolbar({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
const dictionary = useDictionary();
|
const dictionary = useDictionary();
|
||||||
const { view } = useEditor();
|
const { view } = useEditor();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const menuRef = React.useRef<HTMLDivElement>(null);
|
const menuRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEventListener("mousedown", (event: Event) => {
|
useEventListener("mousedown", (event: Event) => {
|
||||||
@@ -84,11 +82,10 @@ export default function LinkToolbar({
|
|||||||
|
|
||||||
return createAndInsertLink(view, title, href, {
|
return createAndInsertLink(view, title, href, {
|
||||||
onCreateLink,
|
onCreateLink,
|
||||||
onShowToast: showToast,
|
|
||||||
dictionary,
|
dictionary,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[onCreateLink, onClose, view, dictionary, showToast]
|
[onCreateLink, onClose, view, dictionary]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnSelectLink = React.useCallback(
|
const handleOnSelectLink = React.useCallback(
|
||||||
@@ -137,7 +134,6 @@ export default function LinkToolbar({
|
|||||||
onCreateLink={onCreateLink ? handleOnCreateLink : undefined}
|
onCreateLink={onCreateLink ? handleOnCreateLink : undefined}
|
||||||
onSelectLink={handleOnSelectLink}
|
onSelectLink={handleOnSelectLink}
|
||||||
onRemoveLink={onClose}
|
onRemoveLink={onClose}
|
||||||
onShowToast={showToast}
|
|
||||||
onClickLink={onClickLink}
|
onClickLink={onClickLink}
|
||||||
onSearchLink={onSearchLink}
|
onSearchLink={onSearchLink}
|
||||||
dictionary={dictionary}
|
dictionary={dictionary}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import useDictionary from "~/hooks/useDictionary";
|
|||||||
import useEventListener from "~/hooks/useEventListener";
|
import useEventListener from "~/hooks/useEventListener";
|
||||||
import useMobile from "~/hooks/useMobile";
|
import useMobile from "~/hooks/useMobile";
|
||||||
import usePrevious from "~/hooks/usePrevious";
|
import usePrevious from "~/hooks/usePrevious";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import getCodeMenuItems from "../menus/code";
|
import getCodeMenuItems from "../menus/code";
|
||||||
import getDividerMenuItems from "../menus/divider";
|
import getDividerMenuItems from "../menus/divider";
|
||||||
import getFormattingMenuItems from "../menus/formatting";
|
import getFormattingMenuItems from "../menus/formatting";
|
||||||
@@ -97,7 +96,6 @@ function useIsDragging() {
|
|||||||
export default function SelectionToolbar(props: Props) {
|
export default function SelectionToolbar(props: Props) {
|
||||||
const { onClose, readOnly, onOpen } = props;
|
const { onClose, readOnly, onOpen } = props;
|
||||||
const { view, commands } = useEditor();
|
const { view, commands } = useEditor();
|
||||||
const { showToast: onShowToast } = useToasts();
|
|
||||||
const dictionary = useDictionary();
|
const dictionary = useDictionary();
|
||||||
const menuRef = React.useRef<HTMLDivElement | null>(null);
|
const menuRef = React.useRef<HTMLDivElement | null>(null);
|
||||||
const isActive = useIsActive(view.state);
|
const isActive = useIsActive(view.state);
|
||||||
@@ -175,7 +173,6 @@ export default function SelectionToolbar(props: Props) {
|
|||||||
|
|
||||||
return createAndInsertLink(view, title, href, {
|
return createAndInsertLink(view, title, href, {
|
||||||
onCreateLink,
|
onCreateLink,
|
||||||
onShowToast,
|
|
||||||
dictionary,
|
dictionary,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -271,7 +268,6 @@ export default function SelectionToolbar(props: Props) {
|
|||||||
mark={range.mark}
|
mark={range.mark}
|
||||||
from={range.from}
|
from={range.from}
|
||||||
to={range.to}
|
to={range.to}
|
||||||
onShowToast={onShowToast}
|
|
||||||
onClickLink={props.onClickLink}
|
onClickLink={props.onClickLink}
|
||||||
onSearchLink={props.onSearchLink}
|
onSearchLink={props.onSearchLink}
|
||||||
onCreateLink={onCreateLink ? handleOnCreateLink : undefined}
|
onCreateLink={onCreateLink ? handleOnCreateLink : undefined}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import capitalize from "lodash/capitalize";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans } from "react-i18next";
|
import { Trans } from "react-i18next";
|
||||||
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import insertFiles from "@shared/editor/commands/insertFiles";
|
import insertFiles from "@shared/editor/commands/insertFiles";
|
||||||
import { EmbedDescriptor } from "@shared/editor/embeds";
|
import { EmbedDescriptor } from "@shared/editor/embeds";
|
||||||
@@ -15,7 +16,6 @@ import { AttachmentValidation } from "@shared/validations";
|
|||||||
import { Portal } from "~/components/Portal";
|
import { Portal } from "~/components/Portal";
|
||||||
import Scrollable from "~/components/Scrollable";
|
import Scrollable from "~/components/Scrollable";
|
||||||
import useDictionary from "~/hooks/useDictionary";
|
import useDictionary from "~/hooks/useDictionary";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import Logger from "~/utils/Logger";
|
import Logger from "~/utils/Logger";
|
||||||
import { useEditor } from "./EditorContext";
|
import { useEditor } from "./EditorContext";
|
||||||
import Input from "./Input";
|
import Input from "./Input";
|
||||||
@@ -77,7 +77,6 @@ export type Props<T extends MenuItem = MenuItem> = {
|
|||||||
|
|
||||||
function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
||||||
const { view, commands } = useEditor();
|
const { view, commands } = useEditor();
|
||||||
const { showToast: onShowToast } = useToasts();
|
|
||||||
const dictionary = useDictionary();
|
const dictionary = useDictionary();
|
||||||
const hasActivated = React.useRef(false);
|
const hasActivated = React.useRef(false);
|
||||||
const menuRef = React.useRef<HTMLDivElement>(null);
|
const menuRef = React.useRef<HTMLDivElement>(null);
|
||||||
@@ -292,7 +291,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
|||||||
const matches = "matcher" in insertItem && insertItem.matcher(href);
|
const matches = "matcher" in insertItem && insertItem.matcher(href);
|
||||||
|
|
||||||
if (!matches) {
|
if (!matches) {
|
||||||
onShowToast(dictionary.embedInvalidLink);
|
toast.error(dictionary.embedInvalidLink);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +364,6 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
|||||||
uploadFile,
|
uploadFile,
|
||||||
onFileUploadStart,
|
onFileUploadStart,
|
||||||
onFileUploadStop,
|
onFileUploadStop,
|
||||||
onShowToast,
|
|
||||||
dictionary,
|
dictionary,
|
||||||
isAttachment: inputRef.current?.accept === "*",
|
isAttachment: inputRef.current?.accept === "*",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -133,8 +133,6 @@ export type Props = {
|
|||||||
userPreferences?: UserPreferences | null;
|
userPreferences?: UserPreferences | null;
|
||||||
/** Whether embeds should be rendered without an iframe */
|
/** Whether embeds should be rendered without an iframe */
|
||||||
embedsDisabled?: boolean;
|
embedsDisabled?: boolean;
|
||||||
/** Callback when a toast message is triggered (eg "link copied") */
|
|
||||||
onShowToast: (message: string) => void;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
/** Optional style overrides for the container*/
|
/** Optional style overrides for the container*/
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import invariant from "invariant";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { documentPath } from "~/utils/routeHelpers";
|
import { documentPath } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
let importingLock = false;
|
let importingLock = false;
|
||||||
@@ -16,7 +16,6 @@ export default function useImportDocument(
|
|||||||
isImporting: boolean;
|
isImporting: boolean;
|
||||||
} {
|
} {
|
||||||
const { documents } = useStores();
|
const { documents } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const [isImporting, setImporting] = React.useState(false);
|
const [isImporting, setImporting] = React.useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -55,15 +54,13 @@ export default function useImportDocument(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(`${t("Could not import file")}. ${err.message}`, {
|
toast.error(`${t("Could not import file")}. ${err.message}`);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setImporting(false);
|
setImporting(false);
|
||||||
importingLock = false;
|
importingLock = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[t, documents, history, showToast, collectionId, documentId]
|
[t, documents, history, collectionId, documentId]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { QueryNotices } from "@shared/types";
|
import { QueryNotices } from "@shared/types";
|
||||||
import useQuery from "./useQuery";
|
import useQuery from "./useQuery";
|
||||||
import useToasts from "./useToasts";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a toast message based on a notice in the query string. This is usually
|
* Display a toast message based on a notice in the query string. This is usually
|
||||||
@@ -12,13 +12,12 @@ import useToasts from "./useToasts";
|
|||||||
export default function useQueryNotices() {
|
export default function useQueryNotices() {
|
||||||
const query = useQuery();
|
const query = useQuery();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const notice = query.get("notice") as QueryNotices;
|
const notice = query.get("notice") as QueryNotices;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
switch (notice) {
|
switch (notice) {
|
||||||
case QueryNotices.UnsubscribeDocument: {
|
case QueryNotices.UnsubscribeDocument: {
|
||||||
showToast(
|
toast.success(
|
||||||
t("Unsubscribed from document", {
|
t("Unsubscribed from document", {
|
||||||
type: "success",
|
type: "success",
|
||||||
})
|
})
|
||||||
@@ -27,5 +26,5 @@ export default function useQueryNotices() {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}, [t, showToast, notice]);
|
}, [t, notice]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import useStores from "./useStores";
|
|
||||||
|
|
||||||
export default function useToasts() {
|
|
||||||
const { toasts } = useStores();
|
|
||||||
return {
|
|
||||||
showToast: toasts.showToast,
|
|
||||||
hideToast: toasts.hideToast,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { useMenuState, MenuButton, MenuButtonHTMLProps } from "reakit/Menu";
|
import { useMenuState, MenuButton, MenuButtonHTMLProps } from "reakit/Menu";
|
||||||
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { getEventFiles } from "@shared/utils/files";
|
import { getEventFiles } from "@shared/utils/files";
|
||||||
import Collection from "~/models/Collection";
|
import Collection from "~/models/Collection";
|
||||||
import ContextMenu, { Placement } from "~/components/ContextMenu";
|
import ContextMenu, { Placement } from "~/components/ContextMenu";
|
||||||
@@ -29,7 +30,6 @@ import useActionContext from "~/hooks/useActionContext";
|
|||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { MenuItem } from "~/types";
|
import { MenuItem } from "~/types";
|
||||||
import { newDocumentPath } from "~/utils/routeHelpers";
|
import { newDocumentPath } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
@@ -56,7 +56,6 @@ function CollectionMenu({
|
|||||||
});
|
});
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const { documents, dialogs } = useStores();
|
const { documents, dialogs } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const file = React.useRef<HTMLInputElement>(null);
|
const file = React.useRef<HTMLInputElement>(null);
|
||||||
@@ -116,13 +115,11 @@ function CollectionMenu({
|
|||||||
});
|
});
|
||||||
history.push(document.url);
|
history.push(document.url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[history, showToast, collection.id, documents]
|
[history, collection.id, documents]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeSort = React.useCallback(
|
const handleChangeSort = React.useCallback(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useMenuState } from "reakit/Menu";
|
import { useMenuState } from "reakit/Menu";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Comment from "~/models/Comment";
|
import Comment from "~/models/Comment";
|
||||||
import CommentDeleteDialog from "~/components/CommentDeleteDialog";
|
import CommentDeleteDialog from "~/components/CommentDeleteDialog";
|
||||||
import ContextMenu from "~/components/ContextMenu";
|
import ContextMenu from "~/components/ContextMenu";
|
||||||
@@ -12,7 +13,6 @@ import Separator from "~/components/ContextMenu/Separator";
|
|||||||
import EventBoundary from "~/components/EventBoundary";
|
import EventBoundary from "~/components/EventBoundary";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { commentPath, urlify } from "~/utils/routeHelpers";
|
import { commentPath, urlify } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -30,7 +30,6 @@ function CommentMenu({ comment, onEdit, onDelete, className }: Props) {
|
|||||||
const menu = useMenuState({
|
const menu = useMenuState({
|
||||||
modal: true,
|
modal: true,
|
||||||
});
|
});
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { documents, dialogs } = useStores();
|
const { documents, dialogs } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const can = usePolicy(comment.id);
|
const can = usePolicy(comment.id);
|
||||||
@@ -47,9 +46,9 @@ function CommentMenu({ comment, onEdit, onDelete, className }: Props) {
|
|||||||
const handleCopyLink = React.useCallback(() => {
|
const handleCopyLink = React.useCallback(() => {
|
||||||
if (document) {
|
if (document) {
|
||||||
copy(urlify(commentPath(document, comment)));
|
copy(urlify(commentPath(document, comment)));
|
||||||
showToast(t("Link copied"));
|
toast.message(t("Link copied"));
|
||||||
}
|
}
|
||||||
}, [t, document, comment, showToast]);
|
}, [t, document, comment]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { useMenuState, MenuButton, MenuButtonHTMLProps } from "reakit/Menu";
|
import { useMenuState, MenuButton, MenuButtonHTMLProps } from "reakit/Menu";
|
||||||
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import breakpoint from "styled-components-breakpoint";
|
import breakpoint from "styled-components-breakpoint";
|
||||||
import { s, ellipsis } from "@shared/styles";
|
import { s, ellipsis } from "@shared/styles";
|
||||||
@@ -47,7 +48,6 @@ import useMobile from "~/hooks/useMobile";
|
|||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useRequest from "~/hooks/useRequest";
|
import useRequest from "~/hooks/useRequest";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { MenuItem } from "~/types";
|
import { MenuItem } from "~/types";
|
||||||
import { documentEditPath } from "~/utils/routeHelpers";
|
import { documentEditPath } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
@@ -79,7 +79,6 @@ function DocumentMenu({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const { policies, collections, documents, subscriptions } = useStores();
|
const { policies, collections, documents, subscriptions } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const menu = useMenuState({
|
const menu = useMenuState({
|
||||||
modal,
|
modal,
|
||||||
unstable_preventOverflow: true,
|
unstable_preventOverflow: true,
|
||||||
@@ -120,11 +119,9 @@ function DocumentMenu({
|
|||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
await document.restore(options);
|
await document.restore(options);
|
||||||
showToast(t("Document restored"), {
|
toast.success(t("Document restored"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[showToast, t, document]
|
[t, document]
|
||||||
);
|
);
|
||||||
|
|
||||||
const collection = document.collectionId
|
const collection = document.collectionId
|
||||||
@@ -187,13 +184,11 @@ function DocumentMenu({
|
|||||||
);
|
);
|
||||||
history.push(importedDocument.url);
|
history.push(importedDocument.url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[history, showToast, collection, documents, document.id]
|
[history, collection, documents, document.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { useMenuState } from "reakit/Menu";
|
import { useMenuState } from "reakit/Menu";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Share from "~/models/Share";
|
import Share from "~/models/Share";
|
||||||
import ContextMenu from "~/components/ContextMenu";
|
import ContextMenu from "~/components/ContextMenu";
|
||||||
import MenuItem from "~/components/ContextMenu/MenuItem";
|
import MenuItem from "~/components/ContextMenu/MenuItem";
|
||||||
@@ -11,7 +12,6 @@ import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
|
|||||||
import CopyToClipboard from "~/components/CopyToClipboard";
|
import CopyToClipboard from "~/components/CopyToClipboard";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
share: Share;
|
share: Share;
|
||||||
@@ -22,7 +22,6 @@ function ShareMenu({ share }: Props) {
|
|||||||
modal: true,
|
modal: true,
|
||||||
});
|
});
|
||||||
const { shares } = useStores();
|
const { shares } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const can = usePolicy(share.id);
|
const can = usePolicy(share.id);
|
||||||
@@ -41,23 +40,17 @@ function ShareMenu({ share }: Props) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await shares.revoke(share);
|
await shares.revoke(share);
|
||||||
showToast(t("Share link revoked"), {
|
toast.message(t("Share link revoked"));
|
||||||
type: "info",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[t, shares, share, showToast]
|
[t, shares, share]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCopy = React.useCallback(() => {
|
const handleCopy = React.useCallback(() => {
|
||||||
showToast(t("Share link copied"), {
|
toast.success(t("Share link copied"));
|
||||||
type: "info",
|
}, [t]);
|
||||||
});
|
|
||||||
}, [t, showToast]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useMenuState } from "reakit/Menu";
|
import { useMenuState } from "reakit/Menu";
|
||||||
|
import { toast } from "sonner";
|
||||||
import User from "~/models/User";
|
import User from "~/models/User";
|
||||||
import ContextMenu from "~/components/ContextMenu";
|
import ContextMenu from "~/components/ContextMenu";
|
||||||
import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
|
import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
|
||||||
@@ -18,7 +19,6 @@ import { deleteUserActionFactory } from "~/actions/definitions/users";
|
|||||||
import useActionContext from "~/hooks/useActionContext";
|
import useActionContext from "~/hooks/useActionContext";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
user: User;
|
user: User;
|
||||||
@@ -31,7 +31,6 @@ function UserMenu({ user }: Props) {
|
|||||||
modal: true,
|
modal: true,
|
||||||
});
|
});
|
||||||
const can = usePolicy(user.id);
|
const can = usePolicy(user.id);
|
||||||
const { showToast } = useToasts();
|
|
||||||
const context = useActionContext({
|
const context = useActionContext({
|
||||||
isContextMenu: true,
|
isContextMenu: true,
|
||||||
});
|
});
|
||||||
@@ -129,17 +128,14 @@ function UserMenu({ user }: Props) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await users.resendInvite(user);
|
await users.resendInvite(user);
|
||||||
showToast(t(`Invite was resent to ${user.name}`), { type: "success" });
|
toast.success(t(`Invite was resent to ${user.name}`));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(
|
toast.error(
|
||||||
err.message ?? t(`An error occurred while sending the invite`),
|
err.message ?? t(`An error occurred while sending the invite`)
|
||||||
{
|
|
||||||
type: "error",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[users, user, t, showToast]
|
[users, user, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleActivate = React.useCallback(
|
const handleActivate = React.useCallback(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import User from "./User";
|
import User from "./User";
|
||||||
import Model from "./base/Model";
|
import Model from "./base/Model";
|
||||||
|
import Relation from "./decorators/Relation";
|
||||||
|
|
||||||
class GroupMembership extends Model {
|
class GroupMembership extends Model {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -8,6 +9,7 @@ class GroupMembership extends Model {
|
|||||||
|
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
|
||||||
|
@Relation(() => User, { onDelete: "cascade" })
|
||||||
user: User;
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Input from "~/components/Input";
|
import Input from "~/components/Input";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSubmit: () => void;
|
onSubmit: () => void;
|
||||||
@@ -15,7 +15,6 @@ function APITokenNew({ onSubmit }: Props) {
|
|||||||
const [name, setName] = React.useState("");
|
const [name, setName] = React.useState("");
|
||||||
const [isSaving, setIsSaving] = React.useState(false);
|
const [isSaving, setIsSaving] = React.useState(false);
|
||||||
const { apiKeys } = useStores();
|
const { apiKeys } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = React.useCallback(
|
||||||
@@ -27,21 +26,15 @@ function APITokenNew({ onSubmit }: Props) {
|
|||||||
await apiKeys.create({
|
await apiKeys.create({
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
showToast(
|
toast.success(t("API token created"));
|
||||||
t("API token created", {
|
|
||||||
type: "success",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
onSubmit();
|
onSubmit();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[t, showToast, name, onSubmit, apiKeys]
|
[t, name, onSubmit, apiKeys]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleNameChange = React.useCallback((event) => {
|
const handleNameChange = React.useCallback((event) => {
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import Dropzone from "react-dropzone";
|
import Dropzone from "react-dropzone";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
import LoadingIndicator from "~/components/LoadingIndicator";
|
import LoadingIndicator from "~/components/LoadingIndicator";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useImportDocument from "~/hooks/useImportDocument";
|
import useImportDocument from "~/hooks/useImportDocument";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
@@ -22,17 +22,13 @@ const DropToImport: React.FC<Props> = ({
|
|||||||
collectionId,
|
collectionId,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { handleFiles, isImporting } = useImportDocument(collectionId);
|
const { handleFiles, isImporting } = useImportDocument(collectionId);
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleRejection = React.useCallback(() => {
|
const handleRejection = React.useCallback(() => {
|
||||||
showToast(
|
toast.error(
|
||||||
t("Document not supported – try Markdown, Plain text, HTML, or Word"),
|
t("Document not supported – try Markdown, Plain text, HTML, or Word")
|
||||||
{
|
|
||||||
type: "error",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}, [t, showToast]);
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropzone
|
<Dropzone
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { CollectionValidation } from "@shared/validations";
|
import { CollectionValidation } from "@shared/validations";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
@@ -11,7 +12,6 @@ import Input from "~/components/Input";
|
|||||||
import InputSelect from "~/components/InputSelect";
|
import InputSelect from "~/components/InputSelect";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
@@ -30,7 +30,6 @@ const CollectionEdit = ({ collectionId, onSubmit }: Props) => {
|
|||||||
direction: "asc" | "desc";
|
direction: "asc" | "desc";
|
||||||
}>(collection.sort);
|
}>(collection.sort);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = React.useCallback(
|
||||||
@@ -46,18 +45,14 @@ const CollectionEdit = ({ collectionId, onSubmit }: Props) => {
|
|||||||
sort,
|
sort,
|
||||||
});
|
});
|
||||||
onSubmit();
|
onSubmit();
|
||||||
showToast(t("The collection was updated"), {
|
toast.success(t("The collection was updated"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[collection, color, icon, name, onSubmit, showToast, sort, t]
|
[collection, color, icon, name, onSubmit, sort, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSortChange = (value: string) => {
|
const handleSortChange = (value: string) => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { observable } from "mobx";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { withTranslation, Trans, WithTranslation } from "react-i18next";
|
import { withTranslation, Trans, WithTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { randomElement } from "@shared/random";
|
import { randomElement } from "@shared/random";
|
||||||
import { CollectionPermission } from "@shared/types";
|
import { CollectionPermission } from "@shared/types";
|
||||||
import { colorPalette } from "@shared/utils/collections";
|
import { colorPalette } from "@shared/utils/collections";
|
||||||
@@ -66,9 +67,7 @@ class CollectionNew extends React.Component<Props> {
|
|||||||
this.props.onSubmit();
|
this.props.onSubmit();
|
||||||
history.push(collection.url);
|
history.push(collection.url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.props.toasts.showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
this.isSaving = false;
|
this.isSaving = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import debounce from "lodash/debounce";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import Collection from "~/models/Collection";
|
import Collection from "~/models/Collection";
|
||||||
import Group from "~/models/Group";
|
import Group from "~/models/Group";
|
||||||
@@ -29,8 +30,7 @@ function AddGroupsToCollection(props: Props) {
|
|||||||
useBoolean(false);
|
useBoolean(false);
|
||||||
const [query, setQuery] = React.useState("");
|
const [query, setQuery] = React.useState("");
|
||||||
|
|
||||||
const { auth, collectionGroupMemberships, groups, policies, toasts } =
|
const { auth, collectionGroupMemberships, groups, policies } = useStores();
|
||||||
useStores();
|
|
||||||
const { fetchPage: fetchGroups } = groups;
|
const { fetchPage: fetchGroups } = groups;
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -55,18 +55,13 @@ function AddGroupsToCollection(props: Props) {
|
|||||||
collectionId: collection.id,
|
collectionId: collection.id,
|
||||||
groupId: group.id,
|
groupId: group.id,
|
||||||
});
|
});
|
||||||
toasts.showToast(
|
toast.success(
|
||||||
t("{{ groupName }} was added to the collection", {
|
t("{{ groupName }} was added to the collection", {
|
||||||
groupName: group.name,
|
groupName: group.name,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
type: "success",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toasts.showToast(t("Could not add user"), {
|
toast.error(t("Could not add user"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Collection from "~/models/Collection";
|
import Collection from "~/models/Collection";
|
||||||
import User from "~/models/User";
|
import User from "~/models/User";
|
||||||
import Invite from "~/scenes/Invite";
|
import Invite from "~/scenes/Invite";
|
||||||
|
import Avatar from "~/components/Avatar";
|
||||||
|
import { AvatarSize } from "~/components/Avatar/Avatar";
|
||||||
import ButtonLink from "~/components/ButtonLink";
|
import ButtonLink from "~/components/ButtonLink";
|
||||||
import Empty from "~/components/Empty";
|
import Empty from "~/components/Empty";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
@@ -15,7 +18,6 @@ import useBoolean from "~/hooks/useBoolean";
|
|||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useThrottledCallback from "~/hooks/useThrottledCallback";
|
import useThrottledCallback from "~/hooks/useThrottledCallback";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import MemberListItem from "./components/MemberListItem";
|
import MemberListItem from "./components/MemberListItem";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -24,7 +26,6 @@ type Props = {
|
|||||||
|
|
||||||
function AddPeopleToCollection({ collection }: Props) {
|
function AddPeopleToCollection({ collection }: Props) {
|
||||||
const { memberships, users } = useStores();
|
const { memberships, users } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [inviteModalOpen, setInviteModalOpen, setInviteModalClosed] =
|
const [inviteModalOpen, setInviteModalOpen, setInviteModalClosed] =
|
||||||
@@ -50,18 +51,16 @@ function AddPeopleToCollection({ collection }: Props) {
|
|||||||
collectionId: collection.id,
|
collectionId: collection.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
showToast(
|
toast.success(
|
||||||
t("{{ userName }} was added to the collection", {
|
t("{{ userName }} was added to the collection", {
|
||||||
userName: user.name,
|
userName: user.name,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
type: "success",
|
icon: <Avatar model={user} size={AvatarSize.Toast} />,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Could not add user"), {
|
toast.error(t("Could not add user"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||||||
import { PlusIcon } from "outline-icons";
|
import { PlusIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { CollectionPermission } from "@shared/types";
|
import { CollectionPermission } from "@shared/types";
|
||||||
import Group from "~/models/Group";
|
import Group from "~/models/Group";
|
||||||
@@ -19,7 +20,6 @@ import Text from "~/components/Text";
|
|||||||
import useBoolean from "~/hooks/useBoolean";
|
import useBoolean from "~/hooks/useBoolean";
|
||||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import AddGroupsToCollection from "./AddGroupsToCollection";
|
import AddGroupsToCollection from "./AddGroupsToCollection";
|
||||||
import AddPeopleToCollection from "./AddPeopleToCollection";
|
import AddPeopleToCollection from "./AddPeopleToCollection";
|
||||||
import CollectionGroupMemberListItem from "./components/CollectionGroupMemberListItem";
|
import CollectionGroupMemberListItem from "./components/CollectionGroupMemberListItem";
|
||||||
@@ -40,7 +40,6 @@ function CollectionPermissions({ collectionId }: Props) {
|
|||||||
groups,
|
groups,
|
||||||
auth,
|
auth,
|
||||||
} = useStores();
|
} = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const collection = collections.get(collectionId);
|
const collection = collections.get(collectionId);
|
||||||
invariant(collection, "Collection not found");
|
invariant(collection, "Collection not found");
|
||||||
|
|
||||||
@@ -60,21 +59,16 @@ function CollectionPermissions({ collectionId }: Props) {
|
|||||||
collectionId: collection.id,
|
collectionId: collection.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
showToast(
|
toast.success(
|
||||||
t(`{{ userName }} was removed from the collection`, {
|
t(`{{ userName }} was removed from the collection`, {
|
||||||
userName: user.name,
|
userName: user.name,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
type: "success",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Could not remove user"), {
|
toast.error(t("Could not remove user"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[memberships, showToast, collection, t]
|
[memberships, collection, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleUpdateUser = React.useCallback(
|
const handleUpdateUser = React.useCallback(
|
||||||
@@ -85,21 +79,16 @@ function CollectionPermissions({ collectionId }: Props) {
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
permission,
|
permission,
|
||||||
});
|
});
|
||||||
showToast(
|
toast.success(
|
||||||
t(`{{ userName }} permissions were updated`, {
|
t(`{{ userName }} permissions were updated`, {
|
||||||
userName: user.name,
|
userName: user.name,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
type: "success",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Could not update user"), {
|
toast.error(t("Could not update user"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[memberships, showToast, collection, t]
|
[memberships, collection, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRemoveGroup = React.useCallback(
|
const handleRemoveGroup = React.useCallback(
|
||||||
@@ -109,21 +98,16 @@ function CollectionPermissions({ collectionId }: Props) {
|
|||||||
collectionId: collection.id,
|
collectionId: collection.id,
|
||||||
groupId: group.id,
|
groupId: group.id,
|
||||||
});
|
});
|
||||||
showToast(
|
toast.success(
|
||||||
t(`The {{ groupName }} group was removed from the collection`, {
|
t(`The {{ groupName }} group was removed from the collection`, {
|
||||||
groupName: group.name,
|
groupName: group.name,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
type: "success",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Could not remove group"), {
|
toast.error(t("Could not remove group"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[collectionGroupMemberships, showToast, collection, t]
|
[collectionGroupMemberships, collection, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleUpdateGroup = React.useCallback(
|
const handleUpdateGroup = React.useCallback(
|
||||||
@@ -134,21 +118,16 @@ function CollectionPermissions({ collectionId }: Props) {
|
|||||||
groupId: group.id,
|
groupId: group.id,
|
||||||
permission,
|
permission,
|
||||||
});
|
});
|
||||||
showToast(
|
toast.success(
|
||||||
t(`{{ groupName }} permissions were updated`, {
|
t(`{{ groupName }} permissions were updated`, {
|
||||||
groupName: group.name,
|
groupName: group.name,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
type: "success",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Could not update user"), {
|
toast.error(t("Could not update user"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[collectionGroupMemberships, showToast, collection, t]
|
[collectionGroupMemberships, collection, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangePermission = React.useCallback(
|
const handleChangePermission = React.useCallback(
|
||||||
@@ -157,16 +136,12 @@ function CollectionPermissions({ collectionId }: Props) {
|
|||||||
await collection.save({
|
await collection.save({
|
||||||
permission,
|
permission,
|
||||||
});
|
});
|
||||||
showToast(t("Default access permissions were updated"), {
|
toast.success(t("Default access permissions were updated"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Could not update permissions"), {
|
toast.error(t("Could not update permissions"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[collection, showToast, t]
|
[collection, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchOptions = React.useMemo(
|
const fetchOptions = React.useMemo(
|
||||||
@@ -182,16 +157,12 @@ function CollectionPermissions({ collectionId }: Props) {
|
|||||||
await collection.save({
|
await collection.save({
|
||||||
sharing: ev.target.checked,
|
sharing: ev.target.checked,
|
||||||
});
|
});
|
||||||
showToast(t("Public document sharing permissions were updated"), {
|
toast.success(t("Public document sharing permissions were updated"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Could not update public document sharing"), {
|
toast.error(t("Could not update public document sharing"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[collection, showToast, t]
|
[collection, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const collectionName = collection.name;
|
const collectionName = collection.name;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { action } from "mobx";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { CommentValidation } from "@shared/validations";
|
import { CommentValidation } from "@shared/validations";
|
||||||
import Comment from "~/models/Comment";
|
import Comment from "~/models/Comment";
|
||||||
@@ -15,7 +16,6 @@ import useCurrentUser from "~/hooks/useCurrentUser";
|
|||||||
import useOnClickOutside from "~/hooks/useOnClickOutside";
|
import useOnClickOutside from "~/hooks/useOnClickOutside";
|
||||||
import usePersistedState from "~/hooks/usePersistedState";
|
import usePersistedState from "~/hooks/usePersistedState";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import CommentEditor from "./CommentEditor";
|
import CommentEditor from "./CommentEditor";
|
||||||
import { Bubble } from "./CommentThreadItem";
|
import { Bubble } from "./CommentThreadItem";
|
||||||
|
|
||||||
@@ -65,7 +65,6 @@ function CommentForm({
|
|||||||
const [forceRender, setForceRender] = React.useState(0);
|
const [forceRender, setForceRender] = React.useState(0);
|
||||||
const [inputFocused, setInputFocused] = React.useState(autoFocus);
|
const [inputFocused, setInputFocused] = React.useState(autoFocus);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { comments } = useStores();
|
const { comments } = useStores();
|
||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
|
|
||||||
@@ -106,7 +105,7 @@ function CommentForm({
|
|||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
comment.isNew = true;
|
comment.isNew = true;
|
||||||
showToast(t("Error creating comment"), { type: "error" });
|
toast.error(t("Error creating comment"));
|
||||||
});
|
});
|
||||||
|
|
||||||
// optimistically update the comment model
|
// optimistically update the comment model
|
||||||
@@ -139,7 +138,7 @@ function CommentForm({
|
|||||||
comment.save().catch(() => {
|
comment.save().catch(() => {
|
||||||
comments.remove(comment.id);
|
comments.remove(comment.id);
|
||||||
comment.isNew = true;
|
comment.isNew = true;
|
||||||
showToast(t("Error creating comment"), { type: "error" });
|
toast.error(t("Error creating comment"));
|
||||||
});
|
});
|
||||||
|
|
||||||
// optimistically update the comment model
|
// optimistically update the comment model
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { observer } from "mobx-react";
|
|||||||
import { darken } from "polished";
|
import { darken } from "polished";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
import breakpoint from "styled-components-breakpoint";
|
import breakpoint from "styled-components-breakpoint";
|
||||||
import { s } from "@shared/styles";
|
import { s } from "@shared/styles";
|
||||||
@@ -17,7 +18,6 @@ import Flex from "~/components/Flex";
|
|||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import Time from "~/components/Time";
|
import Time from "~/components/Time";
|
||||||
import useBoolean from "~/hooks/useBoolean";
|
import useBoolean from "~/hooks/useBoolean";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import CommentMenu from "~/menus/CommentMenu";
|
import CommentMenu from "~/menus/CommentMenu";
|
||||||
import { hover } from "~/styles";
|
import { hover } from "~/styles";
|
||||||
import CommentEditor from "./CommentEditor";
|
import CommentEditor from "./CommentEditor";
|
||||||
@@ -85,7 +85,6 @@ function CommentThreadItem({
|
|||||||
canReply,
|
canReply,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { editor } = useDocumentContext();
|
const { editor } = useDocumentContext();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [forceRender, setForceRender] = React.useState(0);
|
const [forceRender, setForceRender] = React.useState(0);
|
||||||
const [data, setData] = React.useState(toJS(comment.data));
|
const [data, setData] = React.useState(toJS(comment.data));
|
||||||
@@ -116,7 +115,7 @@ function CommentThreadItem({
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setEditing();
|
setEditing();
|
||||||
showToast(t("Error updating comment"), { type: "error" });
|
toast.error(t("Error updating comment"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
withRouter,
|
withRouter,
|
||||||
Redirect,
|
Redirect,
|
||||||
} from "react-router";
|
} from "react-router";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import breakpoint from "styled-components-breakpoint";
|
import breakpoint from "styled-components-breakpoint";
|
||||||
import { s } from "@shared/styles";
|
import { s } from "@shared/styles";
|
||||||
@@ -176,7 +177,7 @@ class DocumentScene extends React.Component<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onSynced = async () => {
|
onSynced = async () => {
|
||||||
const { toasts, history, location, t } = this.props;
|
const { history, location, t } = this.props;
|
||||||
const restore = location.state?.restore;
|
const restore = location.state?.restore;
|
||||||
const revisionId = location.state?.revisionId;
|
const revisionId = location.state?.revisionId;
|
||||||
const editorRef = this.editor.current;
|
const editorRef = this.editor.current;
|
||||||
@@ -191,7 +192,7 @@ class DocumentScene extends React.Component<Props> {
|
|||||||
|
|
||||||
if (response) {
|
if (response) {
|
||||||
await this.replaceDocument(response.data);
|
await this.replaceDocument(response.data);
|
||||||
toasts.showToast(t("Document restored"));
|
toast.success(t("Document restored"));
|
||||||
history.replace(this.props.document.url, history.location.state);
|
history.replace(this.props.document.url, history.location.state);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -316,9 +317,7 @@ class DocumentScene extends React.Component<Props> {
|
|||||||
this.props.ui.setActiveDocument(savedDocument);
|
this.props.ui.setActiveDocument(savedDocument);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.props.toasts.showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
this.isSaving = false;
|
this.isSaving = false;
|
||||||
this.isPublishing = false;
|
this.isPublishing = false;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import throttle from "lodash/throttle";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { IndexeddbPersistence } from "y-indexeddb";
|
import { IndexeddbPersistence } from "y-indexeddb";
|
||||||
import * as Y from "yjs";
|
import * as Y from "yjs";
|
||||||
import MultiplayerExtension from "@shared/editor/extensions/Multiplayer";
|
import MultiplayerExtension from "@shared/editor/extensions/Multiplayer";
|
||||||
@@ -14,7 +15,6 @@ import useIdle from "~/hooks/useIdle";
|
|||||||
import useIsMounted from "~/hooks/useIsMounted";
|
import useIsMounted from "~/hooks/useIsMounted";
|
||||||
import usePageVisibility from "~/hooks/usePageVisibility";
|
import usePageVisibility from "~/hooks/usePageVisibility";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { AwarenessChangeEvent } from "~/types";
|
import { AwarenessChangeEvent } from "~/types";
|
||||||
import Logger from "~/utils/Logger";
|
import Logger from "~/utils/Logger";
|
||||||
import { homePath } from "~/utils/routeHelpers";
|
import { homePath } from "~/utils/routeHelpers";
|
||||||
@@ -51,7 +51,6 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) {
|
|||||||
const [isLocalSynced, setLocalSynced] = React.useState(false);
|
const [isLocalSynced, setLocalSynced] = React.useState(false);
|
||||||
const [isRemoteSynced, setRemoteSynced] = React.useState(false);
|
const [isRemoteSynced, setRemoteSynced] = React.useState(false);
|
||||||
const [ydoc] = React.useState(() => new Y.Doc());
|
const [ydoc] = React.useState(() => new Y.Doc());
|
||||||
const { showToast } = useToasts();
|
|
||||||
const token = auth.collaborationToken;
|
const token = auth.collaborationToken;
|
||||||
const isIdle = useIdle();
|
const isIdle = useIdle();
|
||||||
const isVisible = usePageVisibility();
|
const isVisible = usePageVisibility();
|
||||||
@@ -180,7 +179,6 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) {
|
|||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
history,
|
history,
|
||||||
showToast,
|
|
||||||
t,
|
t,
|
||||||
documentId,
|
documentId,
|
||||||
ui,
|
ui,
|
||||||
@@ -251,21 +249,17 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) {
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
function onUnhandledError(event: ErrorEvent) {
|
function onUnhandledError(event: ErrorEvent) {
|
||||||
if (event.message.includes("URIError: URI malformed")) {
|
if (event.message.includes("URIError: URI malformed")) {
|
||||||
showToast(
|
toast.error(
|
||||||
t(
|
t(
|
||||||
"Sorry, the last change could not be persisted – please reload the page"
|
"Sorry, the last change could not be persisted – please reload the page"
|
||||||
),
|
)
|
||||||
{
|
|
||||||
type: "error",
|
|
||||||
timeout: 0,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("error", onUnhandledError);
|
window.addEventListener("error", onUnhandledError);
|
||||||
return () => window.removeEventListener("error", onUnhandledError);
|
return () => window.removeEventListener("error", onUnhandledError);
|
||||||
}, [showToast, t]);
|
}, [t]);
|
||||||
|
|
||||||
if (!remoteProvider) {
|
if (!remoteProvider) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ExpandedIcon, GlobeIcon, PadlockIcon } from "outline-icons";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { s } from "@shared/styles";
|
import { s } from "@shared/styles";
|
||||||
import { dateLocale, dateToRelative } from "@shared/utils/date";
|
import { dateLocale, dateToRelative } from "@shared/utils/date";
|
||||||
@@ -23,7 +24,6 @@ import useCurrentTeam from "~/hooks/useCurrentTeam";
|
|||||||
import useKeyDown from "~/hooks/useKeyDown";
|
import useKeyDown from "~/hooks/useKeyDown";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import useUserLocale from "~/hooks/useUserLocale";
|
import useUserLocale from "~/hooks/useUserLocale";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -44,7 +44,6 @@ function SharePopover({
|
|||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { shares, collections } = useStores();
|
const { shares, collections } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const [expandedOptions, setExpandedOptions] = React.useState(false);
|
const [expandedOptions, setExpandedOptions] = React.useState(false);
|
||||||
const [isEditMode, setIsEditMode] = React.useState(false);
|
const [isEditMode, setIsEditMode] = React.useState(false);
|
||||||
const [slugValidationError, setSlugValidationError] = React.useState("");
|
const [slugValidationError, setSlugValidationError] = React.useState("");
|
||||||
@@ -100,12 +99,10 @@ function SharePopover({
|
|||||||
published: event.currentTarget.checked,
|
published: event.currentTarget.checked,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[document.id, shares, showToast]
|
[document.id, shares]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChildDocumentsChange = React.useCallback(
|
const handleChildDocumentsChange = React.useCallback(
|
||||||
@@ -118,22 +115,18 @@ function SharePopover({
|
|||||||
includeChildDocuments: event.currentTarget.checked,
|
includeChildDocuments: event.currentTarget.checked,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[document.id, shares, showToast]
|
[document.id, shares]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCopied = React.useCallback(() => {
|
const handleCopied = React.useCallback(() => {
|
||||||
timeout.current = setTimeout(() => {
|
timeout.current = setTimeout(() => {
|
||||||
onRequestClose();
|
onRequestClose();
|
||||||
showToast(t("Share link copied"), {
|
toast.message(t("Share link copied"));
|
||||||
type: "info",
|
|
||||||
});
|
|
||||||
}, 250);
|
}, 250);
|
||||||
}, [t, onRequestClose, showToast]);
|
}, [t, onRequestClose]);
|
||||||
|
|
||||||
const handleUrlSlugChange = React.useMemo(
|
const handleUrlSlugChange = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Document from "~/models/Document";
|
import Document from "~/models/Document";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { collectionPath, documentPath } from "~/utils/routeHelpers";
|
import { collectionPath, documentPath } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -21,7 +21,6 @@ function DocumentDelete({ document, onSubmit }: Props) {
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [isDeleting, setDeleting] = React.useState(false);
|
const [isDeleting, setDeleting] = React.useState(false);
|
||||||
const [isArchiving, setArchiving] = React.useState(false);
|
const [isArchiving, setArchiving] = React.useState(false);
|
||||||
const { showToast } = useToasts();
|
|
||||||
const canArchive = !document.isDraft && !document.isArchived;
|
const canArchive = !document.isDraft && !document.isArchived;
|
||||||
const collection = document.collectionId
|
const collection = document.collectionId
|
||||||
? collections.get(document.collectionId)
|
? collections.get(document.collectionId)
|
||||||
@@ -57,14 +56,12 @@ function DocumentDelete({ document, onSubmit }: Props) {
|
|||||||
|
|
||||||
onSubmit();
|
onSubmit();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setDeleting(false);
|
setDeleting(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[showToast, onSubmit, ui, document, documents, history, collection]
|
[onSubmit, ui, document, documents, history, collection]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleArchive = React.useCallback(
|
const handleArchive = React.useCallback(
|
||||||
@@ -76,14 +73,12 @@ function DocumentDelete({ document, onSubmit }: Props) {
|
|||||||
await document.archive();
|
await document.archive();
|
||||||
onSubmit();
|
onSubmit();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setArchiving(false);
|
setArchiving(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[showToast, onSubmit, document]
|
[onSubmit, document]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import flatten from "lodash/flatten";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ellipsis } from "@shared/styles";
|
import { ellipsis } from "@shared/styles";
|
||||||
import { NavigationNode } from "@shared/types";
|
import { NavigationNode } from "@shared/types";
|
||||||
@@ -12,7 +13,6 @@ import Flex from "~/components/Flex";
|
|||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useCollectionTrees from "~/hooks/useCollectionTrees";
|
import useCollectionTrees from "~/hooks/useCollectionTrees";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { flattenTree } from "~/utils/tree";
|
import { flattenTree } from "~/utils/tree";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -21,7 +21,6 @@ type Props = {
|
|||||||
|
|
||||||
function DocumentMove({ document }: Props) {
|
function DocumentMove({ document }: Props) {
|
||||||
const { dialogs } = useStores();
|
const { dialogs } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const collectionTrees = useCollectionTrees();
|
const collectionTrees = useCollectionTrees();
|
||||||
const [selectedPath, selectPath] = React.useState<NavigationNode | null>(
|
const [selectedPath, selectPath] = React.useState<NavigationNode | null>(
|
||||||
@@ -51,9 +50,7 @@ function DocumentMove({ document }: Props) {
|
|||||||
|
|
||||||
const move = async () => {
|
const move = async () => {
|
||||||
if (!selectedPath) {
|
if (!selectedPath) {
|
||||||
showToast(t("Select a location to move"), {
|
toast.message(t("Select a location to move"));
|
||||||
type: "info",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,15 +65,11 @@ function DocumentMove({ document }: Props) {
|
|||||||
await document.move(collectionId);
|
await document.move(collectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
showToast(t("Document moved"), {
|
toast.success(t("Document moved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
|
|
||||||
dialogs.closeAllModals();
|
dialogs.closeAllModals();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Couldn’t move the document, try again?"), {
|
toast.error(t("Couldn’t move the document, try again?"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import * as React from "react";
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory, useLocation, useRouteMatch } from "react-router-dom";
|
import { useHistory, useLocation, useRouteMatch } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import CenteredContent from "~/components/CenteredContent";
|
import CenteredContent from "~/components/CenteredContent";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import PlaceholderDocument from "~/components/PlaceholderDocument";
|
import PlaceholderDocument from "~/components/PlaceholderDocument";
|
||||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
import useQuery from "~/hooks/useQuery";
|
import useQuery from "~/hooks/useQuery";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { documentEditPath, documentPath } from "~/utils/routeHelpers";
|
import { documentEditPath, documentPath } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -25,7 +25,6 @@ function DocumentNew({ template }: Props) {
|
|||||||
const match = useRouteMatch<{ id?: string }>();
|
const match = useRouteMatch<{ id?: string }>();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { documents, collections } = useStores();
|
const { documents, collections } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const id = match.params.id || query.get("collectionId");
|
const id = match.params.id || query.get("collectionId");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -56,9 +55,7 @@ function DocumentNew({ template }: Props) {
|
|||||||
location.state
|
location.state
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Couldn’t create the document, try again?"), {
|
toast.error(t("Couldn’t create the document, try again?"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
history.goBack();
|
history.goBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Document from "~/models/Document";
|
import Document from "~/models/Document";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
document: Document;
|
document: Document;
|
||||||
@@ -18,7 +18,6 @@ function DocumentPermanentDelete({ document, onSubmit }: Props) {
|
|||||||
const [isDeleting, setIsDeleting] = React.useState(false);
|
const [isDeleting, setIsDeleting] = React.useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { documents } = useStores();
|
const { documents } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = React.useCallback(
|
||||||
@@ -30,20 +29,16 @@ function DocumentPermanentDelete({ document, onSubmit }: Props) {
|
|||||||
await documents.delete(document, {
|
await documents.delete(document, {
|
||||||
permanent: true,
|
permanent: true,
|
||||||
});
|
});
|
||||||
showToast(t("Document permanently deleted"), {
|
toast.success(t("Document permanently deleted"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
onSubmit();
|
onSubmit();
|
||||||
history.push("/trash");
|
history.push("/trash");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[document, onSubmit, showToast, t, history, documents]
|
[document, onSubmit, t, history, documents]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import flatten from "lodash/flatten";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ellipsis } from "@shared/styles";
|
import { ellipsis } from "@shared/styles";
|
||||||
import { NavigationNode } from "@shared/types";
|
import { NavigationNode } from "@shared/types";
|
||||||
@@ -12,7 +13,6 @@ import Flex from "~/components/Flex";
|
|||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useCollectionTrees from "~/hooks/useCollectionTrees";
|
import useCollectionTrees from "~/hooks/useCollectionTrees";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { flattenTree } from "~/utils/tree";
|
import { flattenTree } from "~/utils/tree";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -22,7 +22,6 @@ type Props = {
|
|||||||
|
|
||||||
function DocumentPublish({ document }: Props) {
|
function DocumentPublish({ document }: Props) {
|
||||||
const { dialogs } = useStores();
|
const { dialogs } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const collectionTrees = useCollectionTrees();
|
const collectionTrees = useCollectionTrees();
|
||||||
const [selectedPath, selectPath] = React.useState<NavigationNode | null>(
|
const [selectedPath, selectPath] = React.useState<NavigationNode | null>(
|
||||||
@@ -35,9 +34,7 @@ function DocumentPublish({ document }: Props) {
|
|||||||
|
|
||||||
const publish = async () => {
|
const publish = async () => {
|
||||||
if (!selectedPath) {
|
if (!selectedPath) {
|
||||||
showToast(t("Select a location to publish"), {
|
toast.message(t("Select a location to publish"));
|
||||||
type: "info",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,15 +51,11 @@ function DocumentPublish({ document }: Props) {
|
|||||||
document.collectionId = collectionId;
|
document.collectionId = collectionId;
|
||||||
await document.save(undefined, { publish: true });
|
await document.save(undefined, { publish: true });
|
||||||
|
|
||||||
showToast(t("Document published"), {
|
toast.success(t("Document published"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
|
|
||||||
dialogs.closeAllModals();
|
dialogs.closeAllModals();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Couldn’t publish the document, try again?"), {
|
toast.error(t("Couldn’t publish the document, try again?"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { observer } from "mobx-react";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { CollectionPermission, NavigationNode } from "@shared/types";
|
import { CollectionPermission, NavigationNode } from "@shared/types";
|
||||||
import Collection from "~/models/Collection";
|
import Collection from "~/models/Collection";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
item:
|
item:
|
||||||
@@ -33,7 +33,6 @@ type Props = {
|
|||||||
|
|
||||||
function DocumentReparent({ collection, item, onSubmit, onCancel }: Props) {
|
function DocumentReparent({ collection, item, onSubmit, onCancel }: Props) {
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { documents, collections } = useStores();
|
const { documents, collections } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const prevCollection = collections.get(item.collectionId);
|
const prevCollection = collections.get(item.collectionId);
|
||||||
@@ -50,19 +49,15 @@ function DocumentReparent({ collection, item, onSubmit, onCancel }: Props) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await documents.move(item.id, collection.id);
|
await documents.move(item.id, collection.id);
|
||||||
showToast(t("Document moved"), {
|
toast.message(t("Document moved"));
|
||||||
type: "info",
|
|
||||||
});
|
|
||||||
onSubmit();
|
onSubmit();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[documents, item.id, collection.id, showToast, t, onSubmit]
|
[documents, item.id, collection.id, t, onSubmit]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Group from "~/models/Group";
|
import Group from "~/models/Group";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { settingsPath } from "~/utils/routeHelpers";
|
import { settingsPath } from "~/utils/routeHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -16,7 +16,6 @@ type Props = {
|
|||||||
|
|
||||||
function GroupDelete({ group, onSubmit }: Props) {
|
function GroupDelete({ group, onSubmit }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [isDeleting, setIsDeleting] = React.useState(false);
|
const [isDeleting, setIsDeleting] = React.useState(false);
|
||||||
|
|
||||||
@@ -29,9 +28,7 @@ function GroupDelete({ group, onSubmit }: Props) {
|
|||||||
history.push(settingsPath("groups"));
|
history.push(settingsPath("groups"));
|
||||||
onSubmit();
|
onSubmit();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Group from "~/models/Group";
|
import Group from "~/models/Group";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Input from "~/components/Input";
|
import Input from "~/components/Input";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
group: Group;
|
group: Group;
|
||||||
@@ -14,7 +14,6 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function GroupEdit({ group, onSubmit }: Props) {
|
function GroupEdit({ group, onSubmit }: Props) {
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [name, setName] = React.useState(group.name);
|
const [name, setName] = React.useState(group.name);
|
||||||
const [isSaving, setIsSaving] = React.useState(false);
|
const [isSaving, setIsSaving] = React.useState(false);
|
||||||
@@ -29,14 +28,12 @@ function GroupEdit({ group, onSubmit }: Props) {
|
|||||||
});
|
});
|
||||||
onSubmit();
|
onSubmit();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[group, onSubmit, showToast, name]
|
[group, onSubmit, name]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleNameChange = React.useCallback(
|
const handleNameChange = React.useCallback(
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ import debounce from "lodash/debounce";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Group from "~/models/Group";
|
import Group from "~/models/Group";
|
||||||
import User from "~/models/User";
|
import User from "~/models/User";
|
||||||
import Invite from "~/scenes/Invite";
|
import Invite from "~/scenes/Invite";
|
||||||
|
import Avatar from "~/components/Avatar";
|
||||||
|
import { AvatarSize } from "~/components/Avatar/Avatar";
|
||||||
import ButtonLink from "~/components/ButtonLink";
|
import ButtonLink from "~/components/ButtonLink";
|
||||||
import Empty from "~/components/Empty";
|
import Empty from "~/components/Empty";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
@@ -24,7 +27,7 @@ type Props = {
|
|||||||
function AddPeopleToGroup(props: Props) {
|
function AddPeopleToGroup(props: Props) {
|
||||||
const { group } = props;
|
const { group } = props;
|
||||||
|
|
||||||
const { users, auth, groupMemberships, toasts } = useStores();
|
const { users, auth, groupMemberships } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [query, setQuery] = React.useState("");
|
const [query, setQuery] = React.useState("");
|
||||||
@@ -53,18 +56,16 @@ function AddPeopleToGroup(props: Props) {
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
toasts.showToast(
|
toast.success(
|
||||||
t(`{{userName}} was added to the group`, {
|
t(`{{userName}} was added to the group`, {
|
||||||
userName: user.name,
|
userName: user.name,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
type: "success",
|
icon: <Avatar model={user} size={AvatarSize.Toast} />,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toasts.showToast(t("Could not add user"), {
|
toast.error(t("Could not add user"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||||||
import { PlusIcon } from "outline-icons";
|
import { PlusIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Group from "~/models/Group";
|
import Group from "~/models/Group";
|
||||||
import User from "~/models/User";
|
import User from "~/models/User";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
@@ -13,7 +14,6 @@ import Subheading from "~/components/Subheading";
|
|||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import AddPeopleToGroup from "./AddPeopleToGroup";
|
import AddPeopleToGroup from "./AddPeopleToGroup";
|
||||||
import GroupMemberListItem from "./components/GroupMemberListItem";
|
import GroupMemberListItem from "./components/GroupMemberListItem";
|
||||||
|
|
||||||
@@ -24,7 +24,6 @@ type Props = {
|
|||||||
function GroupMembers({ group }: Props) {
|
function GroupMembers({ group }: Props) {
|
||||||
const [addModalOpen, setAddModalOpen] = React.useState(false);
|
const [addModalOpen, setAddModalOpen] = React.useState(false);
|
||||||
const { users, groupMemberships } = useStores();
|
const { users, groupMemberships } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const can = usePolicy(group);
|
const can = usePolicy(group);
|
||||||
|
|
||||||
@@ -38,18 +37,13 @@ function GroupMembers({ group }: Props) {
|
|||||||
groupId: group.id,
|
groupId: group.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
showToast(
|
toast.success(
|
||||||
t(`{{userName}} was removed from the group`, {
|
t(`{{userName}} was removed from the group`, {
|
||||||
userName: user.name,
|
userName: user.name,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
type: "success",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(t("Could not remove user"), {
|
toast.error(t("Could not remove user"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Group from "~/models/Group";
|
import Group from "~/models/Group";
|
||||||
import GroupMembers from "~/scenes/GroupMembers";
|
import GroupMembers from "~/scenes/GroupMembers";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
@@ -9,7 +10,6 @@ import Input from "~/components/Input";
|
|||||||
import Modal from "~/components/Modal";
|
import Modal from "~/components/Modal";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSubmit: () => void;
|
onSubmit: () => void;
|
||||||
@@ -18,7 +18,6 @@ type Props = {
|
|||||||
function GroupNew({ onSubmit }: Props) {
|
function GroupNew({ onSubmit }: Props) {
|
||||||
const { groups } = useStores();
|
const { groups } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const [name, setName] = React.useState<string | undefined>();
|
const [name, setName] = React.useState<string | undefined>();
|
||||||
const [isSaving, setIsSaving] = React.useState(false);
|
const [isSaving, setIsSaving] = React.useState(false);
|
||||||
const [group, setGroup] = React.useState<Group | undefined>();
|
const [group, setGroup] = React.useState<Group | undefined>();
|
||||||
@@ -38,9 +37,7 @@ function GroupNew({ onSubmit }: Props) {
|
|||||||
await group.save();
|
await group.save();
|
||||||
setGroup(group);
|
setGroup(group);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { LinkIcon, CloseIcon } from "outline-icons";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { s } from "@shared/styles";
|
import { s } from "@shared/styles";
|
||||||
import { UserRole } from "@shared/types";
|
import { UserRole } from "@shared/types";
|
||||||
@@ -19,7 +20,6 @@ import useCurrentTeam from "~/hooks/useCurrentTeam";
|
|||||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSubmit: () => void;
|
onSubmit: () => void;
|
||||||
@@ -52,7 +52,6 @@ function Invite({ onSubmit }: Props) {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const { users } = useStores();
|
const { users } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -69,23 +68,17 @@ function Invite({ onSubmit }: Props) {
|
|||||||
onSubmit();
|
onSubmit();
|
||||||
|
|
||||||
if (data.sent.length > 0) {
|
if (data.sent.length > 0) {
|
||||||
showToast(t("We sent out your invites!"), {
|
toast.success(t("We sent out your invites!"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
showToast(t("Those email addresses are already invited"), {
|
toast.message(t("Those email addresses are already invited"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onSubmit, showToast, invites, t, users]
|
[onSubmit, invites, t, users]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChange = React.useCallback((ev, index) => {
|
const handleChange = React.useCallback((ev, index) => {
|
||||||
@@ -98,13 +91,10 @@ function Invite({ onSubmit }: Props) {
|
|||||||
|
|
||||||
const handleAdd = React.useCallback(() => {
|
const handleAdd = React.useCallback(() => {
|
||||||
if (invites.length >= UserValidation.maxInvitesPerRequest) {
|
if (invites.length >= UserValidation.maxInvitesPerRequest) {
|
||||||
showToast(
|
toast.message(
|
||||||
t("Sorry, you can only send {{MAX_INVITES}} invites at a time", {
|
t("Sorry, you can only send {{MAX_INVITES}} invites at a time", {
|
||||||
MAX_INVITES: UserValidation.maxInvitesPerRequest,
|
MAX_INVITES: UserValidation.maxInvitesPerRequest,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
type: "warning",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +107,7 @@ function Invite({ onSubmit }: Props) {
|
|||||||
});
|
});
|
||||||
return newInvites;
|
return newInvites;
|
||||||
});
|
});
|
||||||
}, [showToast, invites, t]);
|
}, [invites, t]);
|
||||||
|
|
||||||
const handleRemove = React.useCallback(
|
const handleRemove = React.useCallback(
|
||||||
(ev: React.SyntheticEvent, index: number) => {
|
(ev: React.SyntheticEvent, index: number) => {
|
||||||
@@ -133,10 +123,8 @@ function Invite({ onSubmit }: Props) {
|
|||||||
|
|
||||||
const handleCopy = React.useCallback(() => {
|
const handleCopy = React.useCallback(() => {
|
||||||
setLinkCopied(true);
|
setLinkCopied(true);
|
||||||
showToast(t("Share link copied"), {
|
toast.success(t("Share link copied"));
|
||||||
type: "success",
|
}, [t]);
|
||||||
});
|
|
||||||
}, [showToast, t]);
|
|
||||||
|
|
||||||
const handleRoleChange = React.useCallback(
|
const handleRoleChange = React.useCallback(
|
||||||
(role: UserRole, index: number) => {
|
(role: UserRole, index: number) => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { TeamIcon } from "outline-icons";
|
|||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { ThemeProvider, useTheme } from "styled-components";
|
import { ThemeProvider, useTheme } from "styled-components";
|
||||||
import { buildDarkTheme, buildLightTheme } from "@shared/styles/theme";
|
import { buildDarkTheme, buildLightTheme } from "@shared/styles/theme";
|
||||||
import { CustomTheme, TeamPreference } from "@shared/types";
|
import { CustomTheme, TeamPreference } from "@shared/types";
|
||||||
@@ -21,7 +22,6 @@ import Text from "~/components/Text";
|
|||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import isCloudHosted from "~/utils/isCloudHosted";
|
import isCloudHosted from "~/utils/isCloudHosted";
|
||||||
import TeamDelete from "../TeamDelete";
|
import TeamDelete from "../TeamDelete";
|
||||||
import ImageInput from "./components/ImageInput";
|
import ImageInput from "./components/ImageInput";
|
||||||
@@ -29,7 +29,6 @@ import SettingRow from "./components/SettingRow";
|
|||||||
|
|
||||||
function Details() {
|
function Details() {
|
||||||
const { auth, dialogs, ui } = useStores();
|
const { auth, dialogs, ui } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -76,13 +75,9 @@ function Details() {
|
|||||||
customTheme,
|
customTheme,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
showToast(t("Settings saved"), {
|
toast.success(t("Settings saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@@ -93,7 +88,6 @@ function Details() {
|
|||||||
team.preferences,
|
team.preferences,
|
||||||
publicBranding,
|
publicBranding,
|
||||||
customTheme,
|
customTheme,
|
||||||
showToast,
|
|
||||||
t,
|
t,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -116,16 +110,14 @@ function Details() {
|
|||||||
await auth.updateTeam({
|
await auth.updateTeam({
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
});
|
});
|
||||||
showToast(t("Logo updated"), {
|
toast.success(t("Logo updated"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAvatarError = React.useCallback(
|
const handleAvatarError = React.useCallback(
|
||||||
(error: string | null | undefined) => {
|
(error: string | null | undefined) => {
|
||||||
showToast(error || t("Unable to upload new logo"));
|
toast.error(error || t("Unable to upload new logo"));
|
||||||
},
|
},
|
||||||
[showToast, t]
|
[t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const showDeleteWorkspace = () => {
|
const showDeleteWorkspace = () => {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||||||
import { BeakerIcon } from "outline-icons";
|
import { BeakerIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { TeamPreference } from "@shared/types";
|
import { TeamPreference } from "@shared/types";
|
||||||
import Heading from "~/components/Heading";
|
import Heading from "~/components/Heading";
|
||||||
import Scene from "~/components/Scene";
|
import Scene from "~/components/Scene";
|
||||||
@@ -9,14 +10,12 @@ import Switch from "~/components/Switch";
|
|||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import SettingRow from "./components/SettingRow";
|
import SettingRow from "./components/SettingRow";
|
||||||
|
|
||||||
function Features() {
|
function Features() {
|
||||||
const { auth } = useStores();
|
const { auth } = useStores();
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
const handlePreferenceChange =
|
const handlePreferenceChange =
|
||||||
(inverted = false) =>
|
(inverted = false) =>
|
||||||
@@ -27,9 +26,7 @@ function Features() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await auth.updateTeam({ preferences });
|
await auth.updateTeam({ preferences });
|
||||||
showToast(t("Settings saved"), {
|
toast.success(t("Settings saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { IntegrationType, IntegrationService } from "@shared/types";
|
import { IntegrationType, IntegrationService } from "@shared/types";
|
||||||
import Integration from "~/models/Integration";
|
import Integration from "~/models/Integration";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
@@ -12,7 +13,6 @@ import Input from "~/components/Input";
|
|||||||
import Scene from "~/components/Scene";
|
import Scene from "~/components/Scene";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import SettingRow from "./components/SettingRow";
|
import SettingRow from "./components/SettingRow";
|
||||||
|
|
||||||
type FormData = {
|
type FormData = {
|
||||||
@@ -22,7 +22,6 @@ type FormData = {
|
|||||||
function GoogleAnalytics() {
|
function GoogleAnalytics() {
|
||||||
const { integrations } = useStores();
|
const { integrations } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
const integration = find(integrations.orderedData, {
|
const integration = find(integrations.orderedData, {
|
||||||
type: IntegrationType.Analytics,
|
type: IntegrationType.Analytics,
|
||||||
@@ -67,16 +66,12 @@ function GoogleAnalytics() {
|
|||||||
await integration?.delete();
|
await integration?.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
showToast(t("Settings saved"), {
|
toast.success(t("Settings saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[integrations, integration, t, showToast]
|
[integrations, integration, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from "outline-icons";
|
} from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { NotificationEventType } from "@shared/types";
|
import { NotificationEventType } from "@shared/types";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Heading from "~/components/Heading";
|
import Heading from "~/components/Heading";
|
||||||
@@ -23,12 +24,10 @@ import Switch from "~/components/Switch";
|
|||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import env from "~/env";
|
import env from "~/env";
|
||||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import isCloudHosted from "~/utils/isCloudHosted";
|
import isCloudHosted from "~/utils/isCloudHosted";
|
||||||
import SettingRow from "./components/SettingRow";
|
import SettingRow from "./components/SettingRow";
|
||||||
|
|
||||||
function Notifications() {
|
function Notifications() {
|
||||||
const { showToast } = useToasts();
|
|
||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -106,9 +105,7 @@ function Notifications() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const showSuccessMessage = debounce(() => {
|
const showSuccessMessage = debounce(() => {
|
||||||
showToast(t("Notifications saved"), {
|
toast.success(t("Notifications saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
const handleChange = React.useCallback(
|
const handleChange = React.useCallback(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||||||
import { SettingsIcon } from "outline-icons";
|
import { SettingsIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { languageOptions } from "@shared/i18n";
|
import { languageOptions } from "@shared/i18n";
|
||||||
import { TeamPreference, UserPreference } from "@shared/types";
|
import { TeamPreference, UserPreference } from "@shared/types";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
@@ -13,13 +14,11 @@ import Text from "~/components/Text";
|
|||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import UserDelete from "../UserDelete";
|
import UserDelete from "../UserDelete";
|
||||||
import SettingRow from "./components/SettingRow";
|
import SettingRow from "./components/SettingRow";
|
||||||
|
|
||||||
function Preferences() {
|
function Preferences() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { dialogs, auth } = useStores();
|
const { dialogs, auth } = useStores();
|
||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
@@ -33,16 +32,12 @@ function Preferences() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await auth.updateUser({ preferences });
|
await auth.updateUser({ preferences });
|
||||||
showToast(t("Preferences saved"), {
|
toast.success(t("Preferences saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLanguageChange = async (language: string) => {
|
const handleLanguageChange = async (language: string) => {
|
||||||
await auth.updateUser({ language });
|
await auth.updateUser({ language });
|
||||||
showToast(t("Preferences saved"), {
|
toast.success(t("Preferences saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const showDeleteAccount = () => {
|
const showDeleteAccount = () => {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||||||
import { ProfileIcon } from "outline-icons";
|
import { ProfileIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Heading from "~/components/Heading";
|
import Heading from "~/components/Heading";
|
||||||
import Input from "~/components/Input";
|
import Input from "~/components/Input";
|
||||||
@@ -9,7 +10,6 @@ import Scene from "~/components/Scene";
|
|||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import ImageInput from "./components/ImageInput";
|
import ImageInput from "./components/ImageInput";
|
||||||
import SettingRow from "./components/SettingRow";
|
import SettingRow from "./components/SettingRow";
|
||||||
|
|
||||||
@@ -18,7 +18,6 @@ const Profile = () => {
|
|||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const form = React.useRef<HTMLFormElement>(null);
|
const form = React.useRef<HTMLFormElement>(null);
|
||||||
const [name, setName] = React.useState<string>(user.name || "");
|
const [name, setName] = React.useState<string>(user.name || "");
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleSubmit = async (ev: React.SyntheticEvent) => {
|
const handleSubmit = async (ev: React.SyntheticEvent) => {
|
||||||
@@ -28,13 +27,9 @@ const Profile = () => {
|
|||||||
await auth.updateUser({
|
await auth.updateUser({
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
showToast(t("Profile saved"), {
|
toast.success(t("Profile saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,15 +41,11 @@ const Profile = () => {
|
|||||||
await auth.updateUser({
|
await auth.updateUser({
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
});
|
});
|
||||||
showToast(t("Profile picture updated"), {
|
toast.success(t("Profile picture updated"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAvatarError = (error: string | null | undefined) => {
|
const handleAvatarError = (error: string | null | undefined) => {
|
||||||
showToast(error || t("Unable to upload new profile picture"), {
|
toast.error(error || t("Unable to upload new profile picture"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValid = form.current?.checkValidity();
|
const isValid = form.current?.checkValidity();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { CheckboxIcon, EmailIcon, PadlockIcon } from "outline-icons";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { useTheme } from "styled-components";
|
import { useTheme } from "styled-components";
|
||||||
import { TeamPreference } from "@shared/types";
|
import { TeamPreference } from "@shared/types";
|
||||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||||
@@ -18,7 +19,6 @@ import env from "~/env";
|
|||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import useRequest from "~/hooks/useRequest";
|
import useRequest from "~/hooks/useRequest";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import isCloudHosted from "~/utils/isCloudHosted";
|
import isCloudHosted from "~/utils/isCloudHosted";
|
||||||
import DomainManagement from "./components/DomainManagement";
|
import DomainManagement from "./components/DomainManagement";
|
||||||
import SettingRow from "./components/SettingRow";
|
import SettingRow from "./components/SettingRow";
|
||||||
@@ -27,7 +27,6 @@ function Security() {
|
|||||||
const { auth, authenticationProviders, dialogs } = useStores();
|
const { auth, authenticationProviders, dialogs } = useStores();
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [data, setData] = useState({
|
const [data, setData] = useState({
|
||||||
sharing: team.sharing,
|
sharing: team.sharing,
|
||||||
@@ -53,11 +52,9 @@ function Security() {
|
|||||||
const showSuccessMessage = React.useMemo(
|
const showSuccessMessage = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
debounce(() => {
|
debounce(() => {
|
||||||
showToast(t("Settings saved"), {
|
toast.success(t("Settings saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
}, 250),
|
}, 250),
|
||||||
[showToast, t]
|
[t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const saveData = React.useCallback(
|
const saveData = React.useCallback(
|
||||||
@@ -67,12 +64,10 @@ function Security() {
|
|||||||
await auth.updateTeam(newData);
|
await auth.updateTeam(newData);
|
||||||
showSuccessMessage();
|
showSuccessMessage();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[auth, showSuccessMessage, showToast]
|
[auth, showSuccessMessage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChange = React.useCallback(
|
const handleChange = React.useCallback(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { BuildingBlocksIcon } from "outline-icons";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { IntegrationService, IntegrationType } from "@shared/types";
|
import { IntegrationService, IntegrationType } from "@shared/types";
|
||||||
import Integration from "~/models/Integration";
|
import Integration from "~/models/Integration";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
@@ -11,7 +12,6 @@ import Heading from "~/components/Heading";
|
|||||||
import Input from "~/components/Input";
|
import Input from "~/components/Input";
|
||||||
import Scene from "~/components/Scene";
|
import Scene from "~/components/Scene";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import SettingRow from "./components/SettingRow";
|
import SettingRow from "./components/SettingRow";
|
||||||
|
|
||||||
type FormData = {
|
type FormData = {
|
||||||
@@ -22,7 +22,6 @@ type FormData = {
|
|||||||
function SelfHosted() {
|
function SelfHosted() {
|
||||||
const { integrations } = useStores();
|
const { integrations } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
const integrationDiagrams = find(integrations.orderedData, {
|
const integrationDiagrams = find(integrations.orderedData, {
|
||||||
type: IntegrationType.Embed,
|
type: IntegrationType.Embed,
|
||||||
@@ -89,16 +88,12 @@ function SelfHosted() {
|
|||||||
await integrationGrist?.delete();
|
await integrationGrist?.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
showToast(t("Settings saved"), {
|
toast.success(t("Settings saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[integrations, integrationDiagrams, integrationGrist, t, showToast]
|
[integrations, integrationDiagrams, integrationGrist, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { CopyIcon } from "outline-icons";
|
import { CopyIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import ApiKey from "~/models/ApiKey";
|
import ApiKey from "~/models/ApiKey";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import CopyToClipboard from "~/components/CopyToClipboard";
|
import CopyToClipboard from "~/components/CopyToClipboard";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import ListItem from "~/components/List/Item";
|
import ListItem from "~/components/List/Item";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import ApiKeyMenu from "~/menus/ApiKeyMenu";
|
import ApiKeyMenu from "~/menus/ApiKeyMenu";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -15,7 +15,6 @@ type Props = {
|
|||||||
|
|
||||||
const ApiKeyListItem = ({ apiKey }: Props) => {
|
const ApiKeyListItem = ({ apiKey }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const [linkCopied, setLinkCopied] = React.useState<boolean>(false);
|
const [linkCopied, setLinkCopied] = React.useState<boolean>(false);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -28,10 +27,8 @@ const ApiKeyListItem = ({ apiKey }: Props) => {
|
|||||||
|
|
||||||
const handleCopy = React.useCallback(() => {
|
const handleCopy = React.useCallback(() => {
|
||||||
setLinkCopied(true);
|
setLinkCopied(true);
|
||||||
showToast(t("API token copied to clipboard"), {
|
toast.message(t("API token copied to clipboard"));
|
||||||
type: "success",
|
}, [t]);
|
||||||
});
|
|
||||||
}, [showToast, t]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||||||
import { CloseIcon } from "outline-icons";
|
import { CloseIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Fade from "~/components/Fade";
|
import Fade from "~/components/Fade";
|
||||||
@@ -11,7 +12,6 @@ import NudeButton from "~/components/NudeButton";
|
|||||||
import Tooltip from "~/components/Tooltip";
|
import Tooltip from "~/components/Tooltip";
|
||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import SettingRow from "./SettingRow";
|
import SettingRow from "./SettingRow";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -22,7 +22,6 @@ function DomainManagement({ onSuccess }: Props) {
|
|||||||
const { auth } = useStores();
|
const { auth } = useStores();
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
const [allowedDomains, setAllowedDomains] = React.useState([
|
const [allowedDomains, setAllowedDomains] = React.useState([
|
||||||
...(team.allowedDomains ?? []),
|
...(team.allowedDomains ?? []),
|
||||||
@@ -43,11 +42,9 @@ function DomainManagement({ onSuccess }: Props) {
|
|||||||
setExistingDomainsTouched(false);
|
setExistingDomainsTouched(false);
|
||||||
updateLastKnownDomainCount(allowedDomains.length);
|
updateLastKnownDomainCount(allowedDomains.length);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [auth, allowedDomains, onSuccess, showToast]);
|
}, [auth, allowedDomains, onSuccess]);
|
||||||
|
|
||||||
const handleRemoveDomain = async (index: number) => {
|
const handleRemoveDomain = async (index: number) => {
|
||||||
const newDomains = allowedDomains.filter((_, i) => index !== i);
|
const newDomains = allowedDomains.filter((_, i) => index !== i);
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { NewDocumentIcon } from "outline-icons";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import Dropzone from "react-dropzone";
|
import Dropzone from "react-dropzone";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { s } from "@shared/styles";
|
import { s } from "@shared/styles";
|
||||||
import { AttachmentPreset } from "@shared/types";
|
import { AttachmentPreset } from "@shared/types";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import LoadingIndicator from "~/components/LoadingIndicator";
|
import LoadingIndicator from "~/components/LoadingIndicator";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import { uploadFile } from "~/utils/files";
|
import { uploadFile } from "~/utils/files";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -23,15 +23,12 @@ type Props = {
|
|||||||
function DropToImport({ disabled, onSubmit, children, format }: Props) {
|
function DropToImport({ disabled, onSubmit, children, format }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { collections } = useStores();
|
const { collections } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const [isImporting, setImporting] = React.useState(false);
|
const [isImporting, setImporting] = React.useState(false);
|
||||||
|
|
||||||
const handleFiles = React.useCallback(
|
const handleFiles = React.useCallback(
|
||||||
async (files) => {
|
async (files) => {
|
||||||
if (files.length > 1) {
|
if (files.length > 1) {
|
||||||
showToast(t("Please choose a single file to import"), {
|
toast.error(t("Please choose a single file to import"));
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const file = files[0];
|
const file = files[0];
|
||||||
@@ -45,27 +42,21 @@ function DropToImport({ disabled, onSubmit, children, format }: Props) {
|
|||||||
});
|
});
|
||||||
await collections.import(attachment.id, format);
|
await collections.import(attachment.id, format);
|
||||||
onSubmit();
|
onSubmit();
|
||||||
showToast(
|
toast.success(
|
||||||
t("Your import is being processed, you can safely leave this page"),
|
t("Your import is being processed, you can safely leave this page")
|
||||||
{
|
|
||||||
type: "success",
|
|
||||||
timeout: 6000,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message);
|
toast.error(err.message);
|
||||||
} finally {
|
} finally {
|
||||||
setImporting(false);
|
setImporting(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[t, onSubmit, collections, format, showToast]
|
[t, onSubmit, collections, format]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRejection = React.useCallback(() => {
|
const handleRejection = React.useCallback(() => {
|
||||||
showToast(t("File not supported – please upload a valid ZIP file"), {
|
toast.error(t("File not supported – please upload a valid ZIP file"));
|
||||||
type: "error",
|
}, [t]);
|
||||||
});
|
|
||||||
}, [t, showToast]);
|
|
||||||
|
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return children;
|
return children;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||||||
import { ArchiveIcon, DoneIcon, WarningIcon } from "outline-icons";
|
import { ArchiveIcon, DoneIcon, WarningIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { useTheme } from "styled-components";
|
import { useTheme } from "styled-components";
|
||||||
import {
|
import {
|
||||||
FileOperationFormat,
|
FileOperationFormat,
|
||||||
@@ -16,7 +17,6 @@ import Spinner from "~/components/Spinner";
|
|||||||
import Time from "~/components/Time";
|
import Time from "~/components/Time";
|
||||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import FileOperationMenu from "~/menus/FileOperationMenu";
|
import FileOperationMenu from "~/menus/FileOperationMenu";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -28,7 +28,6 @@ const FileOperationListItem = ({ fileOperation }: Props) => {
|
|||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { dialogs, fileOperations } = useStores();
|
const { dialogs, fileOperations } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
const stateMapping = {
|
const stateMapping = {
|
||||||
[FileOperationState.Creating]: t("Processing"),
|
[FileOperationState.Creating]: t("Processing"),
|
||||||
@@ -65,16 +64,14 @@ const FileOperationListItem = ({ fileOperation }: Props) => {
|
|||||||
await fileOperations.delete(fileOperation);
|
await fileOperations.delete(fileOperation);
|
||||||
|
|
||||||
if (fileOperation.type === FileOperationType.Import) {
|
if (fileOperation.type === FileOperationType.Import) {
|
||||||
showToast(t("Import deleted"));
|
toast.success(t("Import deleted"));
|
||||||
} else {
|
} else {
|
||||||
showToast(t("Export deleted"));
|
toast.success(t("Export deleted"));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [fileOperation, fileOperations, showToast, t]);
|
}, [fileOperation, fileOperations, t]);
|
||||||
|
|
||||||
const handleConfirmDelete = React.useCallback(async () => {
|
const handleConfirmDelete = React.useCallback(async () => {
|
||||||
dialogs.openModal({
|
dialogs.openModal({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Input from "~/components/Input";
|
import Input from "~/components/Input";
|
||||||
@@ -9,7 +10,6 @@ import Text from "~/components/Text";
|
|||||||
import env from "~/env";
|
import env from "~/env";
|
||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type FormData = {
|
type FormData = {
|
||||||
code: string;
|
code: string;
|
||||||
@@ -22,7 +22,6 @@ type Props = {
|
|||||||
function TeamDelete({ onSubmit }: Props) {
|
function TeamDelete({ onSubmit }: Props) {
|
||||||
const [isWaitingCode, setWaitingCode] = React.useState(false);
|
const [isWaitingCode, setWaitingCode] = React.useState(false);
|
||||||
const { auth } = useStores();
|
const { auth } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
@@ -39,12 +38,10 @@ function TeamDelete({ onSubmit }: Props) {
|
|||||||
await auth.requestDeleteTeam();
|
await auth.requestDeleteTeam();
|
||||||
setWaitingCode(true);
|
setWaitingCode(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showToast(error.message, {
|
toast.error(error.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[auth, showToast]
|
[auth]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = React.useCallback(
|
||||||
@@ -54,12 +51,10 @@ function TeamDelete({ onSubmit }: Props) {
|
|||||||
await auth.logout();
|
await auth.logout();
|
||||||
onSubmit();
|
onSubmit();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showToast(error.message, {
|
toast.error(error.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[auth, onSubmit, showToast]
|
[auth, onSubmit]
|
||||||
);
|
);
|
||||||
|
|
||||||
const inputProps = register("code", {
|
const inputProps = register("code", {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import User from "~/models/User";
|
import User from "~/models/User";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
@@ -8,7 +9,6 @@ import Input from "~/components/Input";
|
|||||||
import Notice from "~/components/Notice";
|
import Notice from "~/components/Notice";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
user: User;
|
user: User;
|
||||||
@@ -17,7 +17,6 @@ type Props = {
|
|||||||
function TeamNew({ user }: Props) {
|
function TeamNew({ user }: Props) {
|
||||||
const { auth } = useStores();
|
const { auth } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const [name, setName] = React.useState("");
|
const [name, setName] = React.useState("");
|
||||||
const [isSaving, setIsSaving] = React.useState(false);
|
const [isSaving, setIsSaving] = React.useState(false);
|
||||||
|
|
||||||
@@ -32,9 +31,7 @@ function TeamNew({ user }: Props) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Input from "~/components/Input";
|
import Input from "~/components/Input";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import env from "~/env";
|
import env from "~/env";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type FormData = {
|
type FormData = {
|
||||||
code: string;
|
code: string;
|
||||||
@@ -17,7 +17,6 @@ type FormData = {
|
|||||||
function UserDelete() {
|
function UserDelete() {
|
||||||
const [isWaitingCode, setWaitingCode] = React.useState(false);
|
const [isWaitingCode, setWaitingCode] = React.useState(false);
|
||||||
const { auth } = useStores();
|
const { auth } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -32,13 +31,11 @@ function UserDelete() {
|
|||||||
try {
|
try {
|
||||||
await auth.requestDeleteUser();
|
await auth.requestDeleteUser();
|
||||||
setWaitingCode(true);
|
setWaitingCode(true);
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
showToast(error.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[auth, showToast]
|
[auth]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = React.useCallback(
|
||||||
@@ -46,13 +43,11 @@ function UserDelete() {
|
|||||||
try {
|
try {
|
||||||
await auth.deleteUser(data);
|
await auth.deleteUser(data);
|
||||||
await auth.logout();
|
await auth.logout();
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
showToast(error.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[auth, showToast]
|
[auth]
|
||||||
);
|
);
|
||||||
|
|
||||||
const inputProps = register("code", {
|
const inputProps = register("code", {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import SearchesStore from "./SearchesStore";
|
|||||||
import SharesStore from "./SharesStore";
|
import SharesStore from "./SharesStore";
|
||||||
import StarsStore from "./StarsStore";
|
import StarsStore from "./StarsStore";
|
||||||
import SubscriptionsStore from "./SubscriptionsStore";
|
import SubscriptionsStore from "./SubscriptionsStore";
|
||||||
import ToastsStore from "./ToastsStore";
|
|
||||||
import UiStore from "./UiStore";
|
import UiStore from "./UiStore";
|
||||||
import UsersStore from "./UsersStore";
|
import UsersStore from "./UsersStore";
|
||||||
import ViewsStore from "./ViewsStore";
|
import ViewsStore from "./ViewsStore";
|
||||||
@@ -53,7 +52,6 @@ export default class RootStore {
|
|||||||
subscriptions: SubscriptionsStore;
|
subscriptions: SubscriptionsStore;
|
||||||
users: UsersStore;
|
users: UsersStore;
|
||||||
views: ViewsStore;
|
views: ViewsStore;
|
||||||
toasts: ToastsStore;
|
|
||||||
fileOperations: FileOperationsStore;
|
fileOperations: FileOperationsStore;
|
||||||
webhookSubscriptions: WebhookSubscriptionsStore;
|
webhookSubscriptions: WebhookSubscriptionsStore;
|
||||||
|
|
||||||
@@ -85,7 +83,6 @@ export default class RootStore {
|
|||||||
this.users = new UsersStore(this);
|
this.users = new UsersStore(this);
|
||||||
this.views = new ViewsStore(this);
|
this.views = new ViewsStore(this);
|
||||||
this.fileOperations = new FileOperationsStore(this);
|
this.fileOperations = new FileOperationsStore(this);
|
||||||
this.toasts = new ToastsStore();
|
|
||||||
this.webhookSubscriptions = new WebhookSubscriptionsStore(this);
|
this.webhookSubscriptions = new WebhookSubscriptionsStore(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import stores from ".";
|
|
||||||
|
|
||||||
describe("ToastsStore", () => {
|
|
||||||
const store = stores.toasts;
|
|
||||||
|
|
||||||
test("#add should add messages", () => {
|
|
||||||
expect(store.orderedData.length).toBe(0);
|
|
||||||
|
|
||||||
store.showToast("first error");
|
|
||||||
store.showToast("second error");
|
|
||||||
expect(store.orderedData.length).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("#remove should remove messages", () => {
|
|
||||||
store.toasts.clear();
|
|
||||||
const id = store.showToast("first error");
|
|
||||||
store.showToast("second error");
|
|
||||||
|
|
||||||
expect(store.orderedData.length).toBe(2);
|
|
||||||
id && store.hideToast(id);
|
|
||||||
|
|
||||||
expect(store.orderedData.length).toBe(1);
|
|
||||||
expect(store.orderedData[0].message).toBe("second error");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import orderBy from "lodash/orderBy";
|
|
||||||
import { observable, action, computed } from "mobx";
|
|
||||||
import { v4 as uuidv4 } from "uuid";
|
|
||||||
import { Toast, ToastOptions } from "~/types";
|
|
||||||
|
|
||||||
export default class ToastsStore {
|
|
||||||
@observable
|
|
||||||
toasts: Map<string, Toast> = new Map();
|
|
||||||
|
|
||||||
lastToastId: string;
|
|
||||||
|
|
||||||
@action
|
|
||||||
showToast = (
|
|
||||||
message: string,
|
|
||||||
options: ToastOptions = {
|
|
||||||
type: "info",
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
if (!message) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const lastToast = this.toasts.get(this.lastToastId);
|
|
||||||
|
|
||||||
if (lastToast?.message === message) {
|
|
||||||
this.toasts.set(this.lastToastId, {
|
|
||||||
...lastToast,
|
|
||||||
reoccurring: lastToast.reoccurring ? ++lastToast.reoccurring : 1,
|
|
||||||
});
|
|
||||||
return this.lastToastId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = uuidv4();
|
|
||||||
const createdAt = new Date().toISOString();
|
|
||||||
this.toasts.set(id, {
|
|
||||||
id,
|
|
||||||
message,
|
|
||||||
createdAt,
|
|
||||||
type: options.type,
|
|
||||||
timeout: options.timeout,
|
|
||||||
action: options.action,
|
|
||||||
});
|
|
||||||
this.lastToastId = id;
|
|
||||||
return id;
|
|
||||||
};
|
|
||||||
|
|
||||||
@action
|
|
||||||
hideToast = (id: string) => {
|
|
||||||
this.toasts.delete(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
@computed
|
|
||||||
get orderedData(): Toast[] {
|
|
||||||
return orderBy(Array.from(this.toasts.values()), "createdAt", "desc");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
22
app/types.ts
22
app/types.ts
@@ -121,19 +121,6 @@ export type LocationWithState = Location & {
|
|||||||
state: Record<string, string>;
|
state: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Toast = {
|
|
||||||
id: string;
|
|
||||||
createdAt: string;
|
|
||||||
message: string;
|
|
||||||
type: "warning" | "error" | "info" | "success" | "loading";
|
|
||||||
timeout?: number;
|
|
||||||
reoccurring?: number;
|
|
||||||
action?: {
|
|
||||||
text: string;
|
|
||||||
onClick: React.MouseEventHandler<HTMLSpanElement>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type FetchOptions = {
|
export type FetchOptions = {
|
||||||
prefetch?: boolean;
|
prefetch?: boolean;
|
||||||
revisionId?: string;
|
revisionId?: string;
|
||||||
@@ -177,15 +164,6 @@ export type SearchResult = {
|
|||||||
document: Document;
|
document: Document;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ToastOptions = {
|
|
||||||
type: "warning" | "error" | "info" | "success" | "loading";
|
|
||||||
timeout?: number;
|
|
||||||
action?: {
|
|
||||||
text: string;
|
|
||||||
onClick: React.MouseEventHandler<HTMLSpanElement>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type WebsocketEntityDeletedEvent = {
|
export type WebsocketEntityDeletedEvent = {
|
||||||
modelId: string;
|
modelId: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -211,6 +211,7 @@
|
|||||||
"socket.io": "^4.7.2",
|
"socket.io": "^4.7.2",
|
||||||
"socket.io-client": "^4.6.1",
|
"socket.io-client": "^4.6.1",
|
||||||
"socket.io-redis": "^6.1.1",
|
"socket.io-redis": "^6.1.1",
|
||||||
|
"sonner": "^1.0.3",
|
||||||
"stoppable": "^1.1.0",
|
"stoppable": "^1.1.0",
|
||||||
"string-replace-to-array": "^2.1.0",
|
"string-replace-to-array": "^2.1.0",
|
||||||
"styled-components": "^5.3.11",
|
"styled-components": "^5.3.11",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { usePopoverState, PopoverDisclosure } from "reakit/Popover";
|
import { usePopoverState, PopoverDisclosure } from "reakit/Popover";
|
||||||
|
import { toast } from "sonner";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { s } from "@shared/styles";
|
import { s } from "@shared/styles";
|
||||||
import { IntegrationType } from "@shared/types";
|
import { IntegrationType } from "@shared/types";
|
||||||
@@ -16,7 +17,6 @@ import ListItem from "~/components/List/Item";
|
|||||||
import Popover from "~/components/Popover";
|
import Popover from "~/components/Popover";
|
||||||
import Switch from "~/components/Switch";
|
import Switch from "~/components/Switch";
|
||||||
import Text from "~/components/Text";
|
import Text from "~/components/Text";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
integration: Integration<IntegrationType.Post>;
|
integration: Integration<IntegrationType.Post>;
|
||||||
@@ -25,7 +25,6 @@ type Props = {
|
|||||||
|
|
||||||
function SlackListItem({ integration, collection }: Props) {
|
function SlackListItem({ integration, collection }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showToast } = useToasts();
|
|
||||||
|
|
||||||
const handleChange = async (ev: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = async (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (ev.target.checked) {
|
if (ev.target.checked) {
|
||||||
@@ -38,9 +37,7 @@ function SlackListItem({ integration, collection }: Props) {
|
|||||||
|
|
||||||
await integration.save();
|
await integration.save();
|
||||||
|
|
||||||
showToast(t("Settings saved"), {
|
toast.success(t("Settings saved"));
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapping = {
|
const mapping = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import WebhookSubscription from "~/models/WebhookSubscription";
|
import WebhookSubscription from "~/models/WebhookSubscription";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import WebhookSubscriptionForm from "./WebhookSubscriptionForm";
|
import WebhookSubscriptionForm from "./WebhookSubscriptionForm";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -16,7 +16,6 @@ interface FormData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function WebhookSubscriptionEdit({ onSubmit, webhookSubscription }: Props) {
|
function WebhookSubscriptionEdit({ onSubmit, webhookSubscription }: Props) {
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = React.useCallback(
|
||||||
@@ -31,19 +30,13 @@ function WebhookSubscriptionEdit({ onSubmit, webhookSubscription }: Props) {
|
|||||||
|
|
||||||
await webhookSubscription.save(toSend);
|
await webhookSubscription.save(toSend);
|
||||||
|
|
||||||
showToast(
|
toast.success(t("Webhook updated"));
|
||||||
t("Webhook updated", {
|
|
||||||
type: "success",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
onSubmit();
|
onSubmit();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[t, showToast, onSubmit, webhookSubscription]
|
[t, onSubmit, webhookSubscription]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { toast } from "sonner";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
|
||||||
import WebhookSubscriptionForm from "./WebhookSubscriptionForm";
|
import WebhookSubscriptionForm from "./WebhookSubscriptionForm";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -16,7 +16,6 @@ interface FormData {
|
|||||||
|
|
||||||
function WebhookSubscriptionNew({ onSubmit }: Props) {
|
function WebhookSubscriptionNew({ onSubmit }: Props) {
|
||||||
const { webhookSubscriptions } = useStores();
|
const { webhookSubscriptions } = useStores();
|
||||||
const { showToast } = useToasts();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = React.useCallback(
|
||||||
@@ -30,19 +29,13 @@ function WebhookSubscriptionNew({ onSubmit }: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await webhookSubscriptions.create(toSend);
|
await webhookSubscriptions.create(toSend);
|
||||||
showToast(
|
toast.success(t("Webhook created"));
|
||||||
t("Webhook created", {
|
|
||||||
type: "success",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
onSubmit();
|
onSubmit();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, {
|
toast.error(err.message);
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[t, showToast, onSubmit, webhookSubscriptions]
|
[t, onSubmit, webhookSubscriptions]
|
||||||
);
|
);
|
||||||
|
|
||||||
return <WebhookSubscriptionForm handleSubmit={handleSubmit} />;
|
return <WebhookSubscriptionForm handleSubmit={handleSubmit} />;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Node } from "prosemirror-model";
|
import { Node } from "prosemirror-model";
|
||||||
import { EditorView } from "prosemirror-view";
|
import { EditorView } from "prosemirror-view";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
function findPlaceholderLink(doc: Node, href: string) {
|
function findPlaceholderLink(doc: Node, href: string) {
|
||||||
let result: { pos: number; node: Node } | undefined;
|
let result: { pos: number; node: Node } | undefined;
|
||||||
@@ -38,11 +39,10 @@ const createAndInsertLink = async function (
|
|||||||
options: {
|
options: {
|
||||||
dictionary: any;
|
dictionary: any;
|
||||||
onCreateLink: (title: string) => Promise<string>;
|
onCreateLink: (title: string) => Promise<string>;
|
||||||
onShowToast: (message: string) => void;
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const { dispatch, state } = view;
|
const { dispatch, state } = view;
|
||||||
const { onCreateLink, onShowToast } = options;
|
const { onCreateLink } = options;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = await onCreateLink(title);
|
const url = await onCreateLink(title);
|
||||||
@@ -79,7 +79,7 @@ const createAndInsertLink = async function (
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
onShowToast(options.dictionary.createLinkError);
|
toast.error(options.dictionary.createLinkError);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { EditorView } from "prosemirror-view";
|
import { EditorView } from "prosemirror-view";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import FileHelper from "../lib/FileHelper";
|
import FileHelper from "../lib/FileHelper";
|
||||||
import uploadPlaceholderPlugin, {
|
import uploadPlaceholderPlugin, {
|
||||||
@@ -19,8 +20,6 @@ export type Options = {
|
|||||||
onFileUploadStart?: () => void;
|
onFileUploadStart?: () => void;
|
||||||
/** Callback fired when the user completes a file upload */
|
/** Callback fired when the user completes a file upload */
|
||||||
onFileUploadStop?: () => void;
|
onFileUploadStop?: () => void;
|
||||||
/** Callback fired when a toast needs to be displayed */
|
|
||||||
onShowToast: (message: string) => void;
|
|
||||||
/** Attributes to overwrite */
|
/** Attributes to overwrite */
|
||||||
attrs?: {
|
attrs?: {
|
||||||
/** Width to use when inserting image */
|
/** Width to use when inserting image */
|
||||||
@@ -40,13 +39,8 @@ const insertFiles = function (
|
|||||||
files: File[],
|
files: File[],
|
||||||
options: Options
|
options: Options
|
||||||
): void {
|
): void {
|
||||||
const {
|
const { dictionary, uploadFile, onFileUploadStart, onFileUploadStop } =
|
||||||
dictionary,
|
options;
|
||||||
uploadFile,
|
|
||||||
onFileUploadStart,
|
|
||||||
onFileUploadStop,
|
|
||||||
onShowToast,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
// okay, we have some dropped files and a handler – lets stop this
|
// okay, we have some dropped files and a handler – lets stop this
|
||||||
// event going any further up the stack
|
// event going any further up the stack
|
||||||
@@ -172,7 +166,7 @@ const insertFiles = function (
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
onShowToast(error.message || dictionary.fileUploadError);
|
toast.error(error.message || dictionary.fileUploadError);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
complete++;
|
complete++;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { Command, EditorState, Plugin } from "prosemirror-state";
|
|||||||
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { isExternalUrl, sanitizeUrl } from "../../utils/urls";
|
import { isExternalUrl, sanitizeUrl } from "../../utils/urls";
|
||||||
import findLinkNodes from "../queries/findLinkNodes";
|
import findLinkNodes from "../queries/findLinkNodes";
|
||||||
import getMarkRange from "../queries/getMarkRange";
|
import getMarkRange from "../queries/getMarkRange";
|
||||||
@@ -138,9 +139,7 @@ export default class Link extends Mark {
|
|||||||
event
|
event
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.editor.props.onShowToast(
|
toast.error(this.options.dictionary.openLinkError);
|
||||||
this.options.dictionary.openLinkError
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -177,9 +176,7 @@ export default class Link extends Mark {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.editor.props.onShowToast(
|
toast.error(this.options.dictionary.openLinkError);
|
||||||
this.options.dictionary.openLinkError
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return cloned;
|
return cloned;
|
||||||
@@ -246,9 +243,7 @@ export default class Link extends Mark {
|
|||||||
this.options.onClickLink(sanitizeUrl(href), event);
|
this.options.onClickLink(sanitizeUrl(href), event);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.editor.props.onShowToast(
|
toast.error(this.options.dictionary.openLinkError);
|
||||||
this.options.dictionary.openLinkError
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import visualbasic from "refractor/lang/visual-basic";
|
|||||||
import yaml from "refractor/lang/yaml";
|
import yaml from "refractor/lang/yaml";
|
||||||
import zig from "refractor/lang/zig";
|
import zig from "refractor/lang/zig";
|
||||||
|
|
||||||
|
import { toast } from "sonner";
|
||||||
import { Primitive } from "utility-types";
|
import { Primitive } from "utility-types";
|
||||||
import { Dictionary } from "~/hooks/useDictionary";
|
import { Dictionary } from "~/hooks/useDictionary";
|
||||||
import { UserPreferences } from "../../types";
|
import { UserPreferences } from "../../types";
|
||||||
@@ -130,7 +131,6 @@ export default class CodeFence extends Node {
|
|||||||
constructor(options: {
|
constructor(options: {
|
||||||
dictionary: Dictionary;
|
dictionary: Dictionary;
|
||||||
userPreferences?: UserPreferences | null;
|
userPreferences?: UserPreferences | null;
|
||||||
onShowToast: (message: string) => void;
|
|
||||||
}) {
|
}) {
|
||||||
super(options);
|
super(options);
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ export default class CodeFence extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
copy(codeBlock.node.textContent);
|
copy(codeBlock.node.textContent);
|
||||||
this.options.onShowToast(this.options.dictionary.codeCopied);
|
toast.message(this.options.dictionary.codeCopied);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from "prosemirror-model";
|
} from "prosemirror-model";
|
||||||
import { Command, Plugin, Selection } from "prosemirror-state";
|
import { Command, Plugin, Selection } from "prosemirror-state";
|
||||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { Primitive } from "utility-types";
|
import { Primitive } from "utility-types";
|
||||||
import Storage from "../../utils/Storage";
|
import Storage from "../../utils/Storage";
|
||||||
import backspaceToParagraph from "../commands/backspaceToParagraph";
|
import backspaceToParagraph from "../commands/backspaceToParagraph";
|
||||||
@@ -188,7 +189,7 @@ export default class Heading extends Node {
|
|||||||
.replace("/edit", "");
|
.replace("/edit", "");
|
||||||
copy(normalizedUrl + hash);
|
copy(normalizedUrl + hash);
|
||||||
|
|
||||||
this.options.onShowToast(this.options.dictionary.linkCopied);
|
toast.message(this.options.dictionary.linkCopied);
|
||||||
};
|
};
|
||||||
|
|
||||||
keys({ type, schema }: { type: NodeType; schema: Schema }) {
|
keys({ type, schema }: { type: NodeType; schema: Schema }) {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export default class SimpleImage extends Node {
|
|||||||
}
|
}
|
||||||
const { view } = this.editor;
|
const { view } = this.editor;
|
||||||
const { node } = state.selection;
|
const { node } = state.selection;
|
||||||
const { uploadFile, onFileUploadStart, onFileUploadStop, onShowToast } =
|
const { uploadFile, onFileUploadStart, onFileUploadStop } =
|
||||||
this.editor.props;
|
this.editor.props;
|
||||||
|
|
||||||
if (!uploadFile) {
|
if (!uploadFile) {
|
||||||
@@ -146,7 +146,6 @@ export default class SimpleImage extends Node {
|
|||||||
uploadFile,
|
uploadFile,
|
||||||
onFileUploadStart,
|
onFileUploadStart,
|
||||||
onFileUploadStop,
|
onFileUploadStop,
|
||||||
onShowToast,
|
|
||||||
dictionary: this.options.dictionary,
|
dictionary: this.options.dictionary,
|
||||||
replaceExisting: true,
|
replaceExisting: true,
|
||||||
attrs: {
|
attrs: {
|
||||||
|
|||||||
@@ -132,6 +132,7 @@
|
|||||||
"Submenu": "Submenu",
|
"Submenu": "Submenu",
|
||||||
"Collections could not be loaded, please reload the app": "Collections could not be loaded, please reload the app",
|
"Collections could not be loaded, please reload the app": "Collections could not be loaded, please reload the app",
|
||||||
"Default collection": "Default collection",
|
"Default collection": "Default collection",
|
||||||
|
"Install now": "Install now",
|
||||||
"Deleted Collection": "Deleted Collection",
|
"Deleted Collection": "Deleted Collection",
|
||||||
"Unpin": "Unpin",
|
"Unpin": "Unpin",
|
||||||
"Search collections & documents": "Search collections & documents",
|
"Search collections & documents": "Search collections & documents",
|
||||||
@@ -191,9 +192,9 @@
|
|||||||
"{{userName}} published": "{{userName}} published",
|
"{{userName}} published": "{{userName}} published",
|
||||||
"{{userName}} unpublished": "{{userName}} unpublished",
|
"{{userName}} unpublished": "{{userName}} unpublished",
|
||||||
"{{userName}} moved": "{{userName}} moved",
|
"{{userName}} moved": "{{userName}} moved",
|
||||||
"Your file will be available in {{ location }} soon": "Your file will be available in {{ location }} soon",
|
|
||||||
"Go to exports": "Go to exports",
|
|
||||||
"Export started": "Export started",
|
"Export started": "Export started",
|
||||||
|
"Your file will be available in {{ location }} soon": "Your file will be available in {{ location }} soon",
|
||||||
|
"View": "View",
|
||||||
"A ZIP file containing the images, and documents in the Markdown format.": "A ZIP file containing the images, and documents in the Markdown format.",
|
"A ZIP file containing the images, and documents in the Markdown format.": "A ZIP file containing the images, and documents in the Markdown format.",
|
||||||
"A ZIP file containing the images, and documents as HTML files.": "A ZIP file containing the images, and documents as HTML files.",
|
"A ZIP file containing the images, and documents as HTML files.": "A ZIP file containing the images, and documents as HTML files.",
|
||||||
"Structured data that can be used to transfer data to another compatible {{ appName }} instance.": "Structured data that can be used to transfer data to another compatible {{ appName }} instance.",
|
"Structured data that can be used to transfer data to another compatible {{ appName }} instance.": "Structured data that can be used to transfer data to another compatible {{ appName }} instance.",
|
||||||
|
|||||||
@@ -152,8 +152,8 @@ export const buildLightTheme = (input: Partial<Colors>): DefaultTheme => {
|
|||||||
buttonNeutralBorder: darken(0.15, colors.white),
|
buttonNeutralBorder: darken(0.15, colors.white),
|
||||||
tooltipBackground: colors.almostBlack,
|
tooltipBackground: colors.almostBlack,
|
||||||
tooltipText: colors.white,
|
tooltipText: colors.white,
|
||||||
toastBackground: colors.almostBlack,
|
toastBackground: colors.white,
|
||||||
toastText: colors.white,
|
toastText: colors.almostBlack,
|
||||||
quote: colors.slateLight,
|
quote: colors.slateLight,
|
||||||
codeBackground: colors.smoke,
|
codeBackground: colors.smoke,
|
||||||
codeBorder: colors.smokeDark,
|
codeBorder: colors.smokeDark,
|
||||||
@@ -215,8 +215,8 @@ export const buildDarkTheme = (input: Partial<Colors>): DefaultTheme => {
|
|||||||
buttonNeutralBorder: colors.slateDark,
|
buttonNeutralBorder: colors.slateDark,
|
||||||
tooltipBackground: colors.white,
|
tooltipBackground: colors.white,
|
||||||
tooltipText: colors.lightBlack,
|
tooltipText: colors.lightBlack,
|
||||||
toastBackground: colors.white,
|
toastBackground: colors.veryDarkBlue,
|
||||||
toastText: colors.lightBlack,
|
toastText: colors.almostWhite,
|
||||||
quote: colors.almostWhite,
|
quote: colors.almostWhite,
|
||||||
code: colors.almostWhite,
|
code: colors.almostWhite,
|
||||||
codeBackground: colors.black75,
|
codeBackground: colors.black75,
|
||||||
|
|||||||
102
yarn.lock
102
yarn.lock
@@ -40,14 +40,7 @@
|
|||||||
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3"
|
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3"
|
||||||
chokidar "^3.4.0"
|
chokidar "^3.4.0"
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5":
|
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.22.5":
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658"
|
|
||||||
integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/highlight" "^7.22.5"
|
|
||||||
|
|
||||||
"@babel/code-frame@^7.22.13":
|
|
||||||
version "7.22.13"
|
version "7.22.13"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
|
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
|
||||||
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
|
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
|
||||||
@@ -81,17 +74,7 @@
|
|||||||
json5 "^2.2.2"
|
json5 "^2.2.2"
|
||||||
semver "^6.3.0"
|
semver "^6.3.0"
|
||||||
|
|
||||||
"@babel/generator@^7.22.5", "@babel/generator@^7.7.2":
|
"@babel/generator@^7.22.5", "@babel/generator@^7.23.0", "@babel/generator@^7.7.2":
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7"
|
|
||||||
integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==
|
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.22.5"
|
|
||||||
"@jridgewell/gen-mapping" "^0.3.2"
|
|
||||||
"@jridgewell/trace-mapping" "^0.3.17"
|
|
||||||
jsesc "^2.5.1"
|
|
||||||
|
|
||||||
"@babel/generator@^7.23.0":
|
|
||||||
version "7.23.0"
|
version "7.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
|
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
|
||||||
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
|
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
|
||||||
@@ -175,12 +158,7 @@
|
|||||||
resolve "^1.14.2"
|
resolve "^1.14.2"
|
||||||
semver "^6.1.2"
|
semver "^6.1.2"
|
||||||
|
|
||||||
"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.5":
|
"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20", "@babel/helper-environment-visitor@^7.22.5":
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98"
|
|
||||||
integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==
|
|
||||||
|
|
||||||
"@babel/helper-environment-visitor@^7.22.20":
|
|
||||||
version "7.22.20"
|
version "7.22.20"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
|
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
|
||||||
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
|
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
|
||||||
@@ -192,15 +170,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.18.6"
|
"@babel/types" "^7.18.6"
|
||||||
|
|
||||||
"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0":
|
"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0", "@babel/helper-function-name@^7.23.0":
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be"
|
|
||||||
integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/template" "^7.22.5"
|
|
||||||
"@babel/types" "^7.22.5"
|
|
||||||
|
|
||||||
"@babel/helper-function-name@^7.23.0":
|
|
||||||
version "7.23.0"
|
version "7.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
|
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
|
||||||
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
|
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
|
||||||
@@ -291,14 +261,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.20.0"
|
"@babel/types" "^7.20.0"
|
||||||
|
|
||||||
"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.5":
|
"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.5", "@babel/helper-split-export-declaration@^7.22.6":
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08"
|
|
||||||
integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.22.5"
|
|
||||||
|
|
||||||
"@babel/helper-split-export-declaration@^7.22.6":
|
|
||||||
version "7.22.6"
|
version "7.22.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
|
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
|
||||||
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
|
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
|
||||||
@@ -339,7 +302,7 @@
|
|||||||
"@babel/traverse" "^7.22.5"
|
"@babel/traverse" "^7.22.5"
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/highlight@^7.22.13":
|
"@babel/highlight@^7.22.13", "@babel/highlight@^7.22.5":
|
||||||
version "7.22.20"
|
version "7.22.20"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
|
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
|
||||||
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
|
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
|
||||||
@@ -348,21 +311,7 @@
|
|||||||
chalk "^2.4.2"
|
chalk "^2.4.2"
|
||||||
js-tokens "^4.0.0"
|
js-tokens "^4.0.0"
|
||||||
|
|
||||||
"@babel/highlight@^7.22.5":
|
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.22.15", "@babel/parser@^7.22.5", "@babel/parser@^7.23.0", "@babel/parser@^7.7.0":
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031"
|
|
||||||
integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/helper-validator-identifier" "^7.22.5"
|
|
||||||
chalk "^2.0.0"
|
|
||||||
js-tokens "^4.0.0"
|
|
||||||
|
|
||||||
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.22.5", "@babel/parser@^7.7.0":
|
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea"
|
|
||||||
integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==
|
|
||||||
|
|
||||||
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
|
|
||||||
version "7.23.0"
|
version "7.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
|
||||||
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
|
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
|
||||||
@@ -1110,16 +1059,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.5", "@babel/template@^7.3.3":
|
"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3":
|
||||||
version "7.22.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
|
|
||||||
integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/code-frame" "^7.22.5"
|
|
||||||
"@babel/parser" "^7.22.5"
|
|
||||||
"@babel/types" "^7.22.5"
|
|
||||||
|
|
||||||
"@babel/template@^7.22.15":
|
|
||||||
version "7.22.15"
|
version "7.22.15"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
|
||||||
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
|
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
|
||||||
@@ -1144,16 +1084,7 @@
|
|||||||
debug "^4.1.0"
|
debug "^4.1.0"
|
||||||
globals "^11.1.0"
|
globals "^11.1.0"
|
||||||
|
|
||||||
"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
|
"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
|
||||||
version "7.22.19"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.19.tgz#7425343253556916e440e662bb221a93ddb75684"
|
|
||||||
integrity sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==
|
|
||||||
dependencies:
|
|
||||||
"@babel/helper-string-parser" "^7.22.5"
|
|
||||||
"@babel/helper-validator-identifier" "^7.22.19"
|
|
||||||
to-fast-properties "^2.0.0"
|
|
||||||
|
|
||||||
"@babel/types@^7.23.0":
|
|
||||||
version "7.23.0"
|
version "7.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
|
||||||
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
|
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
|
||||||
@@ -7795,15 +7726,7 @@ immediate@~3.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||||
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
|
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
|
||||||
|
|
||||||
import-fresh@^3.2.1:
|
import-fresh@^3.2.1, import-fresh@^3.3.0:
|
||||||
version "3.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.2.tgz#fc129c160c5d68235507f4331a6baad186bdbc3e"
|
|
||||||
integrity sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==
|
|
||||||
dependencies:
|
|
||||||
parent-module "^1.0.0"
|
|
||||||
resolve-from "^4.0.0"
|
|
||||||
|
|
||||||
import-fresh@^3.3.0:
|
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
||||||
integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
|
integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
|
||||||
@@ -12189,6 +12112,11 @@ socks@^2.3.3:
|
|||||||
ip "^2.0.0"
|
ip "^2.0.0"
|
||||||
smart-buffer "^4.2.0"
|
smart-buffer "^4.2.0"
|
||||||
|
|
||||||
|
sonner@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/sonner/-/sonner-1.0.3.tgz#3d5c08f1773c28e98e51dba527350d4ac1c912a2"
|
||||||
|
integrity sha512-hBoA2zKuYW3lUnpx4K0vAn8j77YuYiwvP9sLQfieNS2pd5FkT20sMyPTDJnl9S+5T27ZJbwQRPiujwvDBwhZQg==
|
||||||
|
|
||||||
sort-keys@^5.0.0:
|
sort-keys@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-5.0.0.tgz#5d775f8ae93ecc29bc7312bbf3acac4e36e3c446"
|
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-5.0.0.tgz#5d775f8ae93ecc29bc7312bbf3acac4e36e3c446"
|
||||||
|
|||||||
Reference in New Issue
Block a user