Added GroupPinnedStatuses

• Added:
- GroupPinnedStatuses
- controllers for timeline, creation, deletion
- redux actions, reducers for creation, deletion
- timeline fetching in timelines action
- options to pin, unpin in status options popover for group admin
This commit is contained in:
mgabdev
2020-09-10 15:07:01 -05:00
parent 899fe425d4
commit d030783089
20 changed files with 332 additions and 23 deletions

View File

@@ -24,6 +24,8 @@ import {
fetchGroupRelationships,
createRemovedAccount,
groupRemoveStatus,
pinGroupStatus,
unpinGroupStatus,
} from '../../actions/groups'
import { initMuteModal } from '../../actions/mutes'
import { initReport } from '../../actions/reports'
@@ -86,6 +88,10 @@ class StatusOptionsPopover extends ImmutablePureComponent {
this.props.onPin(this.props.status)
}
handleGroupPinStatus = () => {
this.props.onPinGroupStatus(this.props.status)
}
handleBookmarkClick = () => {
if (this.props.isPro) {
this.props.onBookmark(this.props.status)
@@ -230,6 +236,29 @@ class StatusOptionsPopover extends ImmutablePureComponent {
}
}
if (withGroupAdmin) {
menu.push(null)
menu.push({
icon: 'trash',
hideArrow: true,
title: intl.formatMessage(messages.group_remove_account),
onClick: this.handleGroupRemoveAccount,
})
menu.push({
icon: 'trash',
hideArrow: true,
title: intl.formatMessage(messages.group_remove_post),
onClick: this.handleGroupRemovePost,
})
menu.push(null)
menu.push({
icon: 'pin',
hideArrow: true,
title: intl.formatMessage(status.get('pinned_by_group') ? messages.groupUnpin : messages.groupPin),
onClick: this.handleGroupPinStatus,
})
}
menu.push(null)
menu.push({
icon: 'copy',
@@ -249,22 +278,6 @@ class StatusOptionsPopover extends ImmutablePureComponent {
title: intl.formatMessage(messages.embed),
onClick: this.handleOnOpenEmbedModal,
})
if (withGroupAdmin) {
menu.push(null)
menu.push({
icon: 'trash',
hideArrow: true,
title: intl.formatMessage(messages.group_remove_account),
onClick: this.handleGroupRemoveAccount,
})
menu.push({
icon: 'trash',
hideArrow: true,
title: intl.formatMessage(messages.group_remove_post),
onClick: this.handleGroupRemovePost,
})
}
if (isStaff) {
menu.push(null)
@@ -316,6 +329,8 @@ const messages = defineMessages({
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
groupPin: { id: 'status.group_pin', defaultMessage: 'Pin in group' },
groupUnpin: { id: 'status.group_unpin', defaultMessage: 'Unpin from group' },
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark status' },
unbookmark: { id: 'status.unbookmark', defaultMessage: 'Remove bookmark' },
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
@@ -471,6 +486,16 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(openModal(MODAL_PRO_UPGRADE))
},
onPinGroupStatus(status) {
dispatch(closePopover())
if (status.get('pinned_by_group')) {
dispatch(unpinGroupStatus(status.getIn(['group', 'id']), status.get('id')))
} else {
dispatch(pinGroupStatus(status.getIn(['group', 'id']), status.get('id')))
}
},
onClosePopover: () => dispatch(closePopover()),
})

View File

@@ -82,6 +82,7 @@ class Status extends ImmutablePureComponent {
'isChild',
'isPromoted',
'isFeatured',
'isPinnedInGroup',
'isMuted',
'isHidden',
'isIntersecting',
@@ -311,6 +312,7 @@ class Status extends ImmutablePureComponent {
const {
intl,
isFeatured,
isPinnedInGroup,
isPromoted,
isChild,
isHidden,
@@ -429,7 +431,7 @@ class Status extends ImmutablePureComponent {
<div
className={[_s.d, _s.outlineNone].join(' ')}
tabIndex={this.props.isMuted ? null : 0}
data-featured={isFeatured ? 'true' : null}
data-featured={(isFeatured || isPinnedInGroup) ? 'true' : null}
aria-label={textForScreenReader(intl, status, rebloggedByText)}
ref={this.handleRef}
onClick={isChild && !isNotification ? this.handleClick : undefined}
@@ -442,6 +444,7 @@ class Status extends ImmutablePureComponent {
status={this.props.status}
isPromoted={isPromoted}
isFeatured={isFeatured}
isPinnedInGroup={isPinnedInGroup}
isComment={isComment && !isChild}
/>
@@ -564,6 +567,7 @@ Status.propTypes = {
isChild: PropTypes.bool,
isPromoted: PropTypes.bool,
isFeatured: PropTypes.bool,
isPinnedInGroup: PropTypes.bool,
isMuted: PropTypes.bool,
isHidden: PropTypes.bool,
isIntersecting: PropTypes.bool,

View File

@@ -67,11 +67,18 @@ class StatusList extends ImmutablePureComponent {
}
getFeaturedStatusCount = () => {
if (!!this.props.groupPinnedStatusIds) {
return this.props.groupPinnedStatusIds.size
}
return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0
}
getCurrentStatusIndex = (id, featured) => {
if (featured) {
if (!!this.props.groupPinnedStatusIds) {
return this.props.groupPinnedStatusIds.indexOf(id)
}
return this.props.featuredStatusIds.indexOf(id)
}
@@ -129,6 +136,7 @@ class StatusList extends ImmutablePureComponent {
const {
statusIds,
featuredStatusIds,
groupPinnedStatusIds,
onLoadMore,
timelineId,
totalQueuedItemsCount,
@@ -225,6 +233,20 @@ class StatusList extends ImmutablePureComponent {
)).concat(scrollableContent)
}
if (scrollableContent && groupPinnedStatusIds) {
scrollableContent = groupPinnedStatusIds.map((statusId) => (
<StatusContainer
key={`f-${statusId}`}
id={statusId}
isPinnedInGroup
onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown}
contextType={timelineId}
commentsLimited
/>
)).concat(scrollableContent)
}
return (
<React.Fragment>
<TimelineQueueButtonHeader
@@ -326,6 +348,7 @@ StatusList.propTypes = {
scrollKey: PropTypes.string.isRequired,
statusIds: ImmutablePropTypes.list.isRequired,
featuredStatusIds: ImmutablePropTypes.list,
groupPinnedStatusIds: ImmutablePropTypes.list,
onLoadMore: PropTypes.func,
isLoading: PropTypes.bool,
isPartial: PropTypes.bool,

View File

@@ -16,16 +16,17 @@ class StatusPrepend extends ImmutablePureComponent {
isFeatured,
isPromoted,
isComment,
isPinnedInGroup,
} = this.props
if (!status) return null
const isRepost = (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object')
if (!isFeatured && !isPromoted && !isRepost && !isComment) return null
if (!isFeatured && !isPinnedInGroup && !isPromoted && !isRepost && !isComment) return null
let iconId
if (isFeatured) iconId = 'pin'
if (isFeatured || isPinnedInGroup) iconId = 'pin'
else if (isPromoted) iconId = 'star'
else if (isRepost) iconId = 'repost'
else if (isComment) iconId = 'comment'
@@ -62,7 +63,7 @@ class StatusPrepend extends ImmutablePureComponent {
{
!isRepost && !isComment &&
<Text color='secondary' size='small'>
{intl.formatMessage(isFeatured ? messages.pinned : messages.promoted)}
{intl.formatMessage(isFeatured ? messages.pinned : isPinnedInGroup ? messages.pinnedByGroup : messages.promoted)}
</Text>
}
{
@@ -99,6 +100,7 @@ const messages = defineMessages({
filtered: { id: 'status.filtered', defaultMessage: 'Filtered' },
promoted: { id: 'status.promoted', defaultMessage: 'Promoted gab' },
pinned: { id: 'status.pinned', defaultMessage: 'Pinned gab' },
pinnedByGroup: { id: 'status.pinned_by_group', defaultMessage: 'Pinned to group' },
reposted: { id: 'status.reposted_by', defaultMessage: '{name} reposted' },
})
@@ -107,6 +109,7 @@ StatusPrepend.propTypes = {
status: ImmutablePropTypes.map,
isComment: PropTypes.bool,
isFeatured: PropTypes.bool,
isPinnedInGroup: PropTypes.bool,
isPromoted: PropTypes.bool,
}