chore: Improve toasts

This commit is contained in:
Tom Moor
2021-01-02 21:47:02 -08:00
parent bb81aa0065
commit 9df02d6fd4
32 changed files with 101 additions and 59 deletions

View File

@ -60,7 +60,9 @@ class DropToImport extends React.Component<Props> {
}
}
} catch (err) {
this.props.ui.showToast(`Could not import file. ${err.message}`);
this.props.ui.showToast(`Could not import file. ${err.message}`, {
type: "error",
});
} finally {
this.isImporting = false;
importingLock = false;

View File

@ -52,7 +52,9 @@ function EditableTitle({ title, onSubmit, canUpdate }: Props) {
setOriginalValue(value);
} catch (error) {
setValue(originalValue);
ui.showToast(error.message);
ui.showToast(error.message, {
type: "error",
});
throw error;
}
}

View File

@ -110,7 +110,9 @@ class SocketProvider extends React.Component<Props> {
this.socket.on("unauthorized", (err) => {
this.socket.authenticated = false;
ui.showToast(err.message);
ui.showToast(err.message, {
type: "error",
});
throw err;
});

View File

@ -1,4 +1,5 @@
// @flow
import { CheckboxIcon, InfoIcon, WarningIcon } from "outline-icons";
import { darken } from "polished";
import * as React from "react";
import styled, { css } from "styled-components";
@ -14,7 +15,7 @@ type Props = {
function Toast({ closeAfterMs = 3000, onRequestClose, toast }: Props) {
const timeout = React.useRef();
const [pulse, setPulse] = React.useState(false);
const { action, reoccurring } = toast;
const { action, type = "info", reoccurring } = toast;
React.useEffect(() => {
timeout.current = setTimeout(onRequestClose, toast.timeout || closeAfterMs);
@ -42,6 +43,10 @@ function Toast({ closeAfterMs = 3000, onRequestClose, toast }: Props) {
onClick={action ? undefined : onRequestClose}
type={toast.type || "success"}
>
{type === "info" && <InfoIcon color="currentColor" />}
{type === "success" && <CheckboxIcon checked color="currentColor" />}
{type === "warning" ||
(type === "error" && <WarningIcon color="currentColor" />)}
<Message>{message}</Message>
{action && (
<Action type={toast.type || "success"} onClick={action.onClick}>
@ -78,10 +83,11 @@ const ListItem = styled.li`
`;
const Container = styled.div`
display: inline-block;
display: inline-flex;
align-items: center;
animation: ${fadeAndScaleIn} 100ms ease;
margin: 8px 0;
padding: 0 12px;
color: ${(props) => props.theme.toastText};
background: ${(props) => props.theme.toastBackground};
font-size: 15px;
@ -95,7 +101,8 @@ const Container = styled.div`
const Message = styled.div`
display: inline-block;
padding: 10px 12px;
font-weight: 500;
padding: 10px 4px;
`;
export default Toast;

View File

@ -66,7 +66,9 @@ class CollectionMenu extends React.Component<Props> {
);
this.props.history.push(document.url);
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, {
type: "error",
});
}
};

View File

@ -86,7 +86,7 @@ class DocumentMenu extends React.Component<Props> {
// when duplicating, go straight to the duplicated document content
this.redirectTo = duped.url;
const { t } = this.props;
this.props.ui.showToast(t("Document duplicated"));
this.props.ui.showToast(t("Document duplicated"), { type: "success" });
};
handleOpenTemplateModal = () => {
@ -104,7 +104,7 @@ class DocumentMenu extends React.Component<Props> {
handleArchive = async (ev: SyntheticEvent<>) => {
await this.props.document.archive();
const { t } = this.props;
this.props.ui.showToast(t("Document archived"));
this.props.ui.showToast(t("Document archived"), { type: "success" });
};
handleRestore = async (
@ -113,13 +113,13 @@ class DocumentMenu extends React.Component<Props> {
) => {
await this.props.document.restore(options);
const { t } = this.props;
this.props.ui.showToast(t("Document restored"));
this.props.ui.showToast(t("Document restored"), { type: "success" });
};
handleUnpublish = async (ev: SyntheticEvent<>) => {
await this.props.document.unpublish();
const { t } = this.props;
this.props.ui.showToast(t("Document unpublished"));
this.props.ui.showToast(t("Document unpublished"), { type: "success" });
};
handlePin = (ev: SyntheticEvent<>) => {

View File

@ -28,13 +28,13 @@ class RevisionMenu extends React.Component<Props> {
ev.preventDefault();
await this.props.document.restore({ revisionId: this.props.revision.id });
const { t } = this.props;
this.props.ui.showToast(t("Document restored"));
this.props.ui.showToast(t("Document restored"), { type: "success" });
this.props.history.push(this.props.document.url);
};
handleCopy = () => {
const { t } = this.props;
this.props.ui.showToast(t("Link copied"));
this.props.ui.showToast(t("Link copied"), { type: "info" });
};
render() {

View File

@ -39,15 +39,15 @@ class ShareMenu extends React.Component<Props> {
try {
await this.props.shares.revoke(this.props.share);
const { t } = this.props;
this.props.ui.showToast(t("Share link revoked"));
this.props.ui.showToast(t("Share link revoked"), { type: "info" });
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
}
};
handleCopy = () => {
const { t } = this.props;
this.props.ui.showToast(t("Share link copied"));
this.props.ui.showToast(t("Share link copied"), { type: "info" });
};
render() {

View File

@ -32,7 +32,7 @@ class CollectionDelete extends React.Component<Props> {
this.props.history.push(homeUrl());
this.props.onSubmit();
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isDeleting = false;
}

View File

@ -47,9 +47,11 @@ class CollectionEdit extends React.Component<Props> {
sort: this.sort,
});
this.props.onSubmit();
this.props.ui.showToast(t("The collection was updated"));
this.props.ui.showToast(t("The collection was updated"), {
type: "success",
});
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isSaving = false;
}

View File

@ -67,10 +67,11 @@ class AddGroupsToCollection extends React.Component<Props> {
this.props.ui.showToast(
t("{{ groupName }} was added to the collection", {
groupName: group.name,
})
}),
{ type: "success" }
);
} catch (err) {
this.props.ui.showToast(t("Could not add user"));
this.props.ui.showToast(t("Could not add user"), { type: "error" });
console.error(err);
}
};

View File

@ -62,10 +62,13 @@ class AddPeopleToCollection extends React.Component<Props> {
permission: "read_write",
});
this.props.ui.showToast(
t("{{ userName }} was added to the collection", { userName: user.name })
t("{{ userName }} was added to the collection", {
userName: user.name,
}),
{ type: "success" }
);
} catch (err) {
this.props.ui.showToast(t("Could not add user"));
this.props.ui.showToast(t("Could not add user"), { type: "error" });
}
};

View File

@ -61,9 +61,11 @@ class CollectionMembers extends React.Component<Props> {
collectionId: this.props.collection.id,
userId: user.id,
});
this.props.ui.showToast(`${user.name} was removed from the collection`);
this.props.ui.showToast(`${user.name} was removed from the collection`, {
type: "success",
});
} catch (err) {
this.props.ui.showToast("Could not remove user");
this.props.ui.showToast("Could not remove user", { type: "error" });
}
};
@ -74,9 +76,11 @@ class CollectionMembers extends React.Component<Props> {
userId: user.id,
permission,
});
this.props.ui.showToast(`${user.name} permissions were updated`);
this.props.ui.showToast(`${user.name} permissions were updated`, {
type: "success",
});
} catch (err) {
this.props.ui.showToast("Could not update user");
this.props.ui.showToast("Could not update user", { type: "error" });
}
};
@ -86,9 +90,11 @@ class CollectionMembers extends React.Component<Props> {
collectionId: this.props.collection.id,
groupId: group.id,
});
this.props.ui.showToast(`${group.name} was removed from the collection`);
this.props.ui.showToast(`${group.name} was removed from the collection`, {
type: "success",
});
} catch (err) {
this.props.ui.showToast("Could not remove group");
this.props.ui.showToast("Could not remove group", { type: "error" });
}
};
@ -99,9 +105,11 @@ class CollectionMembers extends React.Component<Props> {
groupId: group.id,
permission,
});
this.props.ui.showToast(`${group.name} permissions were updated`);
this.props.ui.showToast(`${group.name} permissions were updated`, {
type: "success",
});
} catch (err) {
this.props.ui.showToast("Could not update user");
this.props.ui.showToast("Could not update user", { type: "error" });
}
};

View File

@ -53,7 +53,7 @@ class CollectionNew extends React.Component<Props> {
this.props.onSubmit();
this.props.history.push(collection.url);
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isSaving = false;
}

View File

@ -100,6 +100,7 @@ class DocumentScene extends React.Component<Props> {
`Document updated by ${document.updatedBy.name}`,
{
timeout: 30 * 1000,
type: "warning",
action: {
text: "Reload",
onClick: () => {
@ -239,7 +240,7 @@ class DocumentScene extends React.Component<Props> {
this.props.ui.setActiveDocument(savedDocument);
}
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isSaving = false;
this.isPublishing = false;

View File

@ -86,7 +86,7 @@ class DocumentMove extends React.Component<Props> {
}
handleSuccess = () => {
this.props.ui.showToast("Document moved");
this.props.ui.showToast("Document moved", { type: "info" });
this.props.onRequestClose();
};

View File

@ -48,7 +48,7 @@ class DocumentDelete extends React.Component<Props> {
}
this.props.onSubmit();
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isDeleting = false;
}

View File

@ -37,7 +37,9 @@ class DocumentNew extends React.Component<Props> {
});
this.props.history.replace(editDocumentUrl(document));
} catch (err) {
this.props.ui.showToast("Couldnt create the document, try again?");
this.props.ui.showToast("Couldnt create the document, try again?", {
type: "error",
});
this.props.history.goBack();
}
}

View File

@ -45,7 +45,7 @@ class DocumentShare extends React.Component<Props> {
try {
await share.save({ published: event.target.checked });
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isSaving = false;
}

View File

@ -28,10 +28,12 @@ class DocumentTemplatize extends React.Component<Props> {
try {
const template = await this.props.document.templatize();
this.props.history.push(documentUrl(template));
this.props.ui.showToast("Template created, go ahead and customize it");
this.props.ui.showToast("Template created, go ahead and customize it", {
type: "info",
});
this.props.onSubmit();
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isSaving = false;
}

View File

@ -30,7 +30,7 @@ class GroupDelete extends React.Component<Props> {
this.props.history.push(groupSettings());
this.props.onSubmit();
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isDeleting = false;
}

View File

@ -30,7 +30,7 @@ class GroupEdit extends React.Component<Props> {
await this.props.group.save({ name: this.name });
this.props.onSubmit();
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isSaving = false;
}

View File

@ -62,10 +62,11 @@ class AddPeopleToGroup extends React.Component<Props> {
userId: user.id,
});
this.props.ui.showToast(
t(`{{userName}} was added to the group`, { userName: user.name })
t(`{{userName}} was added to the group`, { userName: user.name }),
{ type: "success" }
);
} catch (err) {
this.props.ui.showToast(t("Could not add user"));
this.props.ui.showToast(t("Could not add user"), { type: "error" });
}
};

View File

@ -52,10 +52,11 @@ class GroupMembers extends React.Component<Props> {
userId: user.id,
});
this.props.ui.showToast(
t(`{{userName}} was removed from the group`, { userName: user.name })
t(`{{userName}} was removed from the group`, { userName: user.name }),
{ type: "success" }
);
} catch (err) {
this.props.ui.showToast(t("Could not remove user"));
this.props.ui.showToast(t("Could not remove user"), { type: "error" });
}
};

View File

@ -39,7 +39,7 @@ class GroupNew extends React.Component<Props> {
try {
this.group = await group.save();
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isSaving = false;
}

View File

@ -51,9 +51,9 @@ class Invite extends React.Component<Props> {
try {
await this.props.users.invite(this.invites);
this.props.onSubmit();
this.props.ui.showToast("We sent out your invites!");
this.props.ui.showToast("We sent out your invites!", { type: "success" });
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
} finally {
this.isSaving = false;
}
@ -73,7 +73,8 @@ class Invite extends React.Component<Props> {
handleAdd = () => {
if (this.invites.length >= MAX_INVITES) {
this.props.ui.showToast(
`Sorry, you can only send ${MAX_INVITES} invites at a time`
`Sorry, you can only send ${MAX_INVITES} invites at a time`,
{ type: "warning" }
);
}
@ -88,7 +89,9 @@ class Invite extends React.Component<Props> {
handleCopy = () => {
this.linkCopied = true;
this.props.ui.showToast("A link was copied to your clipboard");
this.props.ui.showToast("Share link copied", {
type: "success",
});
};
render() {

View File

@ -52,9 +52,9 @@ class Details extends React.Component<Props> {
avatarUrl: this.avatarUrl,
subdomain: this.subdomain,
});
this.props.ui.showToast("Settings saved");
this.props.ui.showToast("Settings saved", { type: "success" });
} catch (err) {
this.props.ui.showToast(err.message);
this.props.ui.showToast(err.message, { type: "error" });
}
};

View File

@ -29,7 +29,7 @@ class Export extends React.Component<Props> {
try {
await this.props.collections.export();
this.isExporting = true;
this.props.ui.showToast("Export in progress…");
this.props.ui.showToast("Export in progress…", { type: "info" });
} finally {
this.isLoading = false;
}

View File

@ -75,7 +75,7 @@ class Notifications extends React.Component<Props> {
};
showSuccessMessage = debounce(() => {
this.props.ui.showToast("Notifications saved");
this.props.ui.showToast("Notifications saved", { type: "success" });
}, 500);
render() {

View File

@ -55,7 +55,7 @@ class Profile extends React.Component<Props> {
language: this.language,
});
this.props.ui.showToast(t("Profile saved"));
this.props.ui.showToast(t("Profile saved"), { type: "success" });
};
handleNameChange = (ev: SyntheticInputEvent<*>) => {
@ -69,12 +69,15 @@ class Profile extends React.Component<Props> {
await this.props.auth.updateUser({
avatarUrl: this.avatarUrl,
});
this.props.ui.showToast(t("Profile picture updated"));
this.props.ui.showToast(t("Profile picture updated"), { type: "success" });
};
handleAvatarError = (error: ?string) => {
const { t } = this.props;
this.props.ui.showToast(error || t("Unable to upload new profile picture"));
this.props.ui.showToast(
error || t("Unable to upload new profile picture"),
{ type: "error" }
);
};
handleLanguageChange = (ev: SyntheticInputEvent<*>) => {

View File

@ -56,7 +56,7 @@ class Security extends React.Component<Props> {
};
showSuccessMessage = debounce(() => {
this.props.ui.showToast("Settings saved");
this.props.ui.showToast("Settings saved", { type: "success" });
}, 500);
render() {

View File

@ -27,7 +27,7 @@ class UserDelete extends React.Component<Props> {
await this.props.auth.deleteUser();
this.props.auth.logout();
} catch (error) {
this.props.ui.showToast(error.message);
this.props.ui.showToast(error.message, { type: "error" });
} finally {
this.isDeleting = false;
}