Progress on dms, code cleanup
Progress on dms, code cleanup
This commit is contained in:
@@ -24,10 +24,13 @@ import Avatar from './avatar'
|
||||
import DisplayName from './display_name'
|
||||
import Button from './button'
|
||||
import Text from './text'
|
||||
|
||||
class Account extends ImmutablePureComponent {
|
||||
|
||||
handleAction = (e) => {
|
||||
this.props.onActionClick(this.props.account, e)
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
|
||||
handleUnrequest = () => {
|
||||
|
||||
61
app/javascript/gabsocial/components/avatar_group.js
Normal file
61
app/javascript/gabsocial/components/avatar_group.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import Image from './image'
|
||||
|
||||
|
||||
/**
|
||||
* Renders an avatar component
|
||||
* @param {list} [props.accounts] - the accounts for images
|
||||
* @param {number} [props.size=40] - the size of the avatar
|
||||
*/
|
||||
class AvatarGroup extends ImmutablePureComponent {
|
||||
|
||||
render() {
|
||||
const { accounts, size } = this.props
|
||||
|
||||
return (
|
||||
<div className={[_s.d].join(' ')}>
|
||||
{
|
||||
accounts.map((account) => {
|
||||
const isPro = account.get('is_pro')
|
||||
const alt = `${account.get('display_name')} ${isPro ? '(PRO)' : ''}`.trim()
|
||||
const className = [_s.d, _s.circle, _s.overflowHidden]
|
||||
if (isPro) {
|
||||
className.push(_s.boxShadowAvatarPro)
|
||||
}
|
||||
|
||||
const options = {
|
||||
alt,
|
||||
className,
|
||||
src: account.get('avatar_static'),
|
||||
style: {
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={[_s.d].join(' ')}>
|
||||
<Image {...options} />
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AvatarGroup.propTypes = {
|
||||
accounts: ImmutablePropTypes.list,
|
||||
size: PropTypes.number,
|
||||
}
|
||||
|
||||
AvatarGroup.defaultProps = {
|
||||
size: 40,
|
||||
}
|
||||
|
||||
export default AvatarGroup
|
||||
81
app/javascript/gabsocial/components/display_name_group.js
Normal file
81
app/javascript/gabsocial/components/display_name_group.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { me } from '../initial_state'
|
||||
import { CX } from '../constants'
|
||||
import Icon from './icon'
|
||||
import Text from './text'
|
||||
|
||||
class DisplayNameGroup extends ImmutablePureComponent {
|
||||
|
||||
render() {
|
||||
const {
|
||||
accounts,
|
||||
isMultiline,
|
||||
isLarge,
|
||||
noHover,
|
||||
isSmall,
|
||||
} = this.props
|
||||
|
||||
if (!account) return null
|
||||
|
||||
const containerClassName = CX({
|
||||
d: 1,
|
||||
maxW100PC: 1,
|
||||
aiCenter: !isMultiline,
|
||||
flexRow: !isMultiline,
|
||||
cursorPointer: !noHover,
|
||||
aiCenter: isCentered,
|
||||
})
|
||||
|
||||
const displayNameClasses = CX({
|
||||
text: 1,
|
||||
overflowWrapBreakWord: 1,
|
||||
whiteSpaceNoWrap: 1,
|
||||
fw600: 1,
|
||||
cPrimary: 1,
|
||||
mr2: 1,
|
||||
lineHeight125: !isSmall,
|
||||
fs14PX: isSmall,
|
||||
fs15PX: !isLarge,
|
||||
fs24PX: isLarge && !isSmall,
|
||||
})
|
||||
|
||||
const usernameClasses = CX({
|
||||
text: 1,
|
||||
displayFlex: 1,
|
||||
flexNormal: 1,
|
||||
flexShrink1: 1,
|
||||
overflowWrapBreakWord: 1,
|
||||
textOverflowEllipsis: 1,
|
||||
cSecondary: 1,
|
||||
fw400: 1,
|
||||
lineHeight15: isMultiline,
|
||||
lineHeight125: !isMultiline,
|
||||
ml5: !isMultiline,
|
||||
fs14PX: isSmall,
|
||||
fs15PX: !isLarge,
|
||||
fs16PX: isLarge && !isSmall,
|
||||
})
|
||||
|
||||
const iconSize =
|
||||
!!isLarge ? 19 :
|
||||
!!isComment ? 12 :
|
||||
!!isSmall ? 14 : 15
|
||||
|
||||
return (
|
||||
<div />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
DisplayNameGroup.propTypes = {
|
||||
accounts: ImmutablePropTypes.map,
|
||||
isLarge: PropTypes.bool,
|
||||
isMultiline: PropTypes.bool,
|
||||
isSmall: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default DisplayNameGroup
|
||||
@@ -3,21 +3,44 @@ import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import { me } from '../initial_state'
|
||||
import { CX } from '../constants'
|
||||
import { getWindowDimension } from '../utils/is_mobile'
|
||||
import {
|
||||
CX,
|
||||
MODAL_COMPOSE,
|
||||
BREAKPOINT_EXTRA_SMALL,
|
||||
} from '../constants'
|
||||
import { openModal } from '../actions/modal'
|
||||
import Button from './button'
|
||||
|
||||
const initialState = getWindowDimension()
|
||||
|
||||
class FloatingActionButton extends React.PureComponent {
|
||||
|
||||
state = {
|
||||
width: initialState.width,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.handleResize()
|
||||
window.addEventListener('resize', this.handleResize, false)
|
||||
}
|
||||
|
||||
handleResize = () => {
|
||||
const { width } = getWindowDimension()
|
||||
this.setState({ width })
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.handleResize, false)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
onOpenCompose,
|
||||
isDesktop,
|
||||
} = this.props
|
||||
const { intl, onOpenCompose } = this.props
|
||||
const { width } = this.state
|
||||
|
||||
if (!me) return null
|
||||
|
||||
const isDesktop = width > BREAKPOINT_EXTRA_SMALL
|
||||
const message = intl.formatMessage(messages.gab)
|
||||
|
||||
const containerClasses = CX({
|
||||
@@ -56,13 +79,12 @@ const messages = defineMessages({
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onOpenCompose: () => dispatch(openModal('COMPOSE')),
|
||||
onOpenCompose: () => dispatch(openModal(MODAL_COMPOSE)),
|
||||
})
|
||||
|
||||
FloatingActionButton.propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
onOpenCompose: PropTypes.func.isRequired,
|
||||
isDesktop: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default injectIntl(connect(null, mapDispatchToProps)(FloatingActionButton))
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ModalLayout from './modal_layout'
|
||||
import { ChatConversationCreate } from '../../features/ui/util/async_components'
|
||||
import WrappedBundle from '../../features/ui/util/wrapped_bundle'
|
||||
|
||||
class ChatConversationCreateModal extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
const { onClose, chatConversationId } = this.props
|
||||
|
||||
return (
|
||||
<ModalLayout
|
||||
title='New Conversation'
|
||||
width={440}
|
||||
onClose={onClose}
|
||||
noPadding
|
||||
>
|
||||
<WrappedBundle component={ChatConversationCreate} componentParams={{ chatConversationId, onCloseModal: onClose }} />
|
||||
</ModalLayout>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ChatConversationCreateModal.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
chatConversationId: PropTypes.string,
|
||||
}
|
||||
|
||||
export default ChatConversationCreateModal
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { deleteChatConversation } from '../../actions/chat_conversations'
|
||||
import ConfirmationModal from './confirmation_modal'
|
||||
|
||||
class ChatConversationDeleteModal extends React.PureComponent {
|
||||
|
||||
handleClick = () => {
|
||||
this.props.onConfirm(this.props.chatConversationId)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onClose } = this.props
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title='Delete Conversation'
|
||||
message='Are you sure you want to delete this chat conversation? The messages will not be deleted and you the other participant can still view messages.'
|
||||
confirm='Delete'
|
||||
onConfirm={this.handleClick}
|
||||
onClose={onClose}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onDeleteChatConversation: (chatConversationId) => {
|
||||
dispatch(deleteChatConversation(chatConversationId))
|
||||
},
|
||||
})
|
||||
|
||||
ChatConversationDeleteModal.propTypes = {
|
||||
chatConversationId: PropTypes.string.isRequired,
|
||||
onDeleteChatConversation: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ChatConversationDeleteModal)
|
||||
@@ -10,6 +10,8 @@ import LoadingModal from './loading_modal'
|
||||
import {
|
||||
MODAL_BLOCK_ACCOUNT,
|
||||
MODAL_BOOST,
|
||||
MODAL_CHAT_CONVERSATION_CREATE,
|
||||
MODAL_CHAT_CONVERSATION_DELETE,
|
||||
MODAL_COMMUNITY_TIMELINE_SETTINGS,
|
||||
MODAL_COMPOSE,
|
||||
MODAL_CONFIRM,
|
||||
@@ -42,6 +44,8 @@ import {
|
||||
import {
|
||||
BlockAccountModal,
|
||||
BoostModal,
|
||||
ChatConversationCreateModal,
|
||||
ChatConversationDeleteModal,
|
||||
CommunityTimelineSettingsModal,
|
||||
ComposeModal,
|
||||
ConfirmationModal,
|
||||
@@ -74,37 +78,40 @@ import {
|
||||
VideoModal,
|
||||
} from '../../features/ui/util/async_components'
|
||||
|
||||
const MODAL_COMPONENTS = {}
|
||||
MODAL_COMPONENTS[MODAL_BLOCK_ACCOUNT] = BlockAccountModal
|
||||
MODAL_COMPONENTS[MODAL_BOOST] = BoostModal
|
||||
MODAL_COMPONENTS[MODAL_COMMUNITY_TIMELINE_SETTINGS] = CommunityTimelineSettingsModal
|
||||
MODAL_COMPONENTS[MODAL_COMPOSE] = ComposeModal
|
||||
MODAL_COMPONENTS[MODAL_CONFIRM] = ConfirmationModal
|
||||
MODAL_COMPONENTS[MODAL_DISPLAY_OPTIONS] = DisplayOptionsModal
|
||||
MODAL_COMPONENTS[MODAL_EDIT_SHORTCUTS] = EditShortcutsModal
|
||||
MODAL_COMPONENTS[MODAL_EDIT_PROFILE] = EditProfileModal
|
||||
MODAL_COMPONENTS[MODAL_EMAIL_CONFIRMATION_REMINDER] = EmailConfirmationReminderModal
|
||||
MODAL_COMPONENTS[MODAL_GROUP_CREATE] = GroupCreateModal
|
||||
MODAL_COMPONENTS[MODAL_GROUP_DELETE] = GroupDeleteModal
|
||||
MODAL_COMPONENTS[MODAL_GROUP_PASSWORD] = GroupPasswordModal
|
||||
MODAL_COMPONENTS[MODAL_HASHTAG_TIMELINE_SETTINGS] = HashtagTimelineSettingsModal
|
||||
MODAL_COMPONENTS[MODAL_HOME_TIMELINE_SETTINGS] = HomeTimelineSettingsModal
|
||||
MODAL_COMPONENTS[MODAL_HOTKEYS] = HotkeysModal
|
||||
MODAL_COMPONENTS[MODAL_LIST_ADD_USER] = ListAddUserModal
|
||||
MODAL_COMPONENTS[MODAL_LIST_CREATE] = ListCreateModal
|
||||
MODAL_COMPONENTS[MODAL_LIST_DELETE] = ListDeleteModal
|
||||
MODAL_COMPONENTS[MODAL_LIST_EDITOR] = ListEditorModal
|
||||
MODAL_COMPONENTS[MODAL_LIST_TIMELINE_SETTINGS] = ListTimelineSettingsModal
|
||||
MODAL_COMPONENTS[MODAL_MEDIA] = MediaModal
|
||||
MODAL_COMPONENTS[MODAL_MUTE] = MuteModal
|
||||
MODAL_COMPONENTS[MODAL_PRO_UPGRADE] = ProUpgradeModal
|
||||
MODAL_COMPONENTS[MODAL_REPORT] = ReportModal
|
||||
MODAL_COMPONENTS[MODAL_STATUS_LIKES] = StatusLikesModal
|
||||
MODAL_COMPONENTS[MODAL_STATUS_REPOSTS] = StatusRepostsModal
|
||||
MODAL_COMPONENTS[MODAL_STATUS_REVISIONS] = StatusRevisionsModal
|
||||
MODAL_COMPONENTS[MODAL_UNAUTHORIZED] = UnauthorizedModal
|
||||
MODAL_COMPONENTS[MODAL_UNFOLLOW] = UnfollowModal
|
||||
MODAL_COMPONENTS[MODAL_VIDEO] = VideoModal
|
||||
const MODAL_COMPONENTS = {
|
||||
[MODAL_BLOCK_ACCOUNT]: BlockAccountModal,
|
||||
[MODAL_BOOST]: BoostModal,
|
||||
[MODAL_CHAT_CONVERSATION_CREATE]: ChatConversationCreateModal,
|
||||
[MODAL_CHAT_CONVERSATION_DELETE]: ChatConversationDeleteModal,
|
||||
[MODAL_COMMUNITY_TIMELINE_SETTINGS]: CommunityTimelineSettingsModal,
|
||||
[MODAL_COMPOSE]: ComposeModal,
|
||||
[MODAL_CONFIRM]: ConfirmationModal,
|
||||
[MODAL_DISPLAY_OPTIONS]: DisplayOptionsModal,
|
||||
[MODAL_EDIT_SHORTCUTS]: EditShortcutsModal,
|
||||
[MODAL_EDIT_PROFILE]: EditProfileModal,
|
||||
[MODAL_EMAIL_CONFIRMATION_REMINDER]: EmailConfirmationReminderModal,
|
||||
[MODAL_GROUP_CREATE]: GroupCreateModal,
|
||||
[MODAL_GROUP_DELETE]: GroupDeleteModal,
|
||||
[MODAL_GROUP_PASSWORD]: GroupPasswordModal,
|
||||
[MODAL_HASHTAG_TIMELINE_SETTINGS]: HashtagTimelineSettingsModal,
|
||||
[MODAL_HOME_TIMELINE_SETTINGS]: HomeTimelineSettingsModal,
|
||||
[MODAL_HOTKEYS]: HotkeysModal,
|
||||
[MODAL_LIST_ADD_USER]: ListAddUserModal,
|
||||
[MODAL_LIST_CREATE]: ListCreateModal,
|
||||
[MODAL_LIST_DELETE]: ListDeleteModal,
|
||||
[MODAL_LIST_EDITOR]: ListEditorModal,
|
||||
[MODAL_LIST_TIMELINE_SETTINGS]: ListTimelineSettingsModal,
|
||||
[MODAL_MEDIA]: MediaModal,
|
||||
[MODAL_MUTE]: MuteModal,
|
||||
[MODAL_PRO_UPGRADE]: ProUpgradeModal,
|
||||
[MODAL_REPORT]: ReportModal,
|
||||
[MODAL_STATUS_LIKES]: StatusLikesModal,
|
||||
[MODAL_STATUS_REPOSTS]: StatusRepostsModal,
|
||||
[MODAL_STATUS_REVISIONS]: StatusRevisionsModal,
|
||||
[MODAL_UNAUTHORIZED]: UnauthorizedModal,
|
||||
[MODAL_UNFOLLOW]: UnfollowModal,
|
||||
[MODAL_VIDEO]: VideoModal,
|
||||
}
|
||||
|
||||
const CENTERED_XS_MODALS = [
|
||||
MODAL_BLOCK_ACCOUNT,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { getRandomInt } from '../../utils/numbers'
|
||||
import PlaceholderLayout from './placeholder_layout'
|
||||
|
||||
export default class ChatMessagePlaceholder extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
const alt = getRandomInt(0, 1) === 1
|
||||
const width = getRandomInt(120, 240)
|
||||
const height = getRandomInt(40, 110)
|
||||
|
||||
if (alt) {
|
||||
return (
|
||||
<PlaceholderLayout viewBox='0 0 400 110' preserveAspectRatio='xMaxYMin meet'>
|
||||
<rect x='80' y='0' rx='20' ry='20' width='260' height={height} />
|
||||
<circle cx='380' cy='20' r='20' />
|
||||
</PlaceholderLayout>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<PlaceholderLayout viewBox='0 0 400 110' preserveAspectRatio='xMinYMax meet'>
|
||||
<circle cx='20' cy='20' r='20' />
|
||||
<rect x='60' y='0' rx='20' ry='20' width={width} height={height} />
|
||||
</PlaceholderLayout>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,6 +12,7 @@ class PlaceholderLayout extends React.PureComponent {
|
||||
intl,
|
||||
theme,
|
||||
viewBox,
|
||||
preserveAspectRatio,
|
||||
} = this.props
|
||||
|
||||
const isLight = ['light', 'white', ''].indexOf(theme) > -1
|
||||
@@ -26,6 +27,7 @@ class PlaceholderLayout extends React.PureComponent {
|
||||
viewBox={viewBox}
|
||||
backgroundColor={backgroundColor}
|
||||
foregroundColor={foregroundColor}
|
||||
preserveAspectRatio={preserveAspectRatio}
|
||||
>
|
||||
{this.props.children}
|
||||
</ContentLoader>
|
||||
@@ -47,6 +49,7 @@ PlaceholderLayout.propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
theme: PropTypes.string.isRequired,
|
||||
viewBox: PropTypes.string.isRequired,
|
||||
preserveAspectRatio: PropTypes.string,
|
||||
}
|
||||
|
||||
export default injectIntl(connect(mapStateToProps)(PlaceholderLayout))
|
||||
|
||||
@@ -94,30 +94,32 @@ class ScrollableList extends React.PureComponent {
|
||||
|
||||
handleScroll = throttle(() => {
|
||||
if (this.window) {
|
||||
const { scrollTop, scrollHeight } = this.documentElement;
|
||||
const { innerHeight } = this.window;
|
||||
const offset = scrollHeight - scrollTop - innerHeight;
|
||||
const { scrollTop, scrollHeight } = this.documentElement
|
||||
const { innerHeight } = this.window
|
||||
const offset = scrollHeight - scrollTop - innerHeight
|
||||
|
||||
if (600 > offset && this.props.onLoadMore && this.props.hasMore && !this.props.isLoading && !this.props.disableInfiniteScroll) {
|
||||
this.props.onLoadMore();
|
||||
this.props.onLoadMore()
|
||||
}
|
||||
|
||||
if (scrollTop < 100 && this.props.onScrollToTop) {
|
||||
this.props.onScrollToTop();
|
||||
this.props.onScrollToTop()
|
||||
} else if (scrollTop < 100 && this.props.onScrollToBottom) {
|
||||
this.props.onScrollToBottom()
|
||||
} else if (this.props.onScroll) {
|
||||
this.props.onScroll();
|
||||
this.props.onScroll()
|
||||
}
|
||||
|
||||
if (!this.lastScrollWasSynthetic) {
|
||||
// If the last scroll wasn't caused by setScrollTop(), assume it was
|
||||
// intentional and cancel any pending scroll reset on mouse idle
|
||||
this.scrollToTopOnMouseIdle = false;
|
||||
this.scrollToTopOnMouseIdle = false
|
||||
}
|
||||
this.lastScrollWasSynthetic = false;
|
||||
this.lastScrollWasSynthetic = false
|
||||
}
|
||||
}, 150, {
|
||||
trailing: true,
|
||||
});
|
||||
})
|
||||
|
||||
handleWheel = throttle(() => {
|
||||
this.scrollToTopOnMouseIdle = false;
|
||||
@@ -175,6 +177,11 @@ class ScrollableList extends React.PureComponent {
|
||||
this.props.onLoadMore();
|
||||
}
|
||||
|
||||
setRef = (c) => {
|
||||
this.node = c
|
||||
if (this.props.scrollRef) this.props.scrollRef(c)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
children,
|
||||
@@ -186,6 +193,8 @@ class ScrollableList extends React.PureComponent {
|
||||
onLoadMore,
|
||||
placeholderComponent: Placeholder,
|
||||
placeholderCount,
|
||||
onScrollToTop,
|
||||
onScrollToBottom,
|
||||
} = this.props
|
||||
const childrenCount = React.Children.count(children);
|
||||
|
||||
@@ -210,8 +219,18 @@ class ScrollableList extends React.PureComponent {
|
||||
return <ColumnIndicator type='loading' />
|
||||
} else if (isLoading || childrenCount > 0 || hasMore || !emptyMessage) {
|
||||
return (
|
||||
<div onMouseMove={this.handleMouseMove}>
|
||||
<div onMouseMove={this.handleMouseMove} ref={this.setRef}>
|
||||
<div role='feed'>
|
||||
{
|
||||
(hasMore && onLoadMore && !isLoading) && !!onScrollToBottom &&
|
||||
<LoadMore onClick={this.handleLoadMore} />
|
||||
}
|
||||
|
||||
{
|
||||
isLoading && !!onScrollToBottom &&
|
||||
<ColumnIndicator type='loading' />
|
||||
}
|
||||
|
||||
{
|
||||
!!this.props.children &&
|
||||
React.Children.map(this.props.children, (child, index) => (
|
||||
@@ -234,12 +253,12 @@ class ScrollableList extends React.PureComponent {
|
||||
}
|
||||
|
||||
{
|
||||
(hasMore && onLoadMore && !isLoading) &&
|
||||
(hasMore && onLoadMore && !isLoading) && !!onScrollToTop &&
|
||||
<LoadMore onClick={this.handleLoadMore} />
|
||||
}
|
||||
|
||||
{
|
||||
isLoading &&
|
||||
isLoading && !!onScrollToTop &&
|
||||
<ColumnIndicator type='loading' />
|
||||
}
|
||||
</div>
|
||||
@@ -268,9 +287,10 @@ ScrollableList.propTypes = {
|
||||
]),
|
||||
children: PropTypes.node,
|
||||
onScrollToTop: PropTypes.func,
|
||||
onScrollToBottom: PropTypes.func,
|
||||
onScroll: PropTypes.func,
|
||||
placeholderComponent: PropTypes.node,
|
||||
placeholderCount: PropTypes.node,
|
||||
placeholderCount: PropTypes.number,
|
||||
disableInfiniteScroll: PropTypes.bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,18 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import {
|
||||
CX,
|
||||
BREAKPOINT_SMALL,
|
||||
} from '../../constants'
|
||||
import { CX } from '../../constants'
|
||||
import {
|
||||
me,
|
||||
emailConfirmed,
|
||||
} from '../../initial_state'
|
||||
import Button from '../button'
|
||||
import { openModal } from '../../actions/modal'
|
||||
import Responsive from '../../features/ui/util/responsive_component'
|
||||
import Heading from '../heading'
|
||||
import BackButton from '../back_button'
|
||||
import Pills from '../pills'
|
||||
|
||||
class SidebarLayout extends React.PureComponent {
|
||||
|
||||
handleOpenComposeModal = () => {
|
||||
this.props.onOpenComposeModal()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
actions,
|
||||
@@ -91,30 +82,6 @@ class SidebarLayout extends React.PureComponent {
|
||||
{children}
|
||||
</nav>
|
||||
|
||||
{
|
||||
!!me &&
|
||||
<Responsive min={BREAKPOINT_SMALL}>
|
||||
<Button
|
||||
onClick={this.handleOpenComposeModal}
|
||||
className={_s.py15}
|
||||
icon='pencil'
|
||||
iconSize='18px'
|
||||
iconClassName={[_s.py5, _s.px5].join(' ')}
|
||||
/>
|
||||
</Responsive>
|
||||
}
|
||||
|
||||
{
|
||||
!!me &&
|
||||
<Responsive max={BREAKPOINT_SMALL}>
|
||||
<Button
|
||||
onClick={this.handleOpenComposeModal}
|
||||
className={_s.py15}
|
||||
icon='pencil'
|
||||
/>
|
||||
</Responsive>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,18 +91,11 @@ class SidebarLayout extends React.PureComponent {
|
||||
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onOpenComposeModal() {
|
||||
dispatch(openModal('COMPOSE'))
|
||||
},
|
||||
})
|
||||
|
||||
SidebarLayout.propTypes = {
|
||||
onOpenComposeModal: PropTypes.func.isRequired,
|
||||
actions: PropTypes.array,
|
||||
tabs: PropTypes.array,
|
||||
title: PropTypes.string,
|
||||
showBackBtn: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(SidebarLayout)
|
||||
export default SidebarLayout
|
||||
@@ -290,7 +290,7 @@ class StatusList extends ImmutablePureComponent {
|
||||
hasMore={hasMore}
|
||||
/>
|
||||
<ScrollableList
|
||||
ref={this.setRef}
|
||||
scrollRef={this.setRef}
|
||||
isLoading={isLoading || isRefreshing}
|
||||
showLoading={isRefreshing || (isLoading && statusIds.size === 0)}
|
||||
onLoadMore={onLoadMore && this.handleLoadOlder}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { CX } from '../constants'
|
||||
|
||||
// Define colors for enumeration for Text component `color` prop
|
||||
const COLORS = {
|
||||
alt: 'alt',
|
||||
primary: 'primary',
|
||||
secondary: 'secondary',
|
||||
tertiary: 'tertiary',
|
||||
@@ -76,6 +77,7 @@ class Text extends React.PureComponent {
|
||||
lineHeight15: isBadge,
|
||||
px5: isBadge,
|
||||
|
||||
cAlt: color === COLORS.alt,
|
||||
cPrimary: color === COLORS.primary,
|
||||
cSecondary: color === COLORS.secondary,
|
||||
cTertiary: color === COLORS.tertiary,
|
||||
|
||||
@@ -35,19 +35,31 @@ class Toast extends React.PureComponent {
|
||||
message,
|
||||
date,
|
||||
to,
|
||||
type,
|
||||
} = this.props
|
||||
|
||||
const contentClasses = CX({
|
||||
default: 1,
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
radiusSmall: 1,
|
||||
w228PX: 1,
|
||||
mt5: 1,
|
||||
pt2: 1,
|
||||
maxWidth240PX: 1,
|
||||
mb5: 1,
|
||||
px15: 1,
|
||||
pt10: 1,
|
||||
pb15: !!title,
|
||||
pb10: !title,
|
||||
bgToast: 1,
|
||||
boxShadowToast: 1,
|
||||
})
|
||||
|
||||
const contentClasses = CX({
|
||||
d: 1,
|
||||
mt5: !!title,
|
||||
pt2: !!title,
|
||||
flexRow: !!image,
|
||||
})
|
||||
|
||||
const innerContentClasses = CX({
|
||||
default: 1,
|
||||
d: 1,
|
||||
flexNormal: 1,
|
||||
pl10: !!image,
|
||||
pt2: !!image,
|
||||
@@ -65,19 +77,11 @@ class Toast extends React.PureComponent {
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.radiusSmall, _s.mb5, _s.px15, _s.pt10, _s.pb15, _s.bgPrimary, _s.boxShadowToast].join(' ')}>
|
||||
<div className={containerClasses}>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
|
||||
<Text size='media' weight='medium' className={[_s.mr15, _s.minWidth160PX].join(' ')}>
|
||||
<Text size='medium' color='alt' weight='bold'>
|
||||
{title}
|
||||
</Text>
|
||||
<Button
|
||||
backgroundColor='secondary'
|
||||
color='primary'
|
||||
icon='close'
|
||||
iconSize='6px'
|
||||
onClick={this.handleOnDismiss}
|
||||
className={[_s.mlAuto, _s.px10].join(' ')}
|
||||
/>
|
||||
</div>
|
||||
<div className={contentClasses}>
|
||||
{
|
||||
@@ -90,12 +94,16 @@ class Toast extends React.PureComponent {
|
||||
/>
|
||||
}
|
||||
<div className={innerContentClasses}>
|
||||
<Text size='small'>
|
||||
<Text size='small' color='alt'>
|
||||
{message}
|
||||
</Text>
|
||||
{
|
||||
date &&
|
||||
<Text color='secondary' size='extraSmall' className={dateClasses}>
|
||||
<Text color='tertiary' size='extraSmall' className={dateClasses}>
|
||||
{
|
||||
!image &&
|
||||
<Text size='small' color='tertiary' className={[_s.ml5, _s.mr5].join(' ')}>·</Text>
|
||||
}
|
||||
<RelativeTimestamp timestamp={date} />
|
||||
</Text>
|
||||
}
|
||||
@@ -116,10 +124,6 @@ Toast.propTypes = {
|
||||
onDismiss: PropTypes.func.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
to: PropTypes.string,
|
||||
type: PropTypes.oneOf([
|
||||
TOAST_TYPE_ERROR,
|
||||
TOAST_TYPE_SUCCESS,
|
||||
]).isRequired,
|
||||
}
|
||||
|
||||
export default Toast
|
||||
Reference in New Issue
Block a user