Add optional export notifications (#3935)
* Add `emails.export_completed` notification to settings menu Signed-off-by: AKP <tom@tdpain.net> * Don't send email when export_completed notifications are disabled Signed-off-by: AKP <tom@tdpain.net> * Automatically subscribe new users to `export_completed` notifications Signed-off-by: AKP <tom@tdpain.net> * Alter secondary text on export page to mention optional notifications Signed-off-by: AKP <tom@tdpain.net> * Alter toast text on collection export for optional notifications Signed-off-by: AKP <tom@tdpain.net> * Only subscribe new admins to export notifs Signed-off-by: AKP <tom@tdpain.net> * Move `export_completed` notification decision into `beforeSend` Signed-off-by: AKP <tom@tdpain.net> * Update server/emails/templates/ExportFailureEmail.tsx Co-authored-by: Tom Moor <tom.moor@gmail.com> * Update server/emails/templates/ExportSuccessEmail.tsx Co-authored-by: Tom Moor <tom.moor@gmail.com> Signed-off-by: AKP <tom@tdpain.net> Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
@@ -25,7 +25,9 @@ function CollectionExport({ collection, onSubmit }: Props) {
|
||||
|
||||
setIsLoading(false);
|
||||
showToast(
|
||||
t("Export started, you will receive an email when it’s complete.")
|
||||
t(
|
||||
"Export started. If you have notifications enabled, you will receive an email when it's complete."
|
||||
)
|
||||
);
|
||||
onSubmit();
|
||||
},
|
||||
|
||||
@@ -56,7 +56,7 @@ function Export() {
|
||||
<Heading>{t("Export")}</Heading>
|
||||
<Text type="secondary">
|
||||
<Trans
|
||||
defaults="A full export might take some time, consider exporting a single document or collection. The exported data is a zip of your documents in Markdown format. You may leave this page once the export has started – we will email a link to <em>{{ userEmail }}</em> when it’s complete."
|
||||
defaults="A full export might take some time, consider exporting a single document or collection. The exported data is a zip of your documents in Markdown format. You may leave this page once the export has started – if you have notifications enabled, we will email a link to <em>{{ userEmail }}</em> when it’s complete."
|
||||
values={{
|
||||
userEmail: user.email,
|
||||
}}
|
||||
|
||||
@@ -51,6 +51,13 @@ function Notifications() {
|
||||
"Receive a notification when someone you invited creates an account"
|
||||
),
|
||||
},
|
||||
{
|
||||
event: "emails.export_completed",
|
||||
title: t("Export completed"),
|
||||
description: t(
|
||||
"Receive a notification when an export you requested has been completed"
|
||||
),
|
||||
},
|
||||
{
|
||||
visible: isCloudHosted,
|
||||
event: "emails.onboarding",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { NotificationSetting } from "@server/models";
|
||||
import BaseEmail from "./BaseEmail";
|
||||
import Body from "./components/Body";
|
||||
import Button from "./components/Button";
|
||||
@@ -10,13 +11,27 @@ import Heading from "./components/Heading";
|
||||
|
||||
type Props = {
|
||||
to: string;
|
||||
userId: string;
|
||||
teamUrl: string;
|
||||
teamId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Email sent to a user when their data export has failed for some reason.
|
||||
*/
|
||||
export default class ExportFailureEmail extends BaseEmail<Props> {
|
||||
protected async beforeSend({ userId, teamId }: Props) {
|
||||
const notificationSetting = await NotificationSetting.findOne({
|
||||
where: {
|
||||
userId,
|
||||
teamId,
|
||||
event: "emails.export_completed",
|
||||
},
|
||||
});
|
||||
|
||||
return notificationSetting !== null;
|
||||
}
|
||||
|
||||
protected subject() {
|
||||
return "Your requested export";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { NotificationSetting } from "@server/models";
|
||||
import BaseEmail from "./BaseEmail";
|
||||
import Body from "./components/Body";
|
||||
import Button from "./components/Button";
|
||||
@@ -10,8 +11,10 @@ import Heading from "./components/Heading";
|
||||
|
||||
type Props = {
|
||||
to: string;
|
||||
userId: string;
|
||||
id: string;
|
||||
teamUrl: string;
|
||||
teamId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -19,6 +22,18 @@ type Props = {
|
||||
* for download in the settings section.
|
||||
*/
|
||||
export default class ExportSuccessEmail extends BaseEmail<Props> {
|
||||
protected async beforeSend({ userId, teamId }: Props) {
|
||||
const notificationSetting = await NotificationSetting.findOne({
|
||||
where: {
|
||||
userId,
|
||||
teamId,
|
||||
event: "emails.export_completed",
|
||||
},
|
||||
});
|
||||
|
||||
return notificationSetting !== null;
|
||||
}
|
||||
|
||||
protected subject() {
|
||||
return "Your requested export";
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ class NotificationSetting extends Model {
|
||||
"emails.invite_accepted",
|
||||
"emails.onboarding",
|
||||
"emails.features",
|
||||
"emails.export_completed",
|
||||
],
|
||||
])
|
||||
@Column(DataType.STRING)
|
||||
|
||||
@@ -497,7 +497,9 @@ class User extends ParanoidModel {
|
||||
};
|
||||
|
||||
// By default when a user signs up we subscribe them to email notifications
|
||||
// when documents they created are edited by other team members and onboarding
|
||||
// when documents they created are edited by other team members and onboarding.
|
||||
// If the user is an admin, they will also be subscribed to export_completed
|
||||
// notifications.
|
||||
@AfterCreate
|
||||
static subscribeToNotifications = async (
|
||||
model: User,
|
||||
@@ -537,6 +539,17 @@ class User extends ParanoidModel {
|
||||
transaction: options.transaction,
|
||||
}),
|
||||
]);
|
||||
|
||||
if (model.isAdmin) {
|
||||
await NotificationSetting.findOrCreate({
|
||||
where: {
|
||||
userId: model.id,
|
||||
teamId: model.teamId,
|
||||
event: "emails.export_completed",
|
||||
},
|
||||
transaction: options.transaction,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
static getCounts = async function (teamId: string) {
|
||||
|
||||
@@ -72,8 +72,10 @@ export default class ExportMarkdownZipTask extends BaseTask<Props> {
|
||||
|
||||
await ExportSuccessEmail.schedule({
|
||||
to: user.email,
|
||||
userId: user.id,
|
||||
id: fileOperation.id,
|
||||
teamUrl: team.url,
|
||||
teamId: team.id,
|
||||
});
|
||||
} catch (error) {
|
||||
await this.updateFileOperation(fileOperation, {
|
||||
@@ -82,7 +84,9 @@ export default class ExportMarkdownZipTask extends BaseTask<Props> {
|
||||
});
|
||||
await ExportFailureEmail.schedule({
|
||||
to: user.email,
|
||||
userId: user.id,
|
||||
teamUrl: team.url,
|
||||
teamId: team.id,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@
|
||||
"Sort": "Sort",
|
||||
"Saving": "Saving",
|
||||
"Save": "Save",
|
||||
"Export started, you will receive an email when it’s complete.": "Export started, you will receive an email when it’s complete.",
|
||||
"Export started. If you have notifications enabled, you will receive an email when it's complete.": "Export started. If you have notifications enabled, you will receive an email when it's complete.",
|
||||
"Exporting the collection <em>{{collectionName}}</em> may take a few seconds. Your documents will be a zip of folders with files in Markdown format. Please visit the Export section on settings to get the zip.": "Exporting the collection <em>{{collectionName}}</em> may take a few seconds. Your documents will be a zip of folders with files in Markdown format. Please visit the Export section on settings to get the zip.",
|
||||
"Exporting": "Exporting",
|
||||
"Export Collection": "Export Collection",
|
||||
@@ -620,7 +620,7 @@
|
||||
"This is the screen that team members will first see when they sign in.": "This is the screen that team members will first see when they sign in.",
|
||||
"Export in progress…": "Export in progress…",
|
||||
"Export deleted": "Export deleted",
|
||||
"A full export might take some time, consider exporting a single document or collection. The exported data is a zip of your documents in Markdown format. You may leave this page once the export has started – we will email a link to <em>{{ userEmail }}</em> when it’s complete.": "A full export might take some time, consider exporting a single document or collection. The exported data is a zip of your documents in Markdown format. You may leave this page once the export has started – we will email a link to <em>{{ userEmail }}</em> when it’s complete.",
|
||||
"A full export might take some time, consider exporting a single document or collection. The exported data is a zip of your documents in Markdown format. You may leave this page once the export has started – if you have notifications enabled, we will email a link to <em>{{ userEmail }}</em> when it’s complete.": "A full export might take some time, consider exporting a single document or collection. The exported data is a zip of your documents in Markdown format. You may leave this page once the export has started – if you have notifications enabled, we will email a link to <em>{{ userEmail }}</em> when it’s complete.",
|
||||
"Export Requested": "Export Requested",
|
||||
"Requesting Export": "Requesting Export",
|
||||
"Export Data": "Export Data",
|
||||
@@ -650,6 +650,8 @@
|
||||
"Receive a notification whenever a new collection is created": "Receive a notification whenever a new collection is created",
|
||||
"Invite accepted": "Invite accepted",
|
||||
"Receive a notification when someone you invited creates an account": "Receive a notification when someone you invited creates an account",
|
||||
"Export completed": "Export completed",
|
||||
"Receive a notification when an export you requested has been completed": "Receive a notification when an export you requested has been completed",
|
||||
"Getting started": "Getting started",
|
||||
"Tips on getting started with Outline’s features and functionality": "Tips on getting started with Outline’s features and functionality",
|
||||
"New features": "New features",
|
||||
|
||||
Reference in New Issue
Block a user