diff --git a/res/css/views/dialogs/_ExportDialog.scss b/res/css/views/dialogs/_ExportDialog.scss index f3e418354c..4727ab5f31 100644 --- a/res/css/views/dialogs/_ExportDialog.scss +++ b/res/css/views/dialogs/_ExportDialog.scss @@ -20,7 +20,7 @@ limitations under the License. display: block; font-family: $font-family; font-weight: $font-semi-bold; - color: $primary-fg-color; + color: $accent-fg-color; margin-top: 18px; margin-bottom: 12px; } diff --git a/src/async-components/views/dialogs/ExportDialog.tsx b/src/components/views/dialogs/ExportDialog.tsx similarity index 89% rename from src/async-components/views/dialogs/ExportDialog.tsx rename to src/components/views/dialogs/ExportDialog.tsx index 40c9db0a67..33a14887fb 100644 --- a/src/async-components/views/dialogs/ExportDialog.tsx +++ b/src/components/views/dialogs/ExportDialog.tsx @@ -17,27 +17,26 @@ limitations under the License. import React, { useRef, useState } from "react"; import { Room } from "matrix-js-sdk/src"; import { _t } from "../../../languageHandler"; -import { IDialogProps } from "../../../components/views/dialogs/IDialogProps"; -import BaseDialog from "../../../components/views/dialogs/BaseDialog"; -import DialogButtons from "../../../components/views/elements/DialogButtons"; -import Field from "../../../components/views/elements/Field"; -import StyledRadioGroup from "../../../components/views/elements/StyledRadioGroup"; -import StyledCheckbox from "../../../components/views/elements/StyledCheckbox"; +import { IDialogProps } from "./IDialogProps"; +import BaseDialog from "./BaseDialog"; +import DialogButtons from "../elements/DialogButtons"; +import Field from "../elements/Field"; +import StyledRadioGroup from "../elements/StyledRadioGroup"; +import StyledCheckbox from "../elements/StyledCheckbox"; import { ExportFormat, ExportType, textForFormat, textForType, } from "../../../utils/exportUtils/exportUtils"; -import withValidation, { IFieldState, IValidationResult } from "../../../components/views/elements/Validation"; +import withValidation, { IFieldState, IValidationResult } from "../elements/Validation"; import HTMLExporter from "../../../utils/exportUtils/HtmlExport"; import JSONExporter from "../../../utils/exportUtils/JSONExport"; import PlainTextExporter from "../../../utils/exportUtils/PlainTextExport"; import { useStateCallback } from "../../../hooks/useStateCallback"; import Exporter from "../../../utils/exportUtils/Exporter"; -import Spinner from "../../../components/views/elements/Spinner"; -import Modal from "../../../Modal"; -import InfoDialog from "../../../components/views/dialogs/InfoDialog"; +import Spinner from "../elements/Spinner"; +import InfoDialog from "./InfoDialog"; interface IProps extends IDialogProps { room: Room; @@ -215,11 +214,10 @@ const ExportDialog: React.FC = ({ room, onFinished }) => { }; const confirmCanel = async () => { - await exporter?.cancelExport().then(() => { - setExportCancelled(true); - setExporting(false); - setExporter(null); - }); + await exporter?.cancelExport(); + setExportCancelled(true); + setExporting(false); + setExporter(null); }; const exportFormatOptions = Object.keys(ExportFormat).map((format) => ({ @@ -256,26 +254,26 @@ const ExportDialog: React.FC = ({ room, onFinished }) => { if (exportCancelled) { // Display successful cancellation message - Modal.createTrackedDialog("Export Cancelled", "", InfoDialog, { - title: _t("Export Cancelled"), - description:

{ _t("The export was cancelled successfully") }

, - hasCloseButton: true, - onFinished: () => { - setExportCancelled(false); - }, - }); - return null; + return ( + + ); } else if (exportSuccessful) { // Display successful export message - Modal.createTrackedDialog("Export Successful", "", InfoDialog, { - title: _t("Export Successful"), - description:

{ _t("Your messages were successfully exported") }

, - hasCloseButton: true, - onFinished: () => { - setExportSuccessful(false); - }, - }); - return null; + return ( + + ); } else if (displayCancel) { // Display cancel warning return ( diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index db642ead3d..1fe556dde2 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -47,6 +47,7 @@ import { useRoomMemberCount } from "../../../hooks/useRoomMembers"; import { Container, MAX_PINNED, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore"; import RoomName from "../elements/RoomName"; import UIStore from "../../../stores/UIStore"; +import ExportDialog from "../dialogs/ExportDialog"; interface IProps { room: Room; @@ -241,7 +242,6 @@ const RoomSummaryCard: React.FC = ({ room, onClose }) => { }; const onRoomExportClick = async () => { - const { default: ExportDialog } = await import("../../../async-components/views/dialogs/ExportDialog"); Modal.createTrackedDialog('export room dialog', '', ExportDialog, { room, }); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 9e6c90ec72..e80aabdd61 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -3061,7 +3061,7 @@ "Export Cancelled": "Export Cancelled", "The export was cancelled successfully": "The export was cancelled successfully", "Export Successful": "Export Successful", - "Your messages were successfully exported": "Your messages were successfully exported", + "Your export was successful. Find it in your Downloads folder.": "Your export was successful. Find it in your Downloads folder.", "Are you sure you want to stop exporting your data? If you do, you'll need to start over.": "Are you sure you want to stop exporting your data? If you do, you'll need to start over.", "Stop": "Stop", "Exporting your data": "Exporting your data", diff --git a/src/utils/exportUtils/Exporter.ts b/src/utils/exportUtils/Exporter.ts index 7254bdc3a4..becc36449d 100644 --- a/src/utils/exportUtils/Exporter.ts +++ b/src/utils/exportUtils/Exporter.ts @@ -22,9 +22,9 @@ import { decryptFile } from "../DecryptFile"; import { mediaFromContent } from "../../customisations/Media"; import { formatFullDateNoDay } from "../../DateUtils"; import { Direction, MatrixClient } from "matrix-js-sdk"; -import JSZip from "jszip"; import { saveAs } from "file-saver"; import { _t } from "../../languageHandler"; +import SdkConfig from "../../SdkConfig"; type BlobFile = { name: string; @@ -71,7 +71,9 @@ export default abstract class Exporter { } protected async downloadZIP(): Promise { - const filename = `matrix-export-${formatFullDateNoDay(new Date())}.zip`; + const brand = SdkConfig.get().brand; + const filename = `${brand} - Chat Export -${formatFullDateNoDay(new Date())}.zip`; + const { default: JSZip } = await import('jszip'); const zip = new JSZip(); // Create a writable stream to the directory diff --git a/src/utils/exportUtils/exportCSS.ts b/src/utils/exportUtils/exportCSS.ts index 46024590a7..3bad94d938 100644 --- a/src/utils/exportUtils/exportCSS.ts +++ b/src/utils/exportUtils/exportCSS.ts @@ -16,13 +16,12 @@ limitations under the License. /* eslint-disable max-len, camelcase */ -import ThemeWatcher from "../../settings/watchers/ThemeWatcher"; +import customCSS from "!!raw-loader!./exportCustomCSS.css"; const getExportCSS = async (): Promise => { - const theme = new ThemeWatcher().getEffectiveTheme(); const stylesheets: string[] = []; document.querySelectorAll('link[rel="stylesheet"]').forEach((e: any) => { - if (e.href.endsWith("bundle.css") || e.href.endsWith(`theme-${theme}.css`)) { + if (e.href.endsWith("bundle.css") || e.href.endsWith("theme-light.css")) { stylesheets.push(e.href); } }); @@ -45,100 +44,6 @@ const getExportCSS = async (): Promise => { "font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace", ); - const customCSS = ` -#snackbar { - display: flex; - visibility: hidden; - min-width: 250px; - margin-left: -125px; - background-color: #333; - color: #fff; - text-align: center; - position: fixed; - z-index: 1; - left: 50%; - bottom: 30px; - font-size: 17px; - padding: 6px 16px; - font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Ubuntu, roboto, noto, arial, sans-serif; - font-weight: 400; - line-height: 1.43; - border-radius: 4px; - letter-spacing: 0.01071em; - } - - #snackbar.mx_show { - visibility: visible; - -webkit-animation: mx_snackbar_fadein 0.5s, mx_snackbar_fadeout 0.5s 2.5s; - animation: mx_snackbar_fadein 0.5s, mx_snackbar_fadeout 0.5s 2.5s; - } - - a.mx_reply_anchor{ - cursor: pointer; - color: #238cf5; - } - - a.mx_reply_anchor:hover{ - text-decoration: underline; - } - - @-webkit-keyframes mx_snackbar_fadein { - from {bottom: 0; opacity: 0;} - to {bottom: 30px; opacity: 1;} - } - - @keyframes mx_snackbar_fadein { - from {bottom: 0; opacity: 0;} - to {bottom: 30px; opacity: 1;} - } - - @-webkit-keyframes mx_snackbar_fadeout { - from {bottom: 30px; opacity: 1;} - to {bottom: 0; opacity: 0;} - } - - @keyframes mx_snackbar_fadeout { - from {bottom: 30px; opacity: 1;} - to {bottom: 0; opacity: 0;} - } - - * { - scroll-behavior: smooth !important; - } - - .mx_Export_EventWrapper:target { - background: ${theme == 'light' ? "white" : "#15191E"}; - animation: mx_event_highlight_animation 2s linear; - } - - @keyframes mx_event_highlight_animation { - 0%,100% { - background: ${theme == 'light' ? "white" : "#15191E"}; - } - 50% { - background: ${theme == 'light' ? "#e3e2df" : "#21262c"}; - } - } - - .mx_ReplyThread_Export { - margin-top: 0px; - margin-bottom: 5px; - } - - .mx_RedactedBody { - padding-left: unset; - } - - img { - white-space: nowrap; - overflow: hidden; - } - - .mx_MatrixChat{ - max_width: 100%; - } -`; - return CSS + customCSS; }; diff --git a/src/utils/exportUtils/exportCustomCSS.css b/src/utils/exportUtils/exportCustomCSS.css new file mode 100644 index 0000000000..284f54ac08 --- /dev/null +++ b/src/utils/exportUtils/exportCustomCSS.css @@ -0,0 +1,138 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + This file is raw-imported (imported as plain text) for the export bundle, which is the reason for the .css format and the colours being hard-coded hard-coded. +*/ + +#snackbar { + display: flex; + visibility: hidden; + min-width: 250px; + margin-left: -125px; + background-color: #333; + color: #fff; + text-align: center; + position: fixed; + z-index: 1; + left: 50%; + bottom: 30px; + font-size: 17px; + padding: 6px 16px; + font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, + segoe ui, helvetica neue, helvetica, Ubuntu, roboto, noto, arial, + sans-serif; + font-weight: 400; + line-height: 1.43; + border-radius: 4px; + letter-spacing: 0.01071em; +} + +#snackbar.mx_show { + visibility: visible; + -webkit-animation: mx_snackbar_fadein 0.5s, mx_snackbar_fadeout 0.5s 2.5s; + animation: mx_snackbar_fadein 0.5s, mx_snackbar_fadeout 0.5s 2.5s; +} + +a.mx_reply_anchor { + cursor: pointer; + color: #238cf5; +} + +a.mx_reply_anchor:hover { + text-decoration: underline; +} + +@-webkit-keyframes mx_snackbar_fadein { + from { + bottom: 0; + opacity: 0; + } + to { + bottom: 30px; + opacity: 1; + } +} + +@keyframes mx_snackbar_fadein { + from { + bottom: 0; + opacity: 0; + } + to { + bottom: 30px; + opacity: 1; + } +} + +@-webkit-keyframes mx_snackbar_fadeout { + from { + bottom: 30px; + opacity: 1; + } + to { + bottom: 0; + opacity: 0; + } +} + +@keyframes mx_snackbar_fadeout { + from { + bottom: 30px; + opacity: 1; + } + to { + bottom: 0; + opacity: 0; + } +} + +* { + scroll-behavior: smooth !important; +} + +.mx_Export_EventWrapper:target { + background: white; + animation: mx_event_highlight_animation 2s linear; +} + +@keyframes mx_event_highlight_animation { + 0%, + 100% { + background: white; + } + 50% { + background: #e3e2df; + } +} + +.mx_ReplyThread_Export { + margin-top: 0px; + margin-bottom: 5px; +} + +.mx_RedactedBody { + padding-left: unset; +} + +img { + white-space: nowrap; + overflow: hidden; +} + +.mx_MatrixChat { + max-width: 100%; +}