From a5e99dd7c3ae667e2d8663c627837462a7c39c86 Mon Sep 17 00:00:00 2001 From: mgabdev <> Date: Mon, 8 Jun 2020 19:38:36 -0400 Subject: [PATCH] Added unique photos and videos tab to profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • 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 --- .../api/v1/accounts/statuses_controller.rb | 12 ++++++- app/javascript/gabsocial/actions/timelines.js | 2 +- .../gabsocial/components/profile_header.js | 11 ++++-- .../gabsocial/features/account_gallery.js | 36 +++++++++++++------ app/javascript/gabsocial/features/ui/ui.js | 7 ++-- app/javascript/gabsocial/selectors/index.js | 27 +++++++++----- 6 files changed, 70 insertions(+), 25 deletions(-) diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb index 224a0567..76b857a4 100644 --- a/app/controllers/api/v1/accounts/statuses_controller.rb +++ b/app/controllers/api/v1/accounts/statuses_controller.rb @@ -52,11 +52,21 @@ class Api::V1::Accounts::StatusesController < Api::BaseController # 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 # 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]) .reorder(id: :desc).distinct(:id).pluck(:id) 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 @account.pinned_statuses end diff --git a/app/javascript/gabsocial/actions/timelines.js b/app/javascript/gabsocial/actions/timelines.js index f6a34f2c..64634707 100644 --- a/app/javascript/gabsocial/actions/timelines.js +++ b/app/javascript/gabsocial/actions/timelines.js @@ -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 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 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 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) => { diff --git a/app/javascript/gabsocial/components/profile_header.js b/app/javascript/gabsocial/components/profile_header.js index 4230d507..c8b13085 100644 --- a/app/javascript/gabsocial/components/profile_header.js +++ b/app/javascript/gabsocial/components/profile_header.js @@ -29,7 +29,8 @@ const messages = defineMessages({ headerPhoto: { id: 'header_photo', defaultMessage: 'Header photo' }, timeline: { id: 'timeline', defaultMessage: 'Timeline' }, 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' }, editProfile: { id: "account.edit_profile", defaultMessage: "Edit profile" }, }) @@ -107,8 +108,12 @@ class ProfileHeader extends ImmutablePureComponent { title: intl.formatMessage(messages.comments), }, { - to: `/${account.get('acct')}/media`, - title: intl.formatMessage(messages.media), + to: `/${account.get('acct')}/photos`, + title: intl.formatMessage(messages.photos), + }, + { + to: `/${account.get('acct')}/videos`, + title: intl.formatMessage(messages.videos), }, ] diff --git a/app/javascript/gabsocial/features/account_gallery.js b/app/javascript/gabsocial/features/account_gallery.js index cb88cf6c..154fc8a6 100644 --- a/app/javascript/gabsocial/features/account_gallery.js +++ b/app/javascript/gabsocial/features/account_gallery.js @@ -12,12 +12,12 @@ const messages = defineMessages({ 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 return { accountId, - attachments: getAccountGallery(state, accountId), + attachments: getAccountGallery(state, accountId, mediaType), isLoading: state.getIn(['timelines', `account:${accountId}:media`, 'isLoading']), hasMore: state.getIn(['timelines', `account:${accountId}:media`, 'hasMore']), } @@ -36,19 +36,32 @@ class AccountGallery extends ImmutablePureComponent { isLoading: PropTypes.bool, hasMore: PropTypes.bool, intl: PropTypes.object.isRequired, + mediaType: PropTypes.oneOf([ + 'photo', + 'video', + ]), } + static defaultProps = { + mediaType: 'both' + } + componentDidMount() { - const { accountId } = this.props + const { accountId, mediaType } = this.props if (accountId && accountId !== -1) { - this.props.dispatch(expandAccountMediaTimeline(accountId)) + this.props.dispatch(expandAccountMediaTimeline(accountId, { mediaType })) } } componentWillReceiveProps(nextProps) { - if (nextProps.accountId && nextProps.accountId !== this.props.accountId) { - this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId)) + if ( + (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 offset = scrollHeight - scrollTop - clientHeight @@ -67,13 +80,16 @@ class AccountGallery extends ImmutablePureComponent { } } - handleLoadMore = maxId => { + handleLoadMore = (maxId) => { 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() this.handleScrollToBottom() } diff --git a/app/javascript/gabsocial/features/ui/ui.js b/app/javascript/gabsocial/features/ui/ui.js index 99bb0a64..c676eb6f 100644 --- a/app/javascript/gabsocial/features/ui/ui.js +++ b/app/javascript/gabsocial/features/ui/ui.js @@ -205,8 +205,11 @@ class SwitchingArea extends PureComponent { - - + + + + + diff --git a/app/javascript/gabsocial/selectors/index.js b/app/javascript/gabsocial/selectors/index.js index ea74c539..3153e362 100644 --- a/app/javascript/gabsocial/selectors/index.js +++ b/app/javascript/gabsocial/selectors/index.js @@ -139,16 +139,27 @@ export const makeGetNotification = () => { export const getAccountGallery = createSelector([ (state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], ImmutableList()), state => state.get('statuses'), -], (statusIds, statuses) => { - let medias = ImmutableList(); + (state, id, mediaType) => mediaType, +], (statusIds, statuses, mediaType) => { + let medias = ImmutableList() - statusIds.forEach(statusId => { - const status = statuses.get(statusId); - medias = medias.concat(status.get('media_attachments').map(media => media.set('status', status))); - }); + statusIds.forEach((statusId) => { + const status = statuses.get(statusId) + medias = medias.concat( + status.get('media_attachments') + .filter((media) => { + if (mediaType === 'video') { + return media.get('type') === 'video' + } + + return media.get('type') !== 'video' + }) + .map((media) => media.set('status', status)) + ) + }) - return medias; -}); + return medias +}) export const getOrderedLists = createSelector([state => state.get('lists')], lists => { if (!lists) return lists