Added unique photos and videos tab to profile
• Added: - unique photos and videos tab to profile - mediaType/media_type query string in api/statuses_controller • Updated: - /media to redirect to /photos - AccountGallery, selector to accept mediaType and update on change in componentWillReceiveProps • Removed: - Generic "media" tab • Todo: - Create index for MediaAttachment.type
This commit is contained in:
parent
1f78bc6879
commit
a5e99dd7c3
@ -52,11 +52,21 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||||||
# Also, Avoid getting slow by not narrowing down by `statuses.account_id`.
|
# Also, Avoid getting slow by not narrowing down by `statuses.account_id`.
|
||||||
# When narrowing down by `statuses.account_id`, `index_statuses_20180106` will be used
|
# When narrowing down by `statuses.account_id`, `index_statuses_20180106` will be used
|
||||||
# and the table will be joined by `Merge Semi Join`, so the query will be slow.
|
# and the table will be joined by `Merge Semi Join`, so the query will be slow.
|
||||||
@account.statuses.joins(:media_attachments).merge(@account.media_attachments).permitted_for(@account, current_account)
|
@account.statuses.joins(:media_attachments)
|
||||||
|
.merge(account_media_ids_query)
|
||||||
|
.permitted_for(@account, current_account)
|
||||||
.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
||||||
.reorder(id: :desc).distinct(:id).pluck(:id)
|
.reorder(id: :desc).distinct(:id).pluck(:id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def account_media_ids_query
|
||||||
|
if params[:media_type] == 'video'
|
||||||
|
@account.media_attachments.where(type: 2)
|
||||||
|
else
|
||||||
|
@account.media_attachments.where.not(type: 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def pinned_scope
|
def pinned_scope
|
||||||
@account.pinned_statuses
|
@account.pinned_statuses
|
||||||
end
|
end
|
||||||
|
@ -151,7 +151,7 @@ export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimelin
|
|||||||
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
|
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
|
||||||
export const expandAccountTimeline = (accountId, { maxId, withReplies, commentsOnly } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}${commentsOnly ? ':comments_only' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { only_comments: commentsOnly, exclude_replies: (!withReplies && !commentsOnly), max_id: maxId });
|
export const expandAccountTimeline = (accountId, { maxId, withReplies, commentsOnly } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}${commentsOnly ? ':comments_only' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { only_comments: commentsOnly, exclude_replies: (!withReplies && !commentsOnly), max_id: maxId });
|
||||||
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
|
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
|
||||||
export const expandAccountMediaTimeline = (accountId, { maxId, limit } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: limit || 20 });
|
export const expandAccountMediaTimeline = (accountId, { maxId, limit, mediaType } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: limit || 20, media_type: mediaType });
|
||||||
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
|
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
|
||||||
export const expandGroupTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`group:${id}`, `/api/v1/timelines/group/${id}`, { max_id: maxId }, done);
|
export const expandGroupTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`group:${id}`, `/api/v1/timelines/group/${id}`, { max_id: maxId }, done);
|
||||||
export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, done = noOp) => {
|
export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, done = noOp) => {
|
||||||
|
@ -29,7 +29,8 @@ const messages = defineMessages({
|
|||||||
headerPhoto: { id: 'header_photo', defaultMessage: 'Header photo' },
|
headerPhoto: { id: 'header_photo', defaultMessage: 'Header photo' },
|
||||||
timeline: { id: 'timeline', defaultMessage: 'Timeline' },
|
timeline: { id: 'timeline', defaultMessage: 'Timeline' },
|
||||||
comments: { id: 'comments', defaultMessage: 'Comments' },
|
comments: { id: 'comments', defaultMessage: 'Comments' },
|
||||||
media: { id: 'media', defaultMessage: 'Media' },
|
photos: { id: 'photos', defaultMessage: 'Photos' },
|
||||||
|
videos: { id: 'videos', defaultMessage: 'Videos' },
|
||||||
accountFollowsYou: { id: 'account.follows_you', defaultMessage: 'Follows you' },
|
accountFollowsYou: { id: 'account.follows_you', defaultMessage: 'Follows you' },
|
||||||
editProfile: { id: "account.edit_profile", defaultMessage: "Edit profile" },
|
editProfile: { id: "account.edit_profile", defaultMessage: "Edit profile" },
|
||||||
})
|
})
|
||||||
@ -107,8 +108,12 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||||||
title: intl.formatMessage(messages.comments),
|
title: intl.formatMessage(messages.comments),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: `/${account.get('acct')}/media`,
|
to: `/${account.get('acct')}/photos`,
|
||||||
title: intl.formatMessage(messages.media),
|
title: intl.formatMessage(messages.photos),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
to: `/${account.get('acct')}/videos`,
|
||||||
|
title: intl.formatMessage(messages.videos),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -12,12 +12,12 @@ const messages = defineMessages({
|
|||||||
none: { id: 'account_gallery.none', defaultMessage: 'No media to show.' },
|
none: { id: 'account_gallery.none', defaultMessage: 'No media to show.' },
|
||||||
})
|
})
|
||||||
|
|
||||||
const mapStateToProps = (state, { account }) => {
|
const mapStateToProps = (state, { account, mediaType }) => {
|
||||||
const accountId = !!account ? account.get('id') : -1
|
const accountId = !!account ? account.get('id') : -1
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accountId,
|
accountId,
|
||||||
attachments: getAccountGallery(state, accountId),
|
attachments: getAccountGallery(state, accountId, mediaType),
|
||||||
isLoading: state.getIn(['timelines', `account:${accountId}:media`, 'isLoading']),
|
isLoading: state.getIn(['timelines', `account:${accountId}:media`, 'isLoading']),
|
||||||
hasMore: state.getIn(['timelines', `account:${accountId}:media`, 'hasMore']),
|
hasMore: state.getIn(['timelines', `account:${accountId}:media`, 'hasMore']),
|
||||||
}
|
}
|
||||||
@ -36,19 +36,32 @@ class AccountGallery extends ImmutablePureComponent {
|
|||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
|
mediaType: PropTypes.oneOf([
|
||||||
|
'photo',
|
||||||
|
'video',
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
mediaType: 'both'
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { accountId } = this.props
|
const { accountId, mediaType } = this.props
|
||||||
|
|
||||||
if (accountId && accountId !== -1) {
|
if (accountId && accountId !== -1) {
|
||||||
this.props.dispatch(expandAccountMediaTimeline(accountId))
|
this.props.dispatch(expandAccountMediaTimeline(accountId, { mediaType }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.accountId && nextProps.accountId !== this.props.accountId) {
|
if (
|
||||||
this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId))
|
(nextProps.accountId && nextProps.accountId !== this.props.accountId) ||
|
||||||
|
(nextProps.accountId && nextProps.mediaType !== this.props.mediaType)
|
||||||
|
) {
|
||||||
|
this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, {
|
||||||
|
mediaType: nextProps.mediaType,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +71,7 @@ class AccountGallery extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleScroll = e => {
|
handleScroll = (e) => {
|
||||||
const { scrollTop, scrollHeight, clientHeight } = e.target
|
const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||||
const offset = scrollHeight - scrollTop - clientHeight
|
const offset = scrollHeight - scrollTop - clientHeight
|
||||||
|
|
||||||
@ -67,13 +80,16 @@ class AccountGallery extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoadMore = maxId => {
|
handleLoadMore = (maxId) => {
|
||||||
if (this.props.accountId && this.props.accountId !== -1) {
|
if (this.props.accountId && this.props.accountId !== -1) {
|
||||||
this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, { maxId }))
|
this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, {
|
||||||
|
maxId,
|
||||||
|
mediaType: this.props.mediaType,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoadOlder = e => {
|
handleLoadOlder = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.handleScrollToBottom()
|
this.handleScrollToBottom()
|
||||||
}
|
}
|
||||||
|
@ -205,8 +205,11 @@ class SwitchingArea extends PureComponent {
|
|||||||
<Redirect from='/@:username/following' to='/:username/following' />
|
<Redirect from='/@:username/following' to='/:username/following' />
|
||||||
<WrappedRoute path='/:username/following' page={ProfilePage} component={Following} content={children} />
|
<WrappedRoute path='/:username/following' page={ProfilePage} component={Following} content={children} />
|
||||||
|
|
||||||
<Redirect from='/@:username/media' to='/:username/media' />
|
<Redirect from='/@:username/media' to='/:username/photos' />
|
||||||
<WrappedRoute path='/:username/media' page={ProfilePage} component={AccountGallery} content={children} />
|
<Redirect from='/@:username/photos' to='/:username/photos' />
|
||||||
|
<Redirect from='/:username/media' to='/:username/photos' />
|
||||||
|
<WrappedRoute path='/:username/photos' page={ProfilePage} component={AccountGallery} content={children} componentParams={{ noSidebar: true, mediaType: 'photo' }} />
|
||||||
|
<WrappedRoute path='/:username/videos' page={ProfilePage} component={AccountGallery} content={children} componentParams={{ noSidebar: true, mediaType: 'video' }} />
|
||||||
|
|
||||||
<Redirect from='/@:username/likes' to='/:username/likes' />
|
<Redirect from='/@:username/likes' to='/:username/likes' />
|
||||||
<WrappedRoute path='/:username/likes' page={ProfilePage} component={LikedStatuses} content={children} />
|
<WrappedRoute path='/:username/likes' page={ProfilePage} component={LikedStatuses} content={children} />
|
||||||
|
@ -139,16 +139,27 @@ export const makeGetNotification = () => {
|
|||||||
export const getAccountGallery = createSelector([
|
export const getAccountGallery = createSelector([
|
||||||
(state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], ImmutableList()),
|
(state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], ImmutableList()),
|
||||||
state => state.get('statuses'),
|
state => state.get('statuses'),
|
||||||
], (statusIds, statuses) => {
|
(state, id, mediaType) => mediaType,
|
||||||
let medias = ImmutableList();
|
], (statusIds, statuses, mediaType) => {
|
||||||
|
let medias = ImmutableList()
|
||||||
|
|
||||||
statusIds.forEach(statusId => {
|
statusIds.forEach((statusId) => {
|
||||||
const status = statuses.get(statusId);
|
const status = statuses.get(statusId)
|
||||||
medias = medias.concat(status.get('media_attachments').map(media => media.set('status', status)));
|
medias = medias.concat(
|
||||||
});
|
status.get('media_attachments')
|
||||||
|
.filter((media) => {
|
||||||
|
if (mediaType === 'video') {
|
||||||
|
return media.get('type') === 'video'
|
||||||
|
}
|
||||||
|
|
||||||
return medias;
|
return media.get('type') !== 'video'
|
||||||
});
|
})
|
||||||
|
.map((media) => media.set('status', status))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return medias
|
||||||
|
})
|
||||||
|
|
||||||
export const getOrderedLists = createSelector([state => state.get('lists')], lists => {
|
export const getOrderedLists = createSelector([state => state.get('lists')], lists => {
|
||||||
if (!lists) return lists
|
if (!lists) return lists
|
||||||
|
Loading…
Reference in New Issue
Block a user