From 9e6b8ff9f5b0d2d7f6be4fd2ab1103ab0c934718 Mon Sep 17 00:00:00 2001 From: Jaiwanth Date: Thu, 17 Jun 2021 10:46:08 +0530 Subject: [PATCH] Start implementation of plain text export --- src/TextForEvent.ts | 4 +- src/components/views/rooms/RoomHeader.js | 2 +- src/utils/exportUtils/Exporter.ts | 2 +- src/utils/exportUtils/PlainTextExport.ts | 68 ++++++++++++++++++++++++ src/utils/exportUtils/exportUtils.ts | 2 + 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 src/utils/exportUtils/PlainTextExport.ts diff --git a/src/TextForEvent.ts b/src/TextForEvent.ts index 649c53664e..df2047a645 100644 --- a/src/TextForEvent.ts +++ b/src/TextForEvent.ts @@ -238,12 +238,12 @@ function textForServerACLEvent(ev): () => string | null { function textForMessageEvent(ev): () => string | null { return () => { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); - let message = senderDisplayName + ': ' + ev.getContent().body; + let message = ev.getContent().body; if (ev.getContent().msgtype === "m.emote") { message = "* " + senderDisplayName + " " + message; } else if (ev.getContent().msgtype === "m.image") { message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName}); - } + } else message = senderDisplayName + ': ' + message; return message; }; } diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index f0f9b4d646..1a21e503f3 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -84,7 +84,7 @@ export default class RoomHeader extends React.Component { _exportConversationalHistory = async () => { await exportConversationalHistory( this.props.room, - exportFormats.HTML, + exportFormats.LOGS, exportTypes.START_DATE, { startDate: parseInt(new Date("2021.05.20").getTime().toFixed(0)), diff --git a/src/utils/exportUtils/Exporter.ts b/src/utils/exportUtils/Exporter.ts index 8aeda1f3c5..44bb7b6a88 100644 --- a/src/utils/exportUtils/Exporter.ts +++ b/src/utils/exportUtils/Exporter.ts @@ -146,5 +146,5 @@ export default abstract class Exporter { return mxEv.getType() === attachmentTypes[0] || attachmentTypes.includes(mxEv.getContent().msgtype); } - abstract export(): Promise; + abstract export(): Promise; } diff --git a/src/utils/exportUtils/PlainTextExport.ts b/src/utils/exportUtils/PlainTextExport.ts new file mode 100644 index 0000000000..6d29a4bb6d --- /dev/null +++ b/src/utils/exportUtils/PlainTextExport.ts @@ -0,0 +1,68 @@ +import streamSaver from "streamsaver"; +import Exporter from "./Exporter"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { formatFullDateNoDay } from "../../DateUtils"; +import { _t } from "../../languageHandler"; +import * as ponyfill from "web-streams-polyfill/ponyfill" +import { haveTileForEvent } from "../../components/views/rooms/EventTile"; +import { exportTypes } from "./exportUtils"; +import { exportOptions } from "./exportUtils"; +import { textForEvent } from "../../TextForEvent"; + +export default class PlainTextExporter extends Exporter { + protected totalSize: number; + protected mediaOmitText: string; + + constructor(room: Room, exportType: exportTypes, exportOptions: exportOptions) { + super(room, exportType, exportOptions); + this.totalSize = 0; + this.mediaOmitText = !this.exportOptions.attachmentsIncluded + ? _t("Media omitted") + : _t("Media omitted - file size limit exceeded"); + window.addEventListener("beforeunload", this.onBeforeUnload) + } + + protected onBeforeUnload = (e: BeforeUnloadEvent) => { + e.preventDefault(); + return e.returnValue = "Are you sure you want to exit during this export?"; + } + + protected async createOutput(events: MatrixEvent[]) { + let content = ""; + for (const event of events) { + if (!haveTileForEvent(event)) continue; + content += `${new Date(event.getTs()).toLocaleString()} - ${textForEvent(event)}\n`; + } + return content; + } + + public async export() { + console.info("Starting export process..."); + console.info("Fetching events..."); + const fetchStart = performance.now(); + const res = await this.getRequiredEvents(); + const fetchEnd = performance.now(); + + console.log(`Fetched ${res.length} events in ${(fetchEnd - fetchStart)/1000} s`); + console.info("Creating Output..."); + + const text = await this.createOutput(res); + + const filename = `matrix-export-${formatFullDateNoDay(new Date())}.txt`; + + console.info("Writing to a file..."); + //Support for firefox browser + streamSaver.WritableStream = ponyfill.WritableStream + //Create a writable stream to the directory + const fileStream = streamSaver.createWriteStream(filename); + const writer = fileStream.getWriter(); + const data = new TextEncoder().encode(text); + await writer.write(data); + await writer.close(); + const exportEnd = performance.now(); + console.info(`Export Successful! Exported ${res.length} events in ${(exportEnd - fetchStart)/1000} seconds`); + window.removeEventListener("beforeunload", this.onBeforeUnload); + } +} + diff --git a/src/utils/exportUtils/exportUtils.ts b/src/utils/exportUtils/exportUtils.ts index 77fb23509e..22457f926b 100644 --- a/src/utils/exportUtils/exportUtils.ts +++ b/src/utils/exportUtils/exportUtils.ts @@ -1,5 +1,6 @@ import { Room } from "matrix-js-sdk/src/models/room"; import HTMLExporter from "./HtmlExport"; +import PlainTextExporter from "./PlainTextExport"; export enum exportFormats { HTML = "HTML", @@ -34,6 +35,7 @@ const exportConversationalHistory = async ( case exportFormats.JSON: break; case exportFormats.LOGS: + await new PlainTextExporter(room, exportType, exportOptions).export(); break; } };