Implement exporting from a specific start date and fix few bugs

This commit is contained in:
Jaiwanth 2021-06-11 12:04:05 +05:30
parent 80e5d4cd77
commit a1b614f2b3
6 changed files with 52 additions and 41 deletions

View file

@ -432,7 +432,7 @@ export default class MImageBody extends React.Component {
// Overidden by MStickerBody // Overidden by MStickerBody
wrapImage(contentUrl, children) { wrapImage(contentUrl, children) {
return <a href={contentUrl} onClick={this.onClick}> return <a href={contentUrl} target={this.props.forExport ? "__blank" : undefined} onClick={this.onClick}>
{children} {children}
</a>; </a>;
} }

View file

@ -82,7 +82,12 @@ export default class RoomHeader extends React.Component {
_exportConversationalHistory = async () => { _exportConversationalHistory = async () => {
await exportConversationalHistory(this.props.room, exportFormats.HTML, exportTypes.LAST_N_MESSAGES, 100); await exportConversationalHistory(
this.props.room,
exportFormats.HTML,
exportTypes.START_DATE,
{ startDate: parseInt(new Date("2021.05.20").getTime().toFixed(0)) },
);
} }
render() { render() {

View file

@ -1,37 +1,15 @@
import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixClientPeg } from "../../MatrixClientPeg"; import { MatrixClientPeg } from "../../MatrixClientPeg";
import { TimelineWindow } from "matrix-js-sdk/src/timeline-window";
import { arrayFastClone } from "../arrays";
import { exportTypes } from "./exportUtils"; import { exportTypes } from "./exportUtils";
import { exportOptions } from "./exportUtils";
export default abstract class Exporter { export default abstract class Exporter {
protected constructor(protected room: Room, protected exportType: exportTypes, protected numberOfEvents?: number) {} protected constructor(
protected room: Room,
protected getTimelineConversation = (): MatrixEvent[] => { protected exportType: exportTypes,
if (!this.room) return; protected exportOptions?: exportOptions,
) {}
const cli = MatrixClientPeg.get();
const timelineSet = this.room.getUnfilteredTimelineSet();
const timelineWindow = new TimelineWindow(
cli, timelineSet,
{windowLimit: Number.MAX_VALUE});
timelineWindow.load(null, 30);
const events: MatrixEvent[] = timelineWindow.getEvents();
// Clone and reverse the events so that we preserve the order
arrayFastClone(events)
.reverse()
.forEach(async (event) => {
await cli.decryptEventIfNeeded(event);
});
return events;
};
protected setEventMetadata = (event: MatrixEvent) => { protected setEventMetadata = (event: MatrixEvent) => {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
@ -47,12 +25,27 @@ export default abstract class Exporter {
return event; return event;
} }
protected getLimit = () => {
let limit: number;
switch (this.exportType) {
case exportTypes.LAST_N_MESSAGES:
limit = this.exportOptions.numberOfMessages;
break;
case exportTypes.TIMELINE:
limit = 40;
break;
default:
limit = Number.MAX_VALUE;
}
return limit;
}
protected getRequiredEvents = async () : Promise<MatrixEvent[]> => { protected getRequiredEvents = async () : Promise<MatrixEvent[]> => {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const eventMapper = client.getEventMapper(); const eventMapper = client.getEventMapper();
let prevToken: string|null = null; let prevToken: string|null = null;
let limit = this.numberOfEvents || Number.MAX_VALUE; let limit = this.getLimit();
let events: MatrixEvent[] = []; let events: MatrixEvent[] = [];
while (limit) { while (limit) {
@ -65,7 +58,14 @@ export default abstract class Exporter {
const matrixEvents: MatrixEvent[] = res.chunk.map(eventMapper); const matrixEvents: MatrixEvent[] = res.chunk.map(eventMapper);
matrixEvents.forEach(mxEv => events.push(mxEv)); for (const mxEv of matrixEvents) {
if (this.exportOptions.startDate && mxEv.getTs() < this.exportOptions.startDate) {
// Once the last message received is older than the start date, we break out of both the loops
limit = 0;
break;
}
events.push(mxEv);
}
prevToken = res.end; prevToken = res.end;
} }

View file

@ -23,6 +23,7 @@ import exportCSS from "./exportCSS";
import exportJS from "./exportJS"; import exportJS from "./exportJS";
import exportIcons from "./exportIcons"; import exportIcons from "./exportIcons";
import { exportTypes } from "./exportUtils"; import { exportTypes } from "./exportUtils";
import { exportOptions } from "./exportUtils";
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import { MatrixClient } from "matrix-js-sdk"; import { MatrixClient } from "matrix-js-sdk";
@ -32,8 +33,8 @@ export default class HTMLExporter extends Exporter {
protected permalinkCreator: RoomPermalinkCreator; protected permalinkCreator: RoomPermalinkCreator;
protected matrixClient: MatrixClient; protected matrixClient: MatrixClient;
constructor(room: Room, exportType: exportTypes, numberOfEvents?: number) { constructor(room: Room, exportType: exportTypes, exportOptions: exportOptions) {
super(room, exportType, numberOfEvents); super(room, exportType, exportOptions);
this.zip = new JSZip(); this.zip = new JSZip();
this.avatars = new Map<string, boolean>(); this.avatars = new Map<string, boolean>();
this.matrixClient = MatrixClientPeg.get(); this.matrixClient = MatrixClientPeg.get();
@ -282,7 +283,7 @@ export default class HTMLExporter extends Exporter {
protected async getEventTile(mxEv: MatrixEvent, continuation: boolean, filePath?: string) { protected async getEventTile(mxEv: MatrixEvent, continuation: boolean, filePath?: string) {
const hasAvatar = this.hasAvatar(mxEv); const hasAvatar = this.hasAvatar(mxEv);
if (hasAvatar) await this.saveAvatarIfNeeded(mxEv); if (hasAvatar) await this.saveAvatarIfNeeded(mxEv);
const eventTile = <li className="mx_Export_EventWrapper" id={mxEv.getId()}> const eventTile = <div className="mx_Export_EventWrapper" id={mxEv.getId()}>
<MatrixClientContext.Provider value = {this.matrixClient}> <MatrixClientContext.Provider value = {this.matrixClient}>
<EventTile <EventTile
mxEvent={mxEv} mxEvent={mxEv}
@ -307,11 +308,11 @@ export default class HTMLExporter extends Exporter {
showReadReceipts={false} showReadReceipts={false}
/> />
</MatrixClientContext.Provider> </MatrixClientContext.Provider>
</li> </div>
let eventTileMarkup = renderToStaticMarkup(eventTile); let eventTileMarkup = renderToStaticMarkup(eventTile);
if (filePath) eventTileMarkup = eventTileMarkup.replace(/(src=|href=)"forExport"/, `$1"${filePath}"`); if (filePath) eventTileMarkup = eventTileMarkup.replace(/(src=|href=)"forExport"/g, `$1"${filePath}"`);
if (hasAvatar) { if (hasAvatar) {
eventTileMarkup = eventTileMarkup.replace(/src="AvatarForExport"/, `src="users/${mxEv.sender.userId}"`); eventTileMarkup = eventTileMarkup.replace(/src="AvatarForExport"/g, `src="users/${mxEv.sender.userId}"`);
} }
return eventTileMarkup; return eventTileMarkup;
} }

View file

@ -19537,7 +19537,7 @@ a.mx_reply_anchor:hover{
} }
li.mx_Export_EventWrapper:target { .mx_Export_EventWrapper:target {
background: white; background: white;
animation: mx_event_highlight_animation 2s linear; animation: mx_event_highlight_animation 2s linear;
} }

View file

@ -14,15 +14,20 @@ export enum exportTypes {
LAST_N_MESSAGES = "LAST_N_MESSAGES", LAST_N_MESSAGES = "LAST_N_MESSAGES",
} }
export interface exportOptions {
startDate?: number;
numberOfMessages?: number;
}
const exportConversationalHistory = async ( const exportConversationalHistory = async (
room: Room, room: Room,
format: string, format: string,
exportType: exportTypes, exportType: exportTypes,
exportTypeMetadata?: number, exportOptions?: exportOptions,
) => { ) => {
switch (format) { switch (format) {
case exportFormats.HTML: case exportFormats.HTML:
await new HTMLExporter(room, exportType, exportTypeMetadata).export(); await new HTMLExporter(room, exportType, exportOptions).export();
break; break;
case exportFormats.JSON: case exportFormats.JSON:
break; break;