|
|
@ -1,49 +1,50 @@
|
|
|
|
'use strict';
|
|
|
|
"use strict";
|
|
|
|
const React = require('react');
|
|
|
|
|
|
|
|
const create = require('create-react-class');
|
|
|
|
|
|
|
|
const sanitize = require('sanitize-html');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Event = require('./events/Event.js');
|
|
|
|
const Promise = require("bluebird");
|
|
|
|
const Info = require('./info.js');
|
|
|
|
const React = require("react");
|
|
|
|
const Input = require('./input.js');
|
|
|
|
const create = require("create-react-class");
|
|
|
|
const User = require('./events/user.js');
|
|
|
|
const sanitize = require("sanitize-html");
|
|
|
|
const Loading = require('./loading.js');
|
|
|
|
|
|
|
|
|
|
|
|
const Event = require("./events/Event.js");
|
|
|
|
|
|
|
|
const Info = require("./info.js");
|
|
|
|
|
|
|
|
const Input = require("./input.js");
|
|
|
|
|
|
|
|
const User = require("./events/user.js");
|
|
|
|
|
|
|
|
const Loading = require("./loading.js");
|
|
|
|
|
|
|
|
|
|
|
|
const generateJdenticon = require("../lib/generate-jdenticon");
|
|
|
|
const generateJdenticon = require("../lib/generate-jdenticon");
|
|
|
|
|
|
|
|
const groupEvents = require("../lib/group-events");
|
|
|
|
|
|
|
|
|
|
|
|
let eventFunctions = {
|
|
|
|
let eventFunctions = {
|
|
|
|
plaintext: function() {
|
|
|
|
plaintext: function() {
|
|
|
|
let plain = "unknown event";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.type == "m.room.message") {
|
|
|
|
if (this.type == "m.room.message") {
|
|
|
|
plain = this.content.body;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.content.format == "org.matrix.custom.html") {
|
|
|
|
if (this.content.format == "org.matrix.custom.html") {
|
|
|
|
plain = sanitize(this.content.formatted_body, {allowedTags: []});
|
|
|
|
return sanitize(this.content.formatted_body, {allowedTags: []});
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return this.content.body;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (this.type == "m.room.member") {
|
|
|
|
if (this.type == "m.room.member") {
|
|
|
|
|
|
|
|
if (this.content.membership == "invite") {
|
|
|
|
if (this.content.membership == "invite") {
|
|
|
|
plain = `${this.sender} invited ${this.state_key}`;
|
|
|
|
return `${this.sender} invited ${this.state_key}`;
|
|
|
|
} else if (this.content.membership == "join") {
|
|
|
|
} else if (this.content.membership == "join") {
|
|
|
|
plain = `${this.state_key} joined the room`;
|
|
|
|
return `${this.state_key} joined the room`;
|
|
|
|
} else if (this.content.membership == "leave") {
|
|
|
|
} else if (this.content.membership == "leave") {
|
|
|
|
plain = `${this.state_key} left the room`;
|
|
|
|
return `${this.state_key} left the room`;
|
|
|
|
} else if (this.content.membership == "kick") {
|
|
|
|
} else if (this.content.membership == "kick") {
|
|
|
|
plain = `${this.sender} kicked ${this.state_key}`;
|
|
|
|
return `${this.sender} kicked ${this.state_key}`;
|
|
|
|
} else if (this.content.membership == "ban") {
|
|
|
|
} else if (this.content.membership == "ban") {
|
|
|
|
plain = `${this.sender} banned ${this.state_key}`;
|
|
|
|
return `${this.sender} banned ${this.state_key}`;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return "unknown member event";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (this.type == "m.room.avatar") {
|
|
|
|
if (this.type == "m.room.avatar") {
|
|
|
|
|
|
|
|
if (this.content.url.length > 0) {
|
|
|
|
if (this.content.url.length > 0) {
|
|
|
|
plain = `${this.sender} changed the room avatar`;
|
|
|
|
return `${this.sender} changed the room avatar`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (this.type == "m.room.name") {
|
|
|
|
if (this.type == "m.room.name") {
|
|
|
|
|
|
|
|
return `${this.sender} changed the room name to ${this.content.name}`;
|
|
|
|
return `${this.sender} changed the room name to ${this.content.name}`;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return "unknown event";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return plain;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -59,114 +60,102 @@ let chat = create({
|
|
|
|
|
|
|
|
|
|
|
|
getSnapshotBeforeUpdate: function(_oldProps, _oldState) {
|
|
|
|
getSnapshotBeforeUpdate: function(_oldProps, _oldState) {
|
|
|
|
let ref = this.state.ref;
|
|
|
|
let ref = this.state.ref;
|
|
|
|
if (ref == null) {return null;}
|
|
|
|
|
|
|
|
if ((ref.scrollHeight - ref.offsetHeight) - ref.scrollTop < 100) { // Less than 100px from bottom
|
|
|
|
if (ref != null && (ref.scrollHeight - ref.offsetHeight) - ref.scrollTop < 100) {
|
|
|
|
|
|
|
|
// Less than 100px from bottom
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps, prevState, snapshot) {
|
|
|
|
componentDidUpdate(prevProps, prevState, snapshot) {
|
|
|
|
let ref = this.state.ref;
|
|
|
|
let ref = this.state.ref;
|
|
|
|
if (ref == null) {return;}
|
|
|
|
|
|
|
|
if (snapshot) { // scroll to bottom
|
|
|
|
if (ref != null && snapshot) {
|
|
|
|
|
|
|
|
// scroll to bottom
|
|
|
|
ref.scrollTop = (ref.scrollHeight - ref.offsetHeight);
|
|
|
|
ref.scrollTop = (ref.scrollHeight - ref.offsetHeight);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
setRef: function(ref) {
|
|
|
|
setRef: function(ref) {
|
|
|
|
if (ref != null) {
|
|
|
|
if (ref != null) {
|
|
|
|
this.setState({ref: ref});
|
|
|
|
this.setState({ ref: ref });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onReplyClick: function(e) {
|
|
|
|
onReplyClick: function(e) {
|
|
|
|
this.setState({replyEvent: e});
|
|
|
|
this.setState({ replyEvent: e });
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
paginateBackwards: function() {
|
|
|
|
paginateBackwards: function() {
|
|
|
|
if (this.state.loading) {
|
|
|
|
if (!this.state.loading) {
|
|
|
|
return;
|
|
|
|
let client = this.props.client;
|
|
|
|
|
|
|
|
let timeline = client.getRoom(this.props.roomId).getLiveTimeline();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.setState({loading: true});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.try(() => {
|
|
|
|
|
|
|
|
return client.paginateEventTimeline(timeline, {backwards: true});
|
|
|
|
|
|
|
|
}).then(() => {
|
|
|
|
|
|
|
|
this.setState({loading: false});
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let client = this.props.client;
|
|
|
|
|
|
|
|
client.paginateEventTimeline(client.getRoom(this.props.roomId).getLiveTimeline(), {backwards: true}).then(() => {
|
|
|
|
|
|
|
|
this.setState({loading: false});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
this.setState({loading: true});
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
render: function() {
|
|
|
|
render: function() {
|
|
|
|
let client = this.props.client;
|
|
|
|
let client = this.props.client;
|
|
|
|
let empty = (
|
|
|
|
|
|
|
|
<div className="main">
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
if (this.props.roomId == undefined) {
|
|
|
|
|
|
|
|
//empty screen
|
|
|
|
|
|
|
|
return empty;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let room = client.getRoom(this.props.roomId);
|
|
|
|
|
|
|
|
if (room == null) {
|
|
|
|
|
|
|
|
return empty;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let messageGroups = {
|
|
|
|
|
|
|
|
current: [],
|
|
|
|
|
|
|
|
groups: [],
|
|
|
|
|
|
|
|
sender: "",
|
|
|
|
|
|
|
|
type: ""
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// if the sender is the same, add it to the 'current' messageGroup, if not,
|
|
|
|
let empty = <div className="main" />;
|
|
|
|
// push the old one to 'groups' and start with a new array.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let liveTimeline = room.getLiveTimeline();
|
|
|
|
|
|
|
|
let liveTimelineEvents = liveTimeline.getEvents();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let events = [];
|
|
|
|
|
|
|
|
if (liveTimelineEvents.length > 0) {
|
|
|
|
|
|
|
|
liveTimelineEvents.forEach((MatrixEvent) => {
|
|
|
|
|
|
|
|
let event = MatrixEvent.event;
|
|
|
|
|
|
|
|
event = Object.assign(event, eventFunctions);
|
|
|
|
|
|
|
|
if (event.sender == null) { // localecho messages
|
|
|
|
|
|
|
|
event.sender = event.user_id;
|
|
|
|
|
|
|
|
event.local = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.sender != messageGroups.sender || event.type != messageGroups.type) {
|
|
|
|
|
|
|
|
messageGroups.sender = event.sender;
|
|
|
|
|
|
|
|
messageGroups.type = event.type;
|
|
|
|
|
|
|
|
if (messageGroups.current.length != 0) {
|
|
|
|
|
|
|
|
messageGroups.groups.push(messageGroups.current);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
messageGroups.current = [];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
messageGroups.current.push(event);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
messageGroups.groups.push(messageGroups.current);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
events = messageGroups.groups.map((events) => {
|
|
|
|
if (this.props.roomId == null) {
|
|
|
|
return <EventGroup key={`${this.props.roomId}-${events[0].event_id}`} events={events} client={this.props.client} room={room} onReplyClick={this.onReplyClick}/>;
|
|
|
|
//empty screen
|
|
|
|
});
|
|
|
|
return empty;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//TODO: replace with something that only renders events in view
|
|
|
|
let room = client.getRoom(this.props.roomId);
|
|
|
|
return (
|
|
|
|
if (room == null) {
|
|
|
|
<div className="main">
|
|
|
|
return empty;
|
|
|
|
<Info room={room} />
|
|
|
|
} else {
|
|
|
|
<div className="chat" ref={this.setRef}>
|
|
|
|
let liveTimeline = room.getLiveTimeline();
|
|
|
|
<div className="events">
|
|
|
|
let liveTimelineEvents = liveTimeline.getEvents();
|
|
|
|
<div className="paginateBackwards" onClick={this.paginateBackwards}>
|
|
|
|
|
|
|
|
{this.state.loading ?
|
|
|
|
let events = liveTimelineEvents.map((item) => {
|
|
|
|
<Loading/> :
|
|
|
|
let event = item.event;
|
|
|
|
<span>load older messages</span>
|
|
|
|
|
|
|
|
}
|
|
|
|
return Object.assign(
|
|
|
|
|
|
|
|
event,
|
|
|
|
|
|
|
|
eventFunctions,
|
|
|
|
|
|
|
|
(event.sender == null)
|
|
|
|
|
|
|
|
/* Whether this event is a local echo */
|
|
|
|
|
|
|
|
? { local: true, sender: event.user_id }
|
|
|
|
|
|
|
|
: null
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let eventGroups = groupEvents(events);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//TODO: replace with something that only renders events in view
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
|
|
<div className="main">
|
|
|
|
|
|
|
|
<Info room={room} />
|
|
|
|
|
|
|
|
<div className="chat" ref={this.setRef}>
|
|
|
|
|
|
|
|
<div className="events">
|
|
|
|
|
|
|
|
<div className="paginateBackwards" onClick={this.paginateBackwards}>
|
|
|
|
|
|
|
|
{this.state.loading ?
|
|
|
|
|
|
|
|
<Loading/> :
|
|
|
|
|
|
|
|
<span>load older messages</span>
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{(eventGroups.map((group) => {
|
|
|
|
|
|
|
|
return <EventGroup key={`${this.props.roomId}-${group.events[0].event_id}`} events={group.events} client={this.props.client} room={room} onReplyClick={this.onReplyClick}/>;
|
|
|
|
|
|
|
|
}))}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{events}
|
|
|
|
<Input client={client} roomId={this.props.roomId} replyEvent={this.state.replyEvent} onReplyClick={this.onReplyClick}/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
<Input client={client} roomId={this.props.roomId} replyEvent={this.state.replyEvent} onReplyClick={this.onReplyClick}/>
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|