Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/assets/icons/lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 5 additions & 3 deletions src/components/Feed/Feed.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import CommentEditToggle from '../ActionCard/CommentEditToggle'
import RichTextArea from '../RichTextArea/RichTextArea'
import NotificationsReader from '../../components/NotificationsReader'

import { EVENT_TYPE } from '../../config/constants'
import { EVENT_TYPE, PROJECT_FEED_TYPE_MESSAGES } from '../../config/constants'

import XMarkIcon from '../../assets/icons/x-mark.svg'
import FullscreenIcon from '../../assets/icons/ui-fullscreen.svg'
import LockIcon from '../../assets/icons/lock.svg'

import './Feed.scss'

Expand Down Expand Up @@ -86,7 +87,7 @@ class Feed extends React.Component {
id, user, currentUser, topicMessage, totalComments, hasMoreComments, onLoadMoreComments, isLoadingComments,
allowComments, comments, children, onNewCommentChange, onAddNewComment, isAddingComment, onSaveMessageChange,
onEditMessage, onSaveMessage, isSavingTopic, onDeleteMessage, onDeleteTopic, isDeletingTopic, error, allMembers,
onEnterFullscreenClick, onExitFullscreenClick, isFullScreen, commentId, projectMembers, commentAnchorPrefix
onEnterFullscreenClick, onExitFullscreenClick, isFullScreen, commentId, projectMembers, commentAnchorPrefix, tag
} = this.props
const { editTopicMode, headerHeight } = this.state
let authorName = user ? user.firstName : 'Unknown'
Expand All @@ -101,7 +102,7 @@ class Feed extends React.Component {
const content = topicMessage.newContent === null || topicMessage.newContent === undefined ? topicMessage.rawContent : topicMessage.newContent

topicHeader = (
<header styleName="feed-header" ref="header">
<header styleName={'feed-header' + (tag === PROJECT_FEED_TYPE_MESSAGES ? ' is-private' : '' )} ref="header">
<NotificationsReader
id={`topic-${id}`}
criteria={{ eventType: EVENT_TYPE.TOPIC.CREATED, contents: { topicId: id } }}
Expand All @@ -127,6 +128,7 @@ class Feed extends React.Component {
) : (
<div styleName="header-view">
<div styleName="header-view-inner">
{tag === PROJECT_FEED_TYPE_MESSAGES && <div styleName="lock-icon"><LockIcon /></div>}
<div styleName="header-info">
<div styleName="title">{title}</div>
<div styleName="header-details">
Expand Down
8 changes: 8 additions & 0 deletions src/components/Feed/Feed.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
background-color: $tc-gray-neutral-light;
border-radius: $card-border-radius $card-border-radius 0 0;

&.is-private {
background-color: $tc-orange-10;
}

.is-fullscreen & {
border-radius: 0;
position: fixed;
Expand Down Expand Up @@ -75,6 +79,10 @@
justify-content: space-between;
}

.lock-icon {
margin: $base-unit / 2 4 * $base-unit 0 0;
}

.title {
@include roboto;
color: $tc-black;
Expand Down
6 changes: 4 additions & 2 deletions src/components/Feed/NewPost.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class NewPost extends React.Component {
}

render() {
const {currentUser, allMembers, titlePlaceholder, contentPlaceholder, isCreating, hasError, expandedTitlePlaceholder, projectMembers} = this.props
const {currentUser, allMembers, titlePlaceholder, contentPlaceholder, isCreating, hasError, expandedTitlePlaceholder, projectMembers, canAccessPrivatePosts} = this.props
let authorName = currentUser.firstName
if (authorName && currentUser.lastName) {
authorName += ' ' + currentUser.lastName
Expand All @@ -39,6 +39,7 @@ class NewPost extends React.Component {
authorName={authorName}
allMembers={allMembers}
projectMembers={projectMembers}
hasPrivateSwitch={canAccessPrivatePosts}
/>
)
}
Expand All @@ -53,7 +54,8 @@ NewPost.propTypes = {
onPost: PropTypes.func.isRequired,
onNewPostChange: PropTypes.func.isRequired,
hasError: PropTypes.bool,
isCreating: PropTypes.bool
isCreating: PropTypes.bool,
canAccessPrivatePosts: PropTypes.bool,
}

export default NewPost
22 changes: 17 additions & 5 deletions src/components/Feed/NewPostMobile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import MobilePage from '../MobilePage/MobilePage'

import SwitchButton from 'appirio-tech-react-components/components/SwitchButton/SwitchButton'
import XMartIcon from '../../assets/icons/x-mark.svg'
import './NewPostMobile.scss'

Expand All @@ -32,7 +33,8 @@ class NewPostMobile extends React.Component {
step: props.step,
statusValue: '',
commentValue: '',
browserActualViewportHeigth: document.documentElement.clientHeight
browserActualViewportHeigth: document.documentElement.clientHeight,
isPrivate: false
}

this.setStep = this.setStep.bind(this)
Expand Down Expand Up @@ -80,9 +82,9 @@ class NewPostMobile extends React.Component {
render() {
const {
statusTitle, commentTitle, commentPlaceholder, submitText, onPost, onClose,
isCreating, nextStepText, statusPlaceholder
isCreating, nextStepText, statusPlaceholder, canAccessPrivatePosts
} = this.props
const { step, statusValue, commentValue, browserActualViewportHeigth } = this.state
const { step, statusValue, commentValue, browserActualViewportHeigth, isPrivate } = this.state

let value
let title
Expand All @@ -104,15 +106,24 @@ class NewPostMobile extends React.Component {
placeholder = commentPlaceholder
onBtnClick = () => onPost({
title: statusValue,
content: commentValue
content: commentValue,
isPrivate
})
btnText = submitText
}

return (
<MobilePage>
<div styleName="header">
<div styleName="plug" />
{canAccessPrivatePosts ?
<SwitchButton
name="private-post"
onChange={(evt) => this.setState({isPrivate: evt.target.checked})}
checked={isPrivate}
label="Private"
/> :
<div styleName="plug" />
}
<div styleName="title">{title}</div>
<div styleName="close-wrapper"><XMartIcon onClick={onClose} /></div>
</div>
Expand Down Expand Up @@ -153,6 +164,7 @@ NewPostMobile.propTypes = {
onClose: PropTypes.func.isRequired,
isCreating: PropTypes.bool,
hasError: PropTypes.bool,
canAccessPrivatePosts: PropTypes.bool,
}

export default NewPostMobile
15 changes: 14 additions & 1 deletion src/components/Feed/NewPostMobile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@
justify-content: space-between;
height: 50px;
padding: 0 $base-unit * 3;

:global {

.SwitchButton label {
margin: 0;

.label {
padding: 0 0 0 10px;
order: 1;
}
}
}
}

.title {
Expand All @@ -21,13 +33,14 @@
align-items: center;
display: flex;
justify-content: flex-end;
margin-left: 59px;
> svg {
fill: $tc-gray-neutral-light;
}
}

.plug {
width: 16px; /* same width as close button to align center title */
width: 83px; /* same width as close button to align center title */
}

.body {
Expand Down
30 changes: 21 additions & 9 deletions src/components/RichTextArea/RichTextArea.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'draft-js-mention-plugin/lib/plugin.css'
import createMentionPlugin, { defaultSuggestionsFilter } from 'draft-js-mention-plugin'
import _ from 'lodash'
import { getAvatarResized } from '../../helpers/tcHelpers'
import SwitchButton from 'appirio-tech-react-components/components/SwitchButton/SwitchButton'

const linkPlugin = createLinkPlugin()
const blockDndPlugin = createBlockDndPlugin()
Expand Down Expand Up @@ -54,7 +55,7 @@ const blocks = [
class RichTextArea extends React.Component {
constructor(props) {
super(props)
this.state = {editorExpanded: false, editorState: EditorState.createEmpty(), titleValue: '', suggestions: [], allSuggestions:[]}
this.state = {editorExpanded: false, editorState: EditorState.createEmpty(), titleValue: '', suggestions: [], allSuggestions:[], isPrivate: false}
this.onTitleChange = this.onTitleChange.bind(this)
this.onEditorChange = this.onEditorChange.bind(this)
this.handleKeyCommand = this.handleKeyCommand.bind(this)
Expand Down Expand Up @@ -119,7 +120,8 @@ class RichTextArea extends React.Component {
titleValue: '',
editorState: EditorState.push(this.state.editorState, EditorState.createEmpty().getCurrentContent()),
currentMDContent: null,
oldMDContent: null
oldMDContent: null,
isPrivate: false
})
}

Expand Down Expand Up @@ -168,7 +170,7 @@ class RichTextArea extends React.Component {
if (!isEditor && !isCloseButton && hasContent) {
return
}
this.setState({editorExpanded: isEditor && !isCloseButton})
this.setState({editorExpanded: isEditor && !isCloseButton, isPrivate: isEditor && !isCloseButton ? this.state.isPrivate : false})
}

handleKeyCommand(command) {
Expand Down Expand Up @@ -224,12 +226,13 @@ class RichTextArea extends React.Component {
if (this.props.isCreating) {
return
}
const title = this.state.titleValue

const title = this.state.titleValue
const content = this.state.currentMDContent
const isPrivate = this.state.isPrivate

if ((this.props.disableTitle || title) && (this.props.disableContent || content)) {
this.props.onPost({title, content})
this.props.onPost({title, content, isPrivate})
}
}
onSearchChange({value}){
Expand All @@ -254,8 +257,8 @@ class RichTextArea extends React.Component {
render() {
const {MentionSuggestions} = this.mentionPlugin
const {className, avatarUrl, authorName, titlePlaceholder, contentPlaceholder, editMode, isCreating,
isGettingComment, disableTitle, disableContent, expandedTitlePlaceholder, editingTopic } = this.props
const {editorExpanded, editorState, titleValue, oldMDContent, currentMDContent, uploading} = this.state
isGettingComment, disableTitle, disableContent, expandedTitlePlaceholder, editingTopic, hasPrivateSwitch } = this.props
const {editorExpanded, editorState, titleValue, oldMDContent, currentMDContent, uploading, isPrivate} = this.state
let canSubmit = (disableTitle || titleValue.trim())
&& (disableContent || editorState.getCurrentContent().hasText())
if (editMode && canSubmit) {
Expand Down Expand Up @@ -290,7 +293,7 @@ class RichTextArea extends React.Component {
}

return (
<div className={cn(className, 'rich-editor', {expanded: editorExpanded || editMode})} ref="richEditor">
<div className={cn(className, 'rich-editor', {expanded: editorExpanded || editMode}, {'is-private': isPrivate})} ref="richEditor">
{(isCreating || isGettingComment) &&
<div className="editing-layer" />
}
Expand Down Expand Up @@ -387,6 +390,14 @@ class RichTextArea extends React.Component {
</div>
}
<div className="tc-btns">
{hasPrivateSwitch &&
<SwitchButton
name="private-post"
onChange={(evt) => this.setState({isPrivate: evt.target.checked})}
checked={isPrivate}
label="Private"
/>
}
{!editMode &&
<button className="tc-btn tc-btn-default tc-btn-sm btn-close-creat">Cancel</button>
}
Expand Down Expand Up @@ -437,7 +448,8 @@ RichTextArea.propTypes = {
content: PropTypes.string,
allMembers: PropTypes.object,
projectMembers: PropTypes.object,
editingTopic: PropTypes.bool
editingTopic: PropTypes.bool,
hasPrivateSwitch: PropTypes.bool,
}

export default RichTextArea
16 changes: 16 additions & 0 deletions src/components/RichTextArea/RichTextArea.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
display: block;
}
}

&.is-private.modal {
background-color: $tc-orange-10;
}
}

.modal-row {
Expand Down Expand Up @@ -209,7 +213,19 @@
}

.tc-btns {
display: flex;
align-items: center;
margin-left: auto;

.SwitchButton label {
margin: 0 10px 0 0;

.label {
padding: 0 0 0 10px;
order: 1;
}
}

@media screen and (max-width: $screen-rg - 1px) {
padding-top: $base-unit;
}
Expand Down
3 changes: 3 additions & 0 deletions src/config/permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ export default {
INVITE_TOPCODER_MEMBER: {
projectRoles: [PROJECT_ROLE_MANAGER, PROJECT_ROLE_ACCOUNT_MANAGER, ROLE_CONNECT_ADMIN],
topcoderRoles: [ROLE_ADMINISTRATOR, ROLE_CONNECT_ADMIN, ROLE_CONNECT_MANAGER, ROLE_CONNECT_ACCOUNT_MANAGER],
},
ACCESS_PRIVATE_POST: {
topcoderRoles: [PROJECT_ROLE_COPILOT, ROLE_CONNECT_MANAGER, ROLE_CONNECT_ADMIN, ROLE_ADMINISTRATOR]
}
}
31 changes: 21 additions & 10 deletions src/projects/detail/containers/DashboardContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ import TwoColsLayout from '../../../components/TwoColsLayout'
import SystemFeed from '../../../components/Feed/SystemFeed'
import WorkInProgress from '../components/WorkInProgress'
import NotificationsReader from '../../../components/NotificationsReader'
import { checkPermission } from '../../../helpers/permissions'
import PERMISSIONS from '../../../config/permissions'

import {
PHASE_STATUS_ACTIVE,
CODER_BOT_USER_FNAME,
CODER_BOT_USER_LNAME,
PROJECT_FEED_TYPE_PRIMARY,
PROJECT_FEED_TYPE_MESSAGES,
EVENT_TYPE,
} from '../../../config/constants'

Expand Down Expand Up @@ -204,16 +207,24 @@ class DashboardContainer extends React.Component {
}
}

const mapStateToProps = ({ notifications, projectState, projectTopics, templates, phasesTopics }) => ({
notifications: preRenderNotifications(notifications.notifications),
productTemplates: templates.productTemplates,
isProcessing: projectState.processing,
phases: projectState.phases,
feeds: projectTopics.feeds[PROJECT_FEED_TYPE_PRIMARY].topics,
isFeedsLoading: projectTopics.isLoading,
phasesStates: projectState.phasesStates,
phasesTopics,
})
const mapStateToProps = ({ notifications, projectState, projectTopics, templates, phasesTopics }) => {
// all feeds includes primary as well as private topics if user has access to private topics
let allFeed = projectTopics.feeds[PROJECT_FEED_TYPE_PRIMARY].topics
if (checkPermission(PERMISSIONS.ACCESS_PRIVATE_POST)) {
allFeed = [...allFeed, ...projectTopics.feeds[PROJECT_FEED_TYPE_MESSAGES].topics]
}

return {
notifications: preRenderNotifications(notifications.notifications),
productTemplates: templates.productTemplates,
isProcessing: projectState.processing,
phases: projectState.phases,
feeds: allFeed,
isFeedsLoading: projectTopics.isLoading,
phasesStates: projectState.phasesStates,
phasesTopics,
}
}

const mapDispatchToProps = {
toggleNotificationRead,
Expand Down
Loading