diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 0585941653..6fb473ae68 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -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);
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 5a69c9bfba..90ba477188 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -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);
diff --git a/web/react/components/post_attachment.jsx b/web/react/components/post_attachment.jsx
new file mode 100644
index 0000000000..6bc9ade476
--- /dev/null
+++ b/web/react/components/post_attachment.jsx
@@ -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(
+
+ {field.title}
+ |
+ );
+ bodyCols.push(
+
+ |
+ );
+ });
+
+ tHead = (
+
+ {headerCols}
+
+ );
+ tBody = (
+
+ {bodyCols}
+
+ );
+ } else {
+ tBody = [];
+
+ fields.forEach((field, i) => {
+ tBody.push(
+
+ |
+ {field.title}
+ |
+
+ |
+
+ );
+ });
+ }
+
+ return (
+
+
+ {tHead}
+
+
+ {tBody}
+
+
+ );
+ }
+
+ render() {
+ const data = this.props.attachment;
+
+ let preText;
+ if (data.pretext) {
+ preText = (
+
+
+ );
+ }
+
+ let author = [];
+ if (data.author_name || data.author_icon) {
+ if (data.author_icon) {
+ author.push(
+
+ );
+ }
+ if (data.author_name) {
+ author.push(
+
+ {data.author_name}
+
+ );
+ }
+ }
+ if (data.author_link) {
+ author = (
+
+ {author}
+
+ );
+ }
+
+ let title;
+ if (data.title) {
+ if (data.title_link) {
+ title = (
+
+ );
+ } else {
+ title = (
+
+ {data.title}
+
+ );
+ }
+ }
+
+ let text;
+ if (data.text) {
+ text = (
+
+
+ );
+ }
+
+ let image;
+ if (data.image_url) {
+ image = (
+
+ );
+ }
+
+ let thumb;
+ if (data.thumb_url) {
+ thumb = (
+
+

+
+ );
+ }
+
+ const fields = this.getFieldsTable();
+
+ let useBorderStyle;
+ if (data.color && data.color[0] === '#') {
+ useBorderStyle = {borderLeftColor: data.color};
+ }
+
+ return (
+
+ {preText}
+
+
+ {author}
+ {title}
+
+
+ {text}
+ {image}
+ {fields}
+
+ {thumb}
+
+
+
+
+
+ );
+ }
+}
+
+PostAttachment.propTypes = {
+ attachment: React.PropTypes.object.isRequired
+};
\ No newline at end of file
diff --git a/web/react/components/post_attachment_list.jsx b/web/react/components/post_attachment_list.jsx
new file mode 100644
index 0000000000..03b8666561
--- /dev/null
+++ b/web/react/components/post_attachment_list.jsx
@@ -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(
+
+ );
+ });
+
+ return (
+
+ {content}
+
+ );
+ }
+}
+
+PostAttachmentList.propTypes = {
+ attachments: React.PropTypes.array.isRequired
+};
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index e4094daf3d..4da13dace2 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -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 = (
+
+ );
+ }
+
return (
{comment}
@@ -331,6 +341,7 @@ export default class PostBody extends React.Component {
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.message)}}
/>
+ {postAttachments}
{fileAttachmentHolder}
{embed}
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 9ce7ca1e8b..f006956bc7 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -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);
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index ef19ac601c..dd88e6e167 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -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;
+ }
+ }
+}
\ No newline at end of file