attachments frontend

This commit is contained in:
Florian Orben
2015-10-31 03:22:02 +01:00
parent d96cb50b68
commit d3ed791ab5
7 changed files with 359 additions and 0 deletions

View File

@@ -109,6 +109,7 @@ export default class CreateComment extends React.Component {
post.pending_post_id = `${userId}:${time}`;
post.user_id = userId;
post.create_at = time;
post.attachments = [];
PostStore.storePendingPost(post);
PostStore.storeCommentDraft(this.props.rootId, null);

View File

@@ -173,6 +173,7 @@ export default class CreatePost extends React.Component {
post.create_at = time;
post.root_id = this.state.rootId;
post.parent_id = this.state.parentId;
post.attachments = [];
const channel = ChannelStore.get(this.state.channelId);

View File

@@ -0,0 +1,240 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
const TextFormatting = require('../utils/text_formatting.jsx');
export default class PostAttachment extends React.Component {
constructor(props) {
super(props);
this.getFieldsTable = this.getFieldsTable.bind(this);
}
getFieldsTable() {
const fields = this.props.attachment.fields;
if (!fields || !fields.length) {
return '';
}
const compactTable = fields.filter((field) => field.short).length > 0;
let tHead;
let tBody;
if (compactTable) {
let headerCols = [];
let bodyCols = [];
fields.forEach((field, i) => {
headerCols.push(
<th
className='attachment___field-caption'
key={'attachment__field-caption-' + i}
>
{field.title}
</th>
);
bodyCols.push(
<td
className='attachment___field'
key={'attachment__field-' + i}
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(field.value || '')}}
>
</td>
);
});
tHead = (
<tr>
{headerCols}
</tr>
);
tBody = (
<tr>
{bodyCols}
</tr>
);
} else {
tBody = [];
fields.forEach((field, i) => {
tBody.push(
<tr key={'attachment__field-' + i}>
<td
className='attachment___field-caption'
>
{field.title}
</td>
<td
className='attachment___field'
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(field.value || '')}}
>
</td>
</tr>
);
});
}
return (
<table
className='attachment___fields'
>
<thead>
{tHead}
</thead>
<tbody>
{tBody}
</tbody>
</table>
);
}
render() {
const data = this.props.attachment;
let preText;
if (data.pretext) {
preText = (
<div
className='attachment__thumb-pretext'
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(data.pretext)}}
>
</div>
);
}
let author = [];
if (data.author_name || data.author_icon) {
if (data.author_icon) {
author.push(
<img
className='attachment__author-icon'
src={data.author_icon}
key={'attachment__author-icon'}
height='14'
width='14'
/>
);
}
if (data.author_name) {
author.push(
<span
className='attachment__author-name'
key={'attachment__author-name'}
>
{data.author_name}
</span>
);
}
}
if (data.author_link) {
author = (
<a
href={data.author_link}
target='_blank'
>
{author}
</a>
);
}
let title;
if (data.title) {
if (data.title_link) {
title = (
<h1
className='attachment__title'
>
<a
className='attachment__title-link'
href={data.title_link}
target='_blank'
>
{data.title}
</a>
</h1>
);
} else {
title = (
<h1
className='attachment__title'
>
{data.title}
</h1>
);
}
}
let text;
if (data.text) {
text = (
<div
className='attachment__text'
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(data.text || '')}}
>
</div>
);
}
let image;
if (data.image_url) {
image = (
<img
className='attachment__image'
src={data.image_url}
/>
);
}
let thumb;
if (data.thumb_url) {
thumb = (
<div
className='attachment__thumb-container'
>
<img
src={data.thumb_url}
/>
</div>
);
}
const fields = this.getFieldsTable();
let useBorderStyle;
if (data.color && data.color[0] === '#') {
useBorderStyle = {borderLeftColor: data.color};
}
return (
<div
className='attachment'
>
{preText}
<div className='attachment__content'>
<div
className={useBorderStyle ? 'attachment__container' : 'attachment__container attachment__container--' + data.color}
style={useBorderStyle}
>
{author}
{title}
<div>
<div
className={thumb ? 'attachment__body' : 'attachment__body attachment__body--no_thumb'}
>
{text}
{image}
{fields}
</div>
{thumb}
<div style={{clear: 'both'}}></div>
</div>
</div>
</div>
</div>
);
}
}
PostAttachment.propTypes = {
attachment: React.PropTypes.object.isRequired
};

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
const PostAttachment = require('./post_attachment.jsx');
export default class PostAttachmentList extends React.Component {
constructor(props) {
super(props);
}
render() {
let content = [];
this.props.attachments.forEach((attachment, i) => {
content.push(
<PostAttachment
attachment={attachment}
key={'att_' + i}
/>
);
});
return (
<div className='attachment_list'>
{content}
</div>
);
}
}
PostAttachmentList.propTypes = {
attachments: React.PropTypes.array.isRequired
};

View File

@@ -7,6 +7,7 @@ const Utils = require('../utils/utils.jsx');
const Constants = require('../utils/constants.jsx');
const TextFormatting = require('../utils/text_formatting.jsx');
const twemoji = require('twemoji');
const PostAttachmentList = require('./post_attachment_list.jsx');
export default class PostBody extends React.Component {
constructor(props) {
@@ -316,6 +317,15 @@ export default class PostBody extends React.Component {
);
}
let postAttachments = '';
if (post.attachments && post.attachments.length) {
postAttachments = (
<PostAttachmentList
attachments={post.attachments}
/>
);
}
return (
<div className='post-body'>
{comment}
@@ -331,6 +341,7 @@ export default class PostBody extends React.Component {
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.message)}}
/>
</div>
{postAttachments}
{fileAttachmentHolder}
{embed}
</div>

View File

@@ -481,6 +481,7 @@ export function applyTheme(theme) {
changeCss('.modal .modal-header', 'background:' + theme.sidebarHeaderBg, 1);
changeCss('#navbar .navbar-default', 'background:' + theme.sidebarHeaderBg, 1);
changeCss('@media(max-width: 768px){.search-bar__container', 'background:' + theme.sidebarHeaderBg, 1);
changeCss('.attachment .attachment__container', 'border-left-color:' + theme.sidebarHeaderBg, 1);
}
if (theme.sidebarHeaderTextColor) {
@@ -519,6 +520,7 @@ export function applyTheme(theme) {
changeCss('.popover.left>.arrow:after', 'border-left-color:' + theme.centerChannelBg, 1);
changeCss('.popover.top>.arrow:after, .tip-overlay.tip-overlay--chat .arrow', 'border-top-color:' + theme.centerChannelBg, 1);
changeCss('.search-bar__container .search__form .search-bar, .form-control', 'background:' + theme.centerChannelBg, 1);
changeCss('.attachment__content', 'background:' + theme.centerChannelBg, 1);
}
if (theme.centerChannelColor) {
@@ -552,6 +554,7 @@ export function applyTheme(theme) {
changeCss('@media(max-width: 768px){.search-bar__container .search__form .search-bar', 'background:' + changeOpacity(theme.centerChannelColor, 0.2) + '; color: inherit;', 1);
changeCss('.input-group-addon, .search-bar__container .search__form, .form-control', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
changeCss('.form-control:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1);
changeCss('.attachment .attachment__content', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1);
changeCss('.channel-intro .channel-intro__content, .webhooks__container', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1);
changeCss('.date-separator .separator__text', 'color:' + theme.centerChannelColor, 2);
changeCss('.date-separator .separator__hr, .modal-footer, .modal .custom-textarea, .post-right__container .post.post--root hr, .search-item-container', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);

View File

@@ -610,3 +610,74 @@ body.ios {
font-weight: 600;
margin: 0 0 0 -4px;
}
.attachment {
.attachment__content {
border-width: 1px;
border-style: solid;
border-radius: 4px;
padding: 2px 5px;
margin: 0 0 5px 0;
}
.attachment__thumb-pretext {
border: 0 none;
background: transparent;
}
.attachment__container {
border-left-width: 4px;
border-left-style: solid;
padding: 2px 0 2px 10px;
&.attachment__container--good {
border-left-color: #00C100;
}
&.attachment__container--warning {
border-left-color: #DEDE01;
}
&.attachment__container--danger {
border-left-color: #E40303;
}
}
.attachment__body {
float: left;
width: 80%;
padding-right: 5px;
&.attachment__body--no_thumb {
width: 100%;
}
}
.attachment__thumb-pretext {
margin-left: 5px;
}
.attachment__title {
margin: 5px 0;
padding: 0;
line-height: 16px;
font-size: 16px;
a {
font-size: 16px;
}
}
.attachment__author-icon {
@include border-radius(50px);
margin-right: 5px;
width: 14px;
height: 14px;
}
.attachment__image {
max-width: 100%;
}
.attachment__thumb-container {
width: 20%;
float: right;
img {
height: 75px;
max-width: 100%;
}
}
.attachment___fields {
width: 100%;
.attachment___field-caption {
font-weight: 700;
}
}
}