layout for file & notif panel

This commit is contained in:
Matthew Hodgson 2016-09-11 02:14:27 +01:00
parent bae6409edb
commit 34bb37aaba
9 changed files with 186 additions and 44 deletions

View file

@ -103,6 +103,7 @@ var FilePanel = React.createClass({
manageReadMarkers={false} manageReadMarkers={false}
timelineSet={this.state.timelineSet} timelineSet={this.state.timelineSet}
showUrlPreview = { false } showUrlPreview = { false }
tileShape="file_grid"
opacity={ this.props.opacity } opacity={ this.props.opacity }
/> />
); );

View file

@ -79,6 +79,9 @@ module.exports = React.createClass({
// className for the panel // className for the panel
className: React.PropTypes.string.isRequired, className: React.PropTypes.string.isRequired,
// shape parameter to be passed to EventTiles
tileShape: React.PropTypes.string,
}, },
componentWillMount: function() { componentWillMount: function() {
@ -392,6 +395,7 @@ module.exports = React.createClass({
showUrlPreview={this.props.showUrlPreview} showUrlPreview={this.props.showUrlPreview}
checkUnmounting={this._isUnmounting} checkUnmounting={this._isUnmounting}
eventSendStatus={mxEv.status} eventSendStatus={mxEv.status}
tileShape={this.props.tileShape}
last={last} isSelectedEvent={highlight}/> last={last} isSelectedEvent={highlight}/>
</li> </li>
); );

View file

@ -47,6 +47,7 @@ var NotificationPanel = React.createClass({
timelineSet={timelineSet} timelineSet={timelineSet}
showUrlPreview = { false } showUrlPreview = { false }
opacity={ this.props.opacity } opacity={ this.props.opacity }
tileShape="notif"
/> />
); );
} }

View file

@ -93,6 +93,9 @@ var TimelinePanel = React.createClass({
// classname to use for the messagepanel // classname to use for the messagepanel
className: React.PropTypes.string, className: React.PropTypes.string,
// shape property to be passed to EventTiles
tileShape: React.PropTypes.string,
}, },
statics: { statics: {
@ -979,6 +982,7 @@ var TimelinePanel = React.createClass({
onFillRequest={ this.onMessageListFillRequest } onFillRequest={ this.onMessageListFillRequest }
opacity={ this.props.opacity } opacity={ this.props.opacity }
className={ this.props.className } className={ this.props.className }
tileShape={ this.props.tileShape }
/> />
); );
}, },

View file

@ -57,6 +57,21 @@ module.exports = React.createClass({
var TintableSvg = sdk.getComponent("elements.TintableSvg"); var TintableSvg = sdk.getComponent("elements.TintableSvg");
if (httpUrl) { if (httpUrl) {
if (this.props.tileShape === "file_grid") {
return (
<span className="mx_MFileBody">
<div className="mx_MImageBody_download">
<a className="mx_ImageBody_downloadLink" href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
{ content.body && content.body.length > 0 ? content.body : "Attachment" }
</a>
<div className="mx_MImageBody_size">
{ content.info && content.info.size ? filesize(content.info.size) : "" }
</div>
</div>
</span>
);
}
else {
return ( return (
<span className="mx_MFileBody"> <span className="mx_MFileBody">
<div className="mx_MImageBody_download"> <div className="mx_MImageBody_download">
@ -67,8 +82,9 @@ module.exports = React.createClass({
</div> </div>
</span> </span>
); );
}
} else { } else {
var extra = text ? ': '+text : ''; var extra = text ? (': ' + text) : '';
return <span className="mx_MFileBody"> return <span className="mx_MFileBody">
Invalid file{extra} Invalid file{extra}
</span> </span>

View file

@ -123,6 +123,30 @@ module.exports = React.createClass({
var content = this.props.mxEvent.getContent(); var content = this.props.mxEvent.getContent();
var cli = MatrixClientPeg.get(); var cli = MatrixClientPeg.get();
var download;
if (this.props.tileShape === "file_grid") {
download = (
<div className="mx_MImageBody_download">
<a className="mx_MImageBody_downloadLink" href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
{content.body}
</a>
<div className="mx_MImageBody_size">
{ content.info && content.info.size ? filesize(content.info.size) : "" }
</div>
</div>
);
}
else {
download = (
<div className="mx_MImageBody_download">
<a href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
<TintableSvg src="img/download.svg" width="12" height="14"/>
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
</a>
</div>
);
}
var thumbUrl = this._getThumbUrl(); var thumbUrl = this._getThumbUrl();
if (thumbUrl) { if (thumbUrl) {
return ( return (
@ -133,12 +157,7 @@ module.exports = React.createClass({
onMouseEnter={this.onImageEnter} onMouseEnter={this.onImageEnter}
onMouseLeave={this.onImageLeave} /> onMouseLeave={this.onImageLeave} />
</a> </a>
<div className="mx_MImageBody_download"> { download }
<a href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
<TintableSvg src="img/download.svg" width="12" height="14"/>
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
</a>
</div>
</span> </span>
); );
} else if (content.body) { } else if (content.body) {

View file

@ -69,12 +69,37 @@ module.exports = React.createClass({
} }
} }
var download;
if (this.props.tileShape === "file_grid") {
download = (
<div className="mx_MImageBody_download">
<a className="mx_MImageBody_downloadLink" href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
{content.body}
</a>
<div className="mx_MImageBody_size">
{ content.info && content.info.size ? filesize(content.info.size) : "" }
</div>
</div>
);
}
else {
download = (
<div className="mx_MImageBody_download">
<a href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
<TintableSvg src="img/download.svg" width="12" height="14"/>
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
</a>
</div>
);
}
return ( return (
<span className="mx_MVideoBody"> <span className="mx_MVideoBody">
<video className="mx_MVideoBody" src={cli.mxcUrlToHttp(content.url)} alt={content.body} <video className="mx_MVideoBody" src={cli.mxcUrlToHttp(content.url)} alt={content.body}
controls preload={preload} autoPlay={false} controls preload={preload} autoPlay={false}
height={height} width={width} poster={poster}> height={height} width={width} poster={poster}>
</video> </video>
{ download }
</span> </span>
); );
}, },

View file

@ -37,6 +37,9 @@ module.exports = React.createClass({
/* callback called when dynamic content in events are loaded */ /* callback called when dynamic content in events are loaded */
onWidgetLoad: React.PropTypes.func, onWidgetLoad: React.PropTypes.func,
/* the shsape of the tile, used */
tileShape: React.PropTypes.string,
}, },
getEventTileOps: function() { getEventTileOps: function() {
@ -69,6 +72,7 @@ module.exports = React.createClass({
return <BodyType ref="body" mxEvent={this.props.mxEvent} highlights={this.props.highlights} return <BodyType ref="body" mxEvent={this.props.mxEvent} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview} showUrlPreview={this.props.showUrlPreview}
tileShape={this.props.tileShape}
onWidgetLoad={this.props.onWidgetLoad} />; onWidgetLoad={this.props.onWidgetLoad} />;
}, },
}); });

View file

@ -128,6 +128,15 @@ module.exports = React.createClass({
/* the status of this event - ie, mxEvent.status. Denormalised to here so /* the status of this event - ie, mxEvent.status. Denormalised to here so
* that we can tell when it changes. */ * that we can tell when it changes. */
eventSendStatus: React.PropTypes.string, eventSendStatus: React.PropTypes.string,
/* the shape of the tile. by default, the layout is intended for the
* normal room timeline. alternative values are: "file_list", "file_grid"
* and "notif". This could be done by CSS, but it'd be horribly inefficient.
* It could also be done by subclassing EventTile, but that'd be quite
* boiilerplatey. So just make the necessary render decisions conditional
* for now.
*/
tileShape: React.PropTypes.string,
}, },
getInitialState: function() { getInitialState: function() {
@ -382,18 +391,16 @@ module.exports = React.createClass({
this.props.eventSendStatus this.props.eventSendStatus
) !== -1, ) !== -1,
mx_EventTile_notSent: this.props.eventSendStatus == 'not_sent', mx_EventTile_notSent: this.props.eventSendStatus == 'not_sent',
mx_EventTile_highlight: this.shouldHighlight(), mx_EventTile_highlight: this.props.tileShape === 'notif' ? false : this.shouldHighlight(),
mx_EventTile_selected: this.props.isSelectedEvent, mx_EventTile_selected: this.props.isSelectedEvent,
mx_EventTile_continuation: this.props.continuation, mx_EventTile_continuation: this.props.tileShape ? '' : this.props.continuation,
mx_EventTile_last: this.props.last, mx_EventTile_last: this.props.last,
mx_EventTile_contextual: this.props.contextual, mx_EventTile_contextual: this.props.contextual,
menu: this.state.menu, menu: this.state.menu,
mx_EventTile_verified: this.state.verified == true, mx_EventTile_verified: this.state.verified == true,
mx_EventTile_unverified: this.state.verified == false, mx_EventTile_unverified: this.state.verified == false,
}); });
var timestamp = <a href={ "#/room/" + this.props.mxEvent.getRoomId() +"/"+ this.props.mxEvent.getId() }> var permalink = "#/room/" + this.props.mxEvent.getRoomId() +"/"+ this.props.mxEvent.getId();
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
</a>
var readAvatars = this.getReadAvatars(); var readAvatars = this.getReadAvatars();
@ -401,7 +408,10 @@ module.exports = React.createClass({
let avatarSize; let avatarSize;
let needsSenderProfile; let needsSenderProfile;
if (isInfoMessage) { if (this.props.tileShape === "notif") {
avatarSize = 24;
needsSenderProfile = true;
} else if (isInfoMessage) {
// a small avatar, with no sender profile, for emotes and // a small avatar, with no sender profile, for emotes and
// joins/parts/etc // joins/parts/etc
avatarSize = 14; avatarSize = 14;
@ -428,17 +438,72 @@ module.exports = React.createClass({
if (needsSenderProfile) { if (needsSenderProfile) {
let aux = null; let aux = null;
if (!this.props.tileShape) {
if (msgtype === 'm.image') aux = "sent an image"; if (msgtype === 'm.image') aux = "sent an image";
else if (msgtype === 'm.video') aux = "sent a video"; else if (msgtype === 'm.video') aux = "sent a video";
else if (msgtype === 'm.file') aux = "uploaded a file"; else if (msgtype === 'm.file') aux = "uploaded a file";
sender = <SenderProfile onClick={ this.onSenderProfileClick } mxEvent={this.props.mxEvent} aux={aux} />; sender = <SenderProfile onClick={ this.onSenderProfileClick } mxEvent={this.props.mxEvent} aux={aux} />;
} }
else {
sender = <SenderProfile mxEvent={this.props.mxEvent} />;
}
}
var editButton = ( var editButton = (
<img className="mx_EventTile_editButton" src="img/icon_context_message.svg" width="19" height="19" alt="Options" title="Options" onClick={this.onEditClicked} /> <img className="mx_EventTile_editButton" src="img/icon_context_message.svg" width="19" height="19" alt="Options" title="Options" onClick={this.onEditClicked} />
); );
if (this.props.tileShape === "notif") {
var room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
return (
<div className={classes}>
<div className="mx_EventTile_roomName">
<a href={ permalink }>
{ room.name }
</a>
</div>
<div className="mx_EventTile_senderDetails">
{ avatar }
<a href={ permalink }>
{ sender }
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
</a>
</div>
<div className="mx_EventTile_line" >
<EventTileType ref="tile"
mxEvent={this.props.mxEvent}
highlights={this.props.highlights}
highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview}
onWidgetLoad={this.props.onWidgetLoad} />
</div>
</div>
);
}
else if (this.props.tileShape === "file_grid") {
return (
<div className={classes}>
<div className="mx_EventTile_line" >
<EventTileType ref="tile"
mxEvent={this.props.mxEvent}
highlights={this.props.highlights}
highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview}
tileShape={this.props.tileShape}
onWidgetLoad={this.props.onWidgetLoad} />
</div>
<a className="mx_EventTile_senderDetailsLink" href={ permalink }>
<div className="mx_EventTile_senderDetails">
{ sender }
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
</div>
</a>
</div>
);
}
else {
return ( return (
<div className={classes}> <div className={classes}>
<div className="mx_EventTile_msgOption"> <div className="mx_EventTile_msgOption">
@ -447,7 +512,9 @@ module.exports = React.createClass({
{ avatar } { avatar }
{ sender } { sender }
<div className="mx_EventTile_line"> <div className="mx_EventTile_line">
{ timestamp } <a href={ permalink }>
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
</a>
<EventTileType ref="tile" <EventTileType ref="tile"
mxEvent={this.props.mxEvent} mxEvent={this.props.mxEvent}
highlights={this.props.highlights} highlights={this.props.highlights}
@ -458,5 +525,6 @@ module.exports = React.createClass({
</div> </div>
</div> </div>
); );
}
}, },
}); });