Include a file-safe room name and ISO date in chat exports (#9440)
* conversation export named after room * sanitization added for exported file name * sanitization added for exported file name * sanitization added for exported file name * sanitization added for exported file name=>lint error fixed * sanitization added for exported file name=>lint error fixed * sanitization added for exported file name=>redundancy removed * sanitization added for exported file name=>redundancy removed * reverted to previous commit * sanitization added for exported file name=>redundancy removed * exported chat date iso formatted * conversation export named after room * conversation export named after room Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> * code refacto filename date format * Add docs to fn * Bring in a util library for sanitizing * Extract file naming function and make consistent for all 3 types Also use the library we dragged in * Write tests & associated fixes * Apply linters locally * Include new date util in index Co-authored-by: Sinharitik589 <sinharitik18112835@gmail.com> Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> Co-authored-by: yaya-usman <yayaazeez222@gmail.com> Co-authored-by: Sinharitik589 <67551927+Sinharitik589@users.noreply.github.com>
This commit is contained in:
parent
ca8b1b04ef
commit
10a429c68d
16 changed files with 233 additions and 90 deletions
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2022 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.
|
||||
|
@ -177,6 +178,16 @@ export function formatFullDateNoDay(date: Date) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ISO date string without textual description of the date (ie: no "Wednesday" or
|
||||
* similar)
|
||||
* @param date The date to format.
|
||||
* @returns The date string in ISO format.
|
||||
*/
|
||||
export function formatFullDateNoDayISO(date: Date): string {
|
||||
return date.toISOString();
|
||||
}
|
||||
|
||||
export function formatFullDateNoDayNoTime(date: Date) {
|
||||
return (
|
||||
date.getFullYear() +
|
||||
|
|
|
@ -756,6 +756,7 @@
|
|||
"Zoom in": "Zoom in",
|
||||
"Zoom out": "Zoom out",
|
||||
"Are you sure you want to exit during this export?": "Are you sure you want to exit during this export?",
|
||||
"Unnamed Room": "Unnamed Room",
|
||||
"Generating a ZIP": "Generating a ZIP",
|
||||
"Fetched %(count)s events out of %(total)s|other": "Fetched %(count)s events out of %(total)s",
|
||||
"Fetched %(count)s events out of %(total)s|one": "Fetched %(count)s event out of %(total)s",
|
||||
|
@ -2768,7 +2769,6 @@
|
|||
"Or send invite link": "Or send invite link",
|
||||
"Unnamed Space": "Unnamed Space",
|
||||
"Invite to %(roomName)s": "Invite to %(roomName)s",
|
||||
"Unnamed Room": "Unnamed Room",
|
||||
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.",
|
||||
"Invite someone using their name, username (like <userId/>) or <a>share this space</a>.": "Invite someone using their name, username (like <userId/>) or <a>share this space</a>.",
|
||||
"Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.": "Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2021 - 2022 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.
|
||||
|
@ -20,12 +20,13 @@ import { MatrixClient } from "matrix-js-sdk/src/client";
|
|||
import { Direction } from "matrix-js-sdk/src/models/event-timeline";
|
||||
import { saveAs } from "file-saver";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import sanitizeFilename from "sanitize-filename";
|
||||
|
||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||
import { ExportType, IExportOptions } from "./exportUtils";
|
||||
import { decryptFile } from "../DecryptFile";
|
||||
import { mediaFromContent } from "../../customisations/Media";
|
||||
import { formatFullDateNoDay } from "../../DateUtils";
|
||||
import { formatFullDateNoDay, formatFullDateNoDayISO } from "../../DateUtils";
|
||||
import { isVoiceMessage } from "../EventUtils";
|
||||
import { IMediaEventContent } from "../../customisations/models/IMediaEventContent";
|
||||
import { _t } from "../../languageHandler";
|
||||
|
@ -57,6 +58,10 @@ export default abstract class Exporter {
|
|||
window.addEventListener("beforeunload", this.onBeforeUnload);
|
||||
}
|
||||
|
||||
public get destinationFileName(): string {
|
||||
return this.makeFileNameNoExtension(SdkConfig.get().brand) + ".zip";
|
||||
}
|
||||
|
||||
protected onBeforeUnload(e: BeforeUnloadEvent): string {
|
||||
e.preventDefault();
|
||||
return e.returnValue = _t("Are you sure you want to exit during this export?");
|
||||
|
@ -75,10 +80,19 @@ export default abstract class Exporter {
|
|||
this.files.push(file);
|
||||
}
|
||||
|
||||
protected makeFileNameNoExtension(brand = "matrix"): string {
|
||||
// First try to use the real name of the room, then a translated copy of a generic name,
|
||||
// then finally hardcoded default to guarantee we'll have a name.
|
||||
const safeRoomName = sanitizeFilename(this.room.name ?? _t("Unnamed Room")).trim() || "Unnamed Room";
|
||||
const safeDate = formatFullDateNoDayISO(new Date())
|
||||
.replace(/:/g, '-'); // ISO format automatically removes a lot of stuff for us
|
||||
const safeBrand = sanitizeFilename(brand);
|
||||
return `${safeBrand} - ${safeRoomName} - Chat Export - ${safeDate}`;
|
||||
}
|
||||
|
||||
protected async downloadZIP(): Promise<string | void> {
|
||||
const brand = SdkConfig.get().brand;
|
||||
const filenameWithoutExt = `${brand} - Chat Export - ${formatFullDateNoDay(new Date())}`;
|
||||
const filename = `${filenameWithoutExt}.zip`;
|
||||
const filename = this.destinationFileName;
|
||||
const filenameWithoutExt = filename.substring(0, filename.length - 4); // take off the .zip
|
||||
const { default: JSZip } = await import('jszip');
|
||||
|
||||
const zip = new JSZip();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2021 - 2022 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.
|
||||
|
@ -20,7 +20,7 @@ import { EventType } from "matrix-js-sdk/src/@types/event";
|
|||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import Exporter from "./Exporter";
|
||||
import { formatFullDateNoDay, formatFullDateNoDayNoTime } from "../../DateUtils";
|
||||
import { formatFullDateNoDayNoTime } from "../../DateUtils";
|
||||
import { ExportType, IExportOptions } from "./exportUtils";
|
||||
import { _t } from "../../languageHandler";
|
||||
import { haveRendererForEvent } from "../../events/EventTileFactory";
|
||||
|
@ -38,6 +38,10 @@ export default class JSONExporter extends Exporter {
|
|||
super(room, exportType, exportOptions, setProgressText);
|
||||
}
|
||||
|
||||
public get destinationFileName(): string {
|
||||
return this.makeFileNameNoExtension() + ".json";
|
||||
}
|
||||
|
||||
protected createJSONString(): string {
|
||||
const exportDate = formatFullDateNoDayNoTime(new Date());
|
||||
const creator = this.room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
|
||||
|
@ -108,7 +112,7 @@ export default class JSONExporter extends Exporter {
|
|||
this.addFile("export.json", new Blob([text]));
|
||||
await this.downloadZIP();
|
||||
} else {
|
||||
const fileName = `matrix-export-${formatFullDateNoDay(new Date())}.json`;
|
||||
const fileName = this.destinationFileName;
|
||||
this.downloadPlainText(fileName, text);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2021 - 2022 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.
|
||||
|
@ -20,7 +20,6 @@ import { logger } from "matrix-js-sdk/src/logger";
|
|||
import React from "react";
|
||||
|
||||
import Exporter from "./Exporter";
|
||||
import { formatFullDateNoDay } from "../../DateUtils";
|
||||
import { _t } from "../../languageHandler";
|
||||
import { ExportType, IExportOptions } from "./exportUtils";
|
||||
import { textForEvent } from "../../TextForEvent";
|
||||
|
@ -43,6 +42,10 @@ export default class PlainTextExporter extends Exporter {
|
|||
: _t("Media omitted - file size limit exceeded");
|
||||
}
|
||||
|
||||
public get destinationFileName(): string {
|
||||
return this.makeFileNameNoExtension() + ".txt";
|
||||
}
|
||||
|
||||
public textForReplyEvent = (content: IContent) => {
|
||||
const REPLY_REGEX = /> <(.*?)>(.*?)\n\n(.*)/s;
|
||||
const REPLY_SOURCE_MAX_LENGTH = 32;
|
||||
|
@ -137,7 +140,7 @@ export default class PlainTextExporter extends Exporter {
|
|||
this.addFile("export.txt", new Blob([text]));
|
||||
await this.downloadZIP();
|
||||
} else {
|
||||
const fileName = `matrix-export-${formatFullDateNoDay(new Date())}.txt`;
|
||||
const fileName = this.destinationFileName;
|
||||
this.downloadPlainText(fileName, text);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue