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