attachments frontend
Этот коммит содержится в:
@@ -109,6 +109,7 @@ export default class CreateComment extends React.Component {
|
|||||||
post.pending_post_id = `${userId}:${time}`;
|
post.pending_post_id = `${userId}:${time}`;
|
||||||
post.user_id = userId;
|
post.user_id = userId;
|
||||||
post.create_at = time;
|
post.create_at = time;
|
||||||
|
post.attachments = [];
|
||||||
|
|
||||||
PostStore.storePendingPost(post);
|
PostStore.storePendingPost(post);
|
||||||
PostStore.storeCommentDraft(this.props.rootId, null);
|
PostStore.storeCommentDraft(this.props.rootId, null);
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ export default class CreatePost extends React.Component {
|
|||||||
post.create_at = time;
|
post.create_at = time;
|
||||||
post.root_id = this.state.rootId;
|
post.root_id = this.state.rootId;
|
||||||
post.parent_id = this.state.parentId;
|
post.parent_id = this.state.parentId;
|
||||||
|
post.attachments = [];
|
||||||
|
|
||||||
const channel = ChannelStore.get(this.state.channelId);
|
const channel = ChannelStore.get(this.state.channelId);
|
||||||
|
|
||||||
|
|||||||
240
web/react/components/post_attachment.jsx
Обычный файл
240
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(
|
||||||
|
<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
|
||||||
|
};
|
||||||
32
web/react/components/post_attachment_list.jsx
Обычный файл
32
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(
|
||||||
|
<PostAttachment
|
||||||
|
attachment={attachment}
|
||||||
|
key={'att_' + i}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='attachment_list'>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PostAttachmentList.propTypes = {
|
||||||
|
attachments: React.PropTypes.array.isRequired
|
||||||
|
};
|
||||||
@@ -7,6 +7,7 @@ const Utils = require('../utils/utils.jsx');
|
|||||||
const Constants = require('../utils/constants.jsx');
|
const Constants = require('../utils/constants.jsx');
|
||||||
const TextFormatting = require('../utils/text_formatting.jsx');
|
const TextFormatting = require('../utils/text_formatting.jsx');
|
||||||
const twemoji = require('twemoji');
|
const twemoji = require('twemoji');
|
||||||
|
const PostAttachmentList = require('./post_attachment_list.jsx');
|
||||||
|
|
||||||
export default class PostBody extends React.Component {
|
export default class PostBody extends React.Component {
|
||||||
constructor(props) {
|
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 (
|
return (
|
||||||
<div className='post-body'>
|
<div className='post-body'>
|
||||||
{comment}
|
{comment}
|
||||||
@@ -331,6 +341,7 @@ export default class PostBody extends React.Component {
|
|||||||
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.message)}}
|
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.message)}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{postAttachments}
|
||||||
{fileAttachmentHolder}
|
{fileAttachmentHolder}
|
||||||
{embed}
|
{embed}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -481,6 +481,7 @@ export function applyTheme(theme) {
|
|||||||
changeCss('.modal .modal-header', 'background:' + theme.sidebarHeaderBg, 1);
|
changeCss('.modal .modal-header', 'background:' + theme.sidebarHeaderBg, 1);
|
||||||
changeCss('#navbar .navbar-default', '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('@media(max-width: 768px){.search-bar__container', 'background:' + theme.sidebarHeaderBg, 1);
|
||||||
|
changeCss('.attachment .attachment__container', 'border-left-color:' + theme.sidebarHeaderBg, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme.sidebarHeaderTextColor) {
|
if (theme.sidebarHeaderTextColor) {
|
||||||
@@ -519,6 +520,7 @@ export function applyTheme(theme) {
|
|||||||
changeCss('.popover.left>.arrow:after', 'border-left-color:' + theme.centerChannelBg, 1);
|
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('.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('.search-bar__container .search__form .search-bar, .form-control', 'background:' + theme.centerChannelBg, 1);
|
||||||
|
changeCss('.attachment__content', 'background:' + theme.centerChannelBg, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme.centerChannelColor) {
|
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('@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('.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('.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('.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__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);
|
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);
|
||||||
|
|||||||
@@ -610,3 +610,74 @@ body.ios {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin: 0 0 0 -4px;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ссылка в новой задаче
Block a user