Progress
This commit is contained in:
parent
35852e7fee
commit
4d7aee59c9
|
@ -49,6 +49,7 @@ class AccountsController < ReactController
|
|||
statuses.merge!(hashtag_scope) if tag_requested?
|
||||
statuses.merge!(only_media_scope) if media_requested?
|
||||
statuses.merge!(no_replies_scope) unless replies_requested?
|
||||
statuses.merge!(only_replies_scope) unless comments_only_requested?
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -68,6 +69,10 @@ class AccountsController < ReactController
|
|||
Status.without_replies
|
||||
end
|
||||
|
||||
def only_replies_scope
|
||||
Status.only_replies
|
||||
end
|
||||
|
||||
def hashtag_scope
|
||||
tag = Tag.find_normalized(params[:tag])
|
||||
|
||||
|
@ -97,6 +102,8 @@ class AccountsController < ReactController
|
|||
short_account_media_url(@account, max_id: max_id, min_id: min_id)
|
||||
elsif replies_requested?
|
||||
short_account_with_replies_url(@account, max_id: max_id, min_id: min_id)
|
||||
elsif comments_only_requested?
|
||||
short_account_comments_only_url(@account, max_id: max_id, min_id: min_id)
|
||||
else
|
||||
short_account_url(@account, max_id: max_id, min_id: min_id)
|
||||
end
|
||||
|
@ -110,6 +117,10 @@ class AccountsController < ReactController
|
|||
request.path.ends_with?('/with_replies')
|
||||
end
|
||||
|
||||
def comments_only_requested?
|
||||
request.path.ends_with?('/comments_only')
|
||||
end
|
||||
|
||||
def tag_requested?
|
||||
request.path.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
|
||||
end
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class Api::V1::AccountByUsernameController < Api::BaseController
|
||||
before_action :set_account
|
||||
before_action :check_account_suspension
|
||||
before_action :check_account_local
|
||||
|
||||
respond_to :json
|
||||
|
||||
|
@ -17,4 +18,9 @@ class Api::V1::AccountByUsernameController < Api::BaseController
|
|||
def check_account_suspension
|
||||
gone if @account.suspended?
|
||||
end
|
||||
|
||||
# if not our domain don't display
|
||||
def check_account_local
|
||||
gone unless @account.local?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,6 +32,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
|
||||
statuses.merge!(only_media_scope) if truthy_param?(:only_media)
|
||||
statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
|
||||
statuses.merge!(only_replies_scope) if truthy_param?(:only_comments)
|
||||
statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
|
||||
statuses.merge!(hashtag_scope) if params[:tagged].present?
|
||||
|
||||
|
@ -64,6 +65,10 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
Status.without_replies
|
||||
end
|
||||
|
||||
def only_replies_scope
|
||||
Status.only_replies
|
||||
end
|
||||
|
||||
def no_reblogs_scope
|
||||
Status.without_reblogs
|
||||
end
|
||||
|
@ -79,7 +84,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params)
|
||||
params.slice(:limit, :only_media, :exclude_replies, :only_comments).permit(:limit, :only_media, :exclude_replies, :only_comments).merge(core_params)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -1,14 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::GifsController < Api::BaseController
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
skip_before_action :set_cache_headers
|
||||
|
||||
def index
|
||||
def categories
|
||||
uri = URI('https://api.tenor.com/v1/categories')
|
||||
params = { :key => "QHFJ0C5EWGBH" }
|
||||
uri.query = URI.encode_www_form(params)
|
||||
theOptions = { :key => "QHFJ0C5EWGBH" }
|
||||
uri.query = URI.encode_www_form(theOptions)
|
||||
|
||||
res = Net::HTTP.get_response(uri)
|
||||
render json: res.body if res.is_a?(Net::HTTPSuccess)
|
||||
end
|
||||
|
||||
def search
|
||||
uri = URI('https://api.tenor.com/v1/search')
|
||||
theOptions = {
|
||||
:key => "QHFJ0C5EWGBH",
|
||||
:media_filter => "minimal",
|
||||
:limit => 30,
|
||||
:q => params[:search],
|
||||
:pos => params[:next] || 0
|
||||
}
|
||||
uri.query = URI.encode_www_form(theOptions)
|
||||
|
||||
res = Net::HTTP.get_response(uri)
|
||||
render json: res.body if res.is_a?(Net::HTTPSuccess)
|
||||
|
|
|
@ -4,6 +4,7 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, except: [:clear, :dismiss, :mark_read]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: [:clear, :dismiss, :mark_read]
|
||||
before_action :require_user!
|
||||
before_action :set_filter_params
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
respond_to :json
|
||||
|
@ -49,7 +50,7 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
end
|
||||
|
||||
def browserable_account_notifications
|
||||
current_account.notifications.browserable(exclude_types, from_account)
|
||||
current_account.notifications.browserable(exclude_types, from_account, params[:only_verified], params[:only_following])
|
||||
end
|
||||
|
||||
def target_statuses_from_notifications
|
||||
|
@ -86,6 +87,13 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
val
|
||||
end
|
||||
|
||||
def set_filter_params
|
||||
params.permit(
|
||||
:only_verified,
|
||||
:only_following
|
||||
)
|
||||
end
|
||||
|
||||
def from_account
|
||||
params[:account_id]
|
||||
end
|
||||
|
|
|
@ -100,12 +100,11 @@ function getFromDB(dispatch, getState, index, id) {
|
|||
|
||||
export function fetchAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(fetchRelationships([id]));
|
||||
|
||||
if (id === -1 || getState().getIn(['accounts', id], null) !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchRelationships([id]));
|
||||
dispatch(fetchAccountRequest(id));
|
||||
|
||||
openDB().then(db => getFromDB(
|
||||
|
@ -128,8 +127,13 @@ export function fetchAccount(id) {
|
|||
|
||||
export function fetchAccountByUsername(username) {
|
||||
return (dispatch, getState) => {
|
||||
if (!username) {
|
||||
return;
|
||||
}
|
||||
|
||||
api(getState).get(`/api/v1/account_by_username/${username}`).then(response => {
|
||||
dispatch(importFetchedAccount(response.data));
|
||||
dispatch(importFetchedAccount(response.data))
|
||||
dispatch(fetchRelationships([response.data.id]))
|
||||
}).then(() => {
|
||||
dispatch(fetchAccountSuccess());
|
||||
}).catch(error => {
|
||||
|
|
|
@ -165,14 +165,15 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
|
|||
// filter verified and following here too
|
||||
const params = {
|
||||
max_id: maxId,
|
||||
// only_verified: onlyVerified,
|
||||
// only_following: onlyFollowing,
|
||||
exclude_types: activeFilter === 'all' ? null : excludeTypesFromFilter(activeFilter),
|
||||
// exclude_types: activeFilter === 'all'
|
||||
// ? excludeTypesFromSettings(getState())
|
||||
// : excludeTypesFromFilter(activeFilter),
|
||||
};
|
||||
|
||||
if (!!onlyVerified) params.only_verified = onlyVerified
|
||||
if (!!onlyFollowing) params.only_following = onlyFollowing
|
||||
|
||||
if (!maxId && notifications.get('items').size > 0) {
|
||||
params.since_id = notifications.getIn(['items', 0, 'id']);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export const fetchGifCategories = () => {
|
|||
|
||||
dispatch(fetchGifCategoriesRequest())
|
||||
|
||||
api(getState).get('/api/v1/gifs').then(response => {
|
||||
api(getState).get('/api/v1/gifs/categories').then(response => {
|
||||
dispatch(fetchGifCategoriesSuccess(response.data.tags))
|
||||
}).catch(function (error) {
|
||||
dispatch(fetchGifCategoriesFail(error))
|
||||
|
@ -29,24 +29,25 @@ export const fetchGifCategories = () => {
|
|||
}
|
||||
}
|
||||
|
||||
export const fetchGifResults = (maxId) => {
|
||||
export const fetchGifResults = (expand) => {
|
||||
return function (dispatch, getState) {
|
||||
if (!me) return
|
||||
|
||||
dispatch(fetchGifResultsRequest())
|
||||
|
||||
const searchText = getState().getIn(['tenor', 'searchText'], '');
|
||||
const search = getState().getIn(['tenor', 'searchText'], '');
|
||||
const pos = 0 //expand ? getState().getIn(['tenor', 'results'], []).length
|
||||
|
||||
axios.get(`https://api.tenor.com/v1/search?q=${searchText}&media_filter=minimal&limit=30&key=${tenorkey}`)
|
||||
.then((response) => {
|
||||
console.log('response:', response)
|
||||
dispatch(fetchGifResultsSuccess(response.data.results))
|
||||
}).catch(function (error) {
|
||||
dispatch(fetchGifResultsFail(error))
|
||||
})
|
||||
api(getState).get('/api/v1/gifs/search', { search, pos }).then((response) => {
|
||||
console.log("response.data:", response.data)
|
||||
dispatch(fetchGifResultsSuccess(response.data))
|
||||
}).catch(function (error) {
|
||||
dispatch(fetchGifResultsFail(error))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const clearGifResults = () => ({
|
||||
type: GIFS_CLEAR_RESULTS,
|
||||
})
|
||||
|
@ -69,10 +70,10 @@ function fetchGifResultsRequest() {
|
|||
}
|
||||
}
|
||||
|
||||
function fetchGifResultsSuccess(results) {
|
||||
function fetchGifResultsSuccess(data) {
|
||||
return {
|
||||
type: GIF_RESULTS_FETCH_SUCCESS,
|
||||
results,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
|
|||
|
||||
export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
|
||||
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
|
||||
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, 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 expandAccountMediaTimeline = (accountId, { maxId, limit } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: limit || 20 });
|
||||
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
|
||||
|
|
|
@ -136,6 +136,7 @@ export default class Button extends PureComponent {
|
|||
backgroundSubtle2Dark_onHover: backgroundColor === COLORS.tertiary || backgroundColor === COLORS.secondary,
|
||||
backgroundColorBlackOpaque_onHover: backgroundColor === COLORS.black,
|
||||
backgroundColorBrandDark_onHover: backgroundColor === COLORS.brand,
|
||||
backgroundColorDangerDark_onHover: backgroundColor === COLORS.danger,
|
||||
|
||||
backgroundColorBrand_onHover: color === COLORS.brand && outline,
|
||||
colorWhite_onHover: !!children && color === COLORS.brand && outline,
|
||||
|
|
|
@ -52,7 +52,7 @@ class Comment extends ImmutablePureComponent {
|
|||
// : todo : add media
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.px10, _s.mb10, _s.py5].join(' ')} data-comment={status.get('id')}>
|
||||
<div className={[_s.default, _s.px15, _s.mb10, _s.py5].join(' ')} data-comment={status.get('id')}>
|
||||
<div className={[_s.default].join(' ')} style={style}>
|
||||
|
||||
<div className={[_s.default, _s.flexRow].join(' ')}>
|
||||
|
|
|
@ -1,20 +1,30 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import Button from './button'
|
||||
import Comment from './comment'
|
||||
import Text from './text'
|
||||
|
||||
export default class CommentList extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
commentsLimited: PropTypes.bool,
|
||||
descendants: ImmutablePropTypes.list,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { descendants } = this.props
|
||||
const {
|
||||
descendants,
|
||||
commentsLimited,
|
||||
} = this.props
|
||||
|
||||
const size = descendants.size
|
||||
const max = Math.min(commentsLimited ? 2 : 6, size)
|
||||
console.log("max:", size, max)
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
descendants.map((descendant, i) => (
|
||||
descendants.slice(0, max).map((descendant, i) => (
|
||||
<Comment
|
||||
key={`comment-${descendant.get('statusId')}-${i}`}
|
||||
id={descendant.get('statusId')}
|
||||
|
@ -22,6 +32,27 @@ export default class CommentList extends ImmutablePureComponent {
|
|||
/>
|
||||
))
|
||||
}
|
||||
{
|
||||
size > 0 && size > max &&
|
||||
<div className={[_s.default, _s.flexRow, _s.px15, _s.pb5, _s.mb10, _s.alignItemsCenter].join(' ')}>
|
||||
<Button
|
||||
text
|
||||
backgroundColor='none'
|
||||
color='tertiary'
|
||||
>
|
||||
<Text weight='bold' color='inherit'>
|
||||
View more comments
|
||||
</Text>
|
||||
</Button>
|
||||
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
|
||||
<Text color='tertiary'>
|
||||
{max}
|
||||
of
|
||||
{size}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -33,29 +33,31 @@ class LoadMore extends PureComponent {
|
|||
const { disabled, visible, gap, intl } = this.props
|
||||
|
||||
return (
|
||||
<Button
|
||||
block
|
||||
radiusSmall
|
||||
backgroundColor='tertiary'
|
||||
color='primary'
|
||||
disabled={disabled || !visible}
|
||||
style={{ visibility: visible ? 'visible' : 'hidden' }}
|
||||
onClick={this.handleClick}
|
||||
aria-label={intl.formatMessage(messages.load_more)}
|
||||
>
|
||||
{
|
||||
!gap &&
|
||||
<Text color='inherit'>
|
||||
{intl.formatMessage(messages.load_more)}
|
||||
</Text>
|
||||
}
|
||||
{
|
||||
gap &&
|
||||
<Text align='center'>
|
||||
<Icon id='ellipsis' />
|
||||
</Text>
|
||||
}
|
||||
</Button>
|
||||
<div className={[_s.default, _s.py10, _s.px10].join(' ')}>
|
||||
<Button
|
||||
block
|
||||
radiusSmall
|
||||
backgroundColor='tertiary'
|
||||
color='primary'
|
||||
disabled={disabled || !visible}
|
||||
style={{ visibility: visible ? 'visible' : 'hidden' }}
|
||||
onClick={this.handleClick}
|
||||
aria-label={intl.formatMessage(messages.load_more)}
|
||||
>
|
||||
{
|
||||
!gap &&
|
||||
<Text color='inherit'>
|
||||
{intl.formatMessage(messages.load_more)}
|
||||
</Text>
|
||||
}
|
||||
{
|
||||
gap &&
|
||||
<Text align='center'>
|
||||
<Icon id='ellipsis' />
|
||||
</Text>
|
||||
}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ class GifPickerModal extends PureComponent {
|
|||
}
|
||||
|
||||
handleSelectGifResult = (resultId) => {
|
||||
|
||||
console.log("handleSelectGifResult:", resultId)
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import { injectIntl, defineMessages } from 'react-intl'
|
||||
import { muteAccount } from '../../actions/accounts'
|
||||
|
||||
const messages = defineMessages({
|
||||
muteMessage: { id: 'confirmations.mute.message', defaultMessage: 'Are you sure you want to mute {name}?' },
|
||||
cancel: { id: 'confirmation_modal.cancel', defaultMessage: 'Cancel' },
|
||||
confirm: { id: 'confirmations.mute.confirm', defaultMessage: 'Mute' },
|
||||
})
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
|
||||
account: state.getIn(['mutes', 'new', 'account']),
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onConfirm(account, notifications) {
|
||||
dispatch(muteAccount(account.get('id'), notifications))
|
||||
},
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
@injectIntl
|
||||
class UnfollowModal extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
isSubmitting: PropTypes.bool.isRequired,
|
||||
account: PropTypes.object.isRequired,
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.button.focus()
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
this.props.onClose()
|
||||
this.props.onConfirm(this.props.account, this.props.notifications)
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.props.onClose()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account, intl } = this.props
|
||||
|
||||
// , {
|
||||
// message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
|
||||
// confirm: intl.formatMessage(messages.unfollowConfirm),
|
||||
// onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
|
||||
// }));
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={`Mute @${account.get('acct')}`}
|
||||
message={<FormattedMessage id='confirmations.mute.message' defaultMessage='Are you sure you want to mute @{name}?' values={{ name: account.get('acct') }} />}
|
||||
confirm={<FormattedMessage id='mute' defaultMessage='Mute' />}
|
||||
onConfirm={() => {
|
||||
// dispatch(blockDomain(domain))
|
||||
// dispatch(blockDomain(domain))
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import axios from 'axios'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
|
||||
import Sticky from 'react-stickynode'
|
||||
import classNames from 'classnames/bind'
|
||||
import {
|
||||
followAccount,
|
||||
|
@ -26,9 +26,11 @@ const cx = classNames.bind(_s)
|
|||
|
||||
const messages = defineMessages({
|
||||
follow: { id: 'follow', defaultMessage: 'Follow' },
|
||||
following: { id: 'following', defaultMessage: 'Following' },
|
||||
unfollow: { id: 'unfollow', defaultMessage: 'Unfollow' },
|
||||
requested: { id: 'requested', defaultMessage: 'Requested' },
|
||||
unblock: { id: 'unblock', defaultMessage: 'Unblock' },
|
||||
blocked: { id: 'account.blocked', defaultMessage: 'Blocked' },
|
||||
followers: { id: 'account.followers', defaultMessage: 'Followers' },
|
||||
follows: { id: 'account.follows', defaultMessage: 'Follows' },
|
||||
profile: { id: 'account.profile', defaultMessage: 'Profile' },
|
||||
|
@ -92,6 +94,10 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
openProfileOptionsPopover: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
stickied: false,
|
||||
}
|
||||
|
||||
handleOpenMore = () => {
|
||||
const { openProfileOptionsPopover, account } = this.props
|
||||
openProfileOptionsPopover({
|
||||
|
@ -110,7 +116,7 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
// : todo :
|
||||
makeInfo() {
|
||||
makeInfo = () => {
|
||||
const { account, intl } = this.props
|
||||
|
||||
const info = []
|
||||
|
@ -132,12 +138,24 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
return info
|
||||
}
|
||||
|
||||
onStickyStateChange = (status) => {
|
||||
switch (status.status) {
|
||||
case Sticky.STATUS_FIXED:
|
||||
this.setState({ stickied: true })
|
||||
break;
|
||||
default:
|
||||
this.setState({ stickied: false })
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setOpenMoreNodeRef = (n) => {
|
||||
this.openMoreNode = n
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account, intl } = this.props
|
||||
const { stickied } = this.state
|
||||
|
||||
const tabs = !account ? null : [
|
||||
{
|
||||
|
@ -160,6 +178,60 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
|
||||
const headerSrc = !!account ? account.get('header') : ''
|
||||
const headerMissing = headerSrc.indexOf('/headers/original/missing.png') > -1 || !headerSrc
|
||||
const avatarSize = headerMissing ? '75' : '150'
|
||||
|
||||
let buttonText = ''
|
||||
let buttonOptions = {}
|
||||
|
||||
if (!account) {
|
||||
//
|
||||
} else {
|
||||
if (!account.get('relationship')) {
|
||||
// Wait until the relationship is loaded
|
||||
} else {
|
||||
const isRequested = account.getIn(['relationship', 'requested'])
|
||||
const isBlocking = account.getIn(['relationship', 'blocking'])
|
||||
const isFollowing = account.getIn(['relationship', 'following'])
|
||||
const isBlockedBy = account.getIn(['relationship', 'blocked_by'])
|
||||
|
||||
if (isRequested) {
|
||||
buttonText = intl.formatMessage(messages.requested)
|
||||
buttonOptions = {
|
||||
onClick: this.handleFollow,
|
||||
color: 'primary',
|
||||
backgroundColor: 'tertiary',
|
||||
}
|
||||
} else if (isBlocking) {
|
||||
buttonText = intl.formatMessage(messages.blocked)
|
||||
buttonOptions = {
|
||||
onClick: this.handleBlock,
|
||||
color: 'white',
|
||||
backgroundColor: 'danger',
|
||||
}
|
||||
} else if (isFollowing) {
|
||||
buttonText = intl.formatMessage(messages.following)
|
||||
buttonOptions = {
|
||||
onClick: this.handleFollow,
|
||||
color: 'white',
|
||||
backgroundColor: 'brand',
|
||||
}
|
||||
} else if (isBlockedBy) {
|
||||
//Don't show
|
||||
}
|
||||
else {
|
||||
buttonText = intl.formatMessage(messages.follow)
|
||||
buttonOptions = {
|
||||
onClick: this.handleFollow,
|
||||
color: 'brand',
|
||||
backgroundColor: 'none',
|
||||
outline: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('buttonOptions:', buttonText, buttonOptions)
|
||||
console.log('account: ', account)
|
||||
|
||||
const avatarContainerClasses = cx({
|
||||
circle: 1,
|
||||
|
@ -168,78 +240,18 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
border2PX: 1,
|
||||
})
|
||||
|
||||
const avatarSize = headerMissing ? '75' : '150'
|
||||
const stickyBarContainerClasses = cx({
|
||||
default: 1,
|
||||
flexRow: 1,
|
||||
px15: 1,
|
||||
alignItemsCenter: 1,
|
||||
displayNone: !stickied,
|
||||
})
|
||||
|
||||
let buttonText = ''
|
||||
let buttonOptions = {}
|
||||
|
||||
if (!account) {
|
||||
console.log('no account')
|
||||
} else {
|
||||
if (!account.get('relationship')) {
|
||||
console.log('no relationship')
|
||||
// Wait until the relationship is loaded
|
||||
} else if (account.getIn(['relationship', 'requested'])) {
|
||||
buttonText = intl.formatMessage(messages.requested)
|
||||
buttonOptions = {
|
||||
narrow: true,
|
||||
onClick: this.handleFollow,
|
||||
color: 'primary',
|
||||
backgroundColor: 'tertiary',
|
||||
}
|
||||
} else if (!account.getIn(['relationship', 'blocking'])) {
|
||||
const isFollowing = account.getIn(['relationship', 'following'])
|
||||
const isBlockedBy = account.getIn(['relationship', 'blocked_by'])
|
||||
buttonText = intl.formatMessage(isFollowing ? messages.unfollow : messages.follow)
|
||||
buttonOptions = {
|
||||
narrow: true,
|
||||
onClick: this.handleFollow,
|
||||
color: 'primary',
|
||||
backgroundColor: 'tertiary',
|
||||
disabled: isBlockedBy,
|
||||
}
|
||||
} else if (account.getIn(['relationship', 'blocking'])) {
|
||||
buttonText = intl.formatMessage(messages.unblock)
|
||||
buttonOptions = {
|
||||
narrow: true,
|
||||
onClick: this.handleBlock,
|
||||
color: 'primary',
|
||||
backgroundColor: 'tertiary',
|
||||
}
|
||||
} else {
|
||||
console.log('no nothin')
|
||||
}
|
||||
|
||||
// if (account.get('id') !== me && account.get('relationship', null) !== null) {
|
||||
// const following = account.getIn(['relationship', 'following'])
|
||||
// const requested = account.getIn(['relationship', 'requested'])
|
||||
// const blocking = account.getIn(['relationship', 'blocking'])
|
||||
|
||||
// if (requested || blocking) {
|
||||
// buttonText = intl.formatMessage(requested ? messages.requested : messages.unblock)
|
||||
// buttonOptions = {
|
||||
// narrow: true,
|
||||
// onClick: requested ? this.handleFollow : this.handleBlock,
|
||||
// color: 'primary',
|
||||
// backgroundColor: 'tertiary',
|
||||
// }
|
||||
// } else if (!account.get('moved') || following) {
|
||||
// buttonOptions = {
|
||||
// narrow: true,
|
||||
// outline: !following,
|
||||
// color: !following ? 'brand' : 'white',
|
||||
// backgroundColor: !following ? 'none' : 'brand',
|
||||
// onClick: this.handleFollow,
|
||||
// }
|
||||
// buttonText = intl.formatMessage(following ? messages.unfollow : messages.follow)
|
||||
// } else {
|
||||
// console.log("SHOW ELSE")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
console.log('buttonOptions:', buttonText, buttonOptions)
|
||||
console.log('account: ', account)
|
||||
const tabBarContainerClasses = cx({
|
||||
default: 1,
|
||||
displayNone: stickied,
|
||||
})
|
||||
|
||||
|
||||
// : todo : "follows you", "mutual follow"
|
||||
|
@ -260,7 +272,7 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
|
||||
<div className={[_s.default, _s.borderBottom1PX, _s.borderColorSecondary, _s.width100PC].join(' ')}>
|
||||
|
||||
<div className={[_s.default, _s.flexRow, _s.px15].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.px15, _s.mb5].join(' ')}>
|
||||
<div className={avatarContainerClasses}>
|
||||
<Avatar size={avatarSize} account={account} />
|
||||
</div>
|
||||
|
@ -271,87 +283,103 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
account && account.get('locked') &&
|
||||
<Icon id='lock-filled' height='14px' width='14px' className={[_s.mt10, _s.ml10].join(' ')} />
|
||||
}
|
||||
{
|
||||
/* : todo :
|
||||
account.getIn(['relationship', 'muting'])
|
||||
*/
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.flexRow, _s.borderBottom1PX, _s.borderColorSecondary, _s.mt5, _s.height53PX].join(' ')}>
|
||||
<div className={[_s.default].join(' ')}>
|
||||
<TabBar tabs={tabs} large />
|
||||
</div>
|
||||
|
||||
{
|
||||
account && account.get('id') === me &&
|
||||
<div className={[_s.default, _s.flexRow, _s.marginLeftAuto, _s.py5].join(' ')}>
|
||||
<Button
|
||||
outline
|
||||
backgroundColor='none'
|
||||
color='brand'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
|
||||
href=''
|
||||
>
|
||||
<Text
|
||||
color='inherit'
|
||||
weight='bold'
|
||||
size='medium'
|
||||
className={[_s.px15].join(' ')}
|
||||
>
|
||||
Edit Profile
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
<Sticky enabled onStateChange={this.onStickyStateChange}>
|
||||
<div className={[_s.default, _s.flexRow, _s.backgroundColorSecondary3, _s.borderBottom1PX, _s.borderColorSecondary, _s.height53PX].join(' ')}>
|
||||
<div className={tabBarContainerClasses}>
|
||||
<TabBar tabs={tabs} large />
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
account && account.get('id') !== me &&
|
||||
<div className={[_s.default, _s.flexRow, _s.marginLeftAuto, _s.py5].join(' ')}>
|
||||
<div ref={this.setOpenMoreNodeRef}>
|
||||
<Button
|
||||
outline
|
||||
icon='ellipsis'
|
||||
iconWidth='18px'
|
||||
iconHeight='18px'
|
||||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
||||
onClick={this.handleOpenMore}
|
||||
/>
|
||||
<div className={stickyBarContainerClasses}>
|
||||
<Avatar size={36} account={account} />
|
||||
<div className={[_s.default, _s.ml10].join(' ')}>
|
||||
<DisplayName account={account} noUsername large />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action='https://chat.gab.com/private-message' method='POST'>
|
||||
{
|
||||
account && account.get('id') === me &&
|
||||
<div className={[_s.default, _s.flexRow, _s.marginLeftAuto, _s.py5].join(' ')}>
|
||||
<Button
|
||||
type='submit'
|
||||
outline
|
||||
icon='chat'
|
||||
iconWidth='18px'
|
||||
iconHeight='18px'
|
||||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
||||
/>
|
||||
<input type='hidden' value={account.get('username')} name='username' />
|
||||
</form>
|
||||
|
||||
<Button
|
||||
{...buttonOptions}
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
|
||||
>
|
||||
<span className={[_s.px15].join(' ')}>
|
||||
color='brand'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
|
||||
href=''
|
||||
>
|
||||
<Text
|
||||
color='inherit'
|
||||
weight='bold'
|
||||
size='medium'
|
||||
className={[_s.px15].join(' ')}
|
||||
>
|
||||
{buttonText}
|
||||
Edit Profile
|
||||
</Text>
|
||||
</span>
|
||||
</Button>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
{
|
||||
account && account.get('id') !== me &&
|
||||
<div className={[_s.default, _s.flexRow, _s.marginLeftAuto, _s.py5].join(' ')}>
|
||||
<div ref={this.setOpenMoreNodeRef}>
|
||||
<Button
|
||||
outline
|
||||
icon='ellipsis'
|
||||
iconWidth='18px'
|
||||
iconHeight='18px'
|
||||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
||||
onClick={this.handleOpenMore}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<form action='https://chat.gab.com/private-message' method='POST'>
|
||||
<Button
|
||||
type='submit'
|
||||
outline
|
||||
icon='chat'
|
||||
iconWidth='18px'
|
||||
iconHeight='18px'
|
||||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
||||
/>
|
||||
<input type='hidden' value={account.get('username')} name='username' />
|
||||
</form>
|
||||
|
||||
{
|
||||
!!buttonText &&
|
||||
<Button
|
||||
{...buttonOptions}
|
||||
narrow
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
|
||||
>
|
||||
<Text
|
||||
color='inherit'
|
||||
weight='bold'
|
||||
size='medium'
|
||||
className={_s.px10}
|
||||
>
|
||||
{buttonText}
|
||||
</Text>
|
||||
</Button>
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Sticky>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -106,8 +106,6 @@ class Sidebar extends ImmutablePureComponent {
|
|||
const acct = account.get('acct')
|
||||
const isPro = account.get('is_pro')
|
||||
|
||||
console.log('showCommunityTimeline:', showCommunityTimeline)
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
title: 'Home',
|
||||
|
|
|
@ -84,7 +84,6 @@ class Status extends ImmutablePureComponent {
|
|||
hidden: PropTypes.bool,
|
||||
onMoveUp: PropTypes.func,
|
||||
onMoveDown: PropTypes.func,
|
||||
showThread: PropTypes.bool,
|
||||
getScrollPosition: PropTypes.func,
|
||||
updateScrollBottom: PropTypes.func,
|
||||
cacheMediaWidth: PropTypes.func,
|
||||
|
@ -93,7 +92,6 @@ class Status extends ImmutablePureComponent {
|
|||
promoted: PropTypes.bool,
|
||||
onOpenProUpgradeModal: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
borderless: PropTypes.bool,
|
||||
isChild: PropTypes.bool,
|
||||
}
|
||||
|
||||
|
@ -267,10 +265,8 @@ class Status extends ImmutablePureComponent {
|
|||
intl,
|
||||
hidden,
|
||||
featured,
|
||||
showThread,
|
||||
group,
|
||||
promoted,
|
||||
borderless,
|
||||
isChild,
|
||||
} = this.props
|
||||
|
||||
|
@ -386,8 +382,8 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
const containerClasses = cx({
|
||||
default: 1,
|
||||
radiusSmall: !borderless && !isChild,
|
||||
// mb15: !borderless && !isChild,
|
||||
radiusSmall: !isChild,
|
||||
// mb15: !isChild,
|
||||
// backgroundColorPrimary: 1,
|
||||
pb15: featured,
|
||||
borderBottom1PX: featured && !isChild,
|
||||
|
@ -397,9 +393,9 @@ class Status extends ImmutablePureComponent {
|
|||
const innerContainerClasses = cx({
|
||||
default: 1,
|
||||
overflowHidden: 1,
|
||||
radiusSmall: !borderless,
|
||||
borderColorSecondary: !borderless,
|
||||
border1PX: !borderless,
|
||||
radiusSmall: isChild,
|
||||
borderColorSecondary: isChild,
|
||||
border1PX: isChild,
|
||||
pb10: isChild && status.get('media_attachments').size === 0,
|
||||
pb5: isChild && status.get('media_attachments').size > 1,
|
||||
cursorPointer: isChild,
|
||||
|
@ -409,7 +405,7 @@ class Status extends ImmutablePureComponent {
|
|||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div
|
||||
className={containerClasses}
|
||||
className={containerClasses}
|
||||
tabIndex={this.props.muted ? null : 0}
|
||||
data-featured={featured ? 'true' : null}
|
||||
aria-label={textForScreenReader(intl, status, rebloggedByText)}
|
||||
|
|
|
@ -105,14 +105,6 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
handleQuoteClick = () => {
|
||||
if (me) {
|
||||
this.props.onQuote(this.props.status, this.context.router.history)
|
||||
} else {
|
||||
this.props.onOpenUnauthorizedModal()
|
||||
}
|
||||
}
|
||||
|
||||
handleFavoriteClick = () => {
|
||||
if (me) {
|
||||
this.props.onFavorite(this.props.status)
|
||||
|
@ -123,17 +115,29 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
handleRepostClick = e => {
|
||||
if (me) {
|
||||
this.props.onRepost(this.props.status, e)
|
||||
// this.props.onRepost(this.props.status, e)
|
||||
this.props.onQuote(this.props.status, this.context.router.history)
|
||||
} else {
|
||||
this.props.onOpenUnauthorizedModal()
|
||||
}
|
||||
}
|
||||
|
||||
handleShareClick = () => {
|
||||
console.log("handleShareClick:", this.shareButton, this.props.status)
|
||||
this.props.onOpenStatusSharePopover(this.shareButton, this.props.status)
|
||||
}
|
||||
|
||||
openLikesList = () => {
|
||||
|
||||
}
|
||||
|
||||
toggleCommentsVisible = () => {
|
||||
|
||||
}
|
||||
|
||||
openRepostsList = () => {
|
||||
|
||||
}
|
||||
|
||||
setShareButton = (n) => {
|
||||
this.shareButton = n
|
||||
}
|
||||
|
@ -176,6 +180,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
text: 1,
|
||||
cursorPointer: 1,
|
||||
fontWeightNormal: 1,
|
||||
underline_onHover: 1,
|
||||
mr10: 1,
|
||||
py5: 1,
|
||||
})
|
||||
|
@ -187,7 +192,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
<div className={[_s.default, _s.flexRow, _s.px5].join(' ')}>
|
||||
{
|
||||
favoriteCount > 0 &&
|
||||
<button className={interactionBtnClasses}>
|
||||
<button className={interactionBtnClasses} onClick={this.openLikesList}>
|
||||
<Text color='secondary' size='small'>
|
||||
{formatMessage(messages.likesLabel, {
|
||||
number: favoriteCount,
|
||||
|
@ -197,7 +202,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
}
|
||||
{
|
||||
replyCount > 0 &&
|
||||
<button className={interactionBtnClasses}>
|
||||
<button className={interactionBtnClasses} onClick={this.toggleCommentsVisible}>
|
||||
<Text color='secondary' size='small'>
|
||||
{formatMessage(messages.commentsLabel, {
|
||||
number: replyCount,
|
||||
|
@ -207,7 +212,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
}
|
||||
{
|
||||
repostCount > 0 &&
|
||||
<button className={interactionBtnClasses}>
|
||||
<button className={interactionBtnClasses} onClick={this.openRepostsList}>
|
||||
<Text color='secondary' size='small'>
|
||||
{formatMessage(messages.repostsLabel, {
|
||||
number: repostCount,
|
||||
|
|
|
@ -38,7 +38,9 @@ const makeGetStatusIds = () => createSelector([
|
|||
});
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, {timelineId}) => {
|
||||
const mapStateToProps = (state, { timelineId }) => {
|
||||
if (!timelineId) return {}
|
||||
|
||||
const getStatusIds = makeGetStatusIds();
|
||||
const promotion = promotions.length > 0 && sample(promotions.filter(p => p.timeline_id === timelineId));
|
||||
|
||||
|
@ -181,15 +183,15 @@ class StatusList extends ImmutablePureComponent {
|
|||
contextType={timelineId}
|
||||
group={group}
|
||||
withGroupAdmin={withGroupAdmin}
|
||||
showThread
|
||||
commentsLimited
|
||||
/>
|
||||
{
|
||||
{ /* : todo : */
|
||||
promotedStatus && index === promotion.position &&
|
||||
<Status
|
||||
id={promotion.status_id}
|
||||
contextType={timelineId}
|
||||
promoted
|
||||
showThread
|
||||
commentsLimited
|
||||
/>
|
||||
}
|
||||
</Fragment>
|
||||
|
@ -205,7 +207,7 @@ class StatusList extends ImmutablePureComponent {
|
|||
onMoveUp={this.handleMoveUp}
|
||||
onMoveDown={this.handleMoveDown}
|
||||
contextType={timelineId}
|
||||
showThread
|
||||
commentsLimited
|
||||
/>
|
||||
)).concat(scrollableContent)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ const cx = classNames.bind(_s)
|
|||
const COLORS = {
|
||||
primary: 'primary',
|
||||
secondary: 'secondary',
|
||||
tertiary: 'tertiary',
|
||||
brand: 'brand',
|
||||
error: 'error',
|
||||
white: 'white',
|
||||
|
@ -78,6 +79,7 @@ export default class Text extends PureComponent {
|
|||
|
||||
colorPrimary: color === COLORS.primary,
|
||||
colorSecondary: color === COLORS.secondary,
|
||||
colorTertiary: color === COLORS.tertiary,
|
||||
colorBrand: color === COLORS.brand,
|
||||
colorWhite: color === COLORS.white,
|
||||
colorGabPro: color === COLORS.pro,
|
||||
|
|
|
@ -12,15 +12,17 @@ const messages = defineMessages({
|
|||
|
||||
const emptyList = ImmutableList()
|
||||
|
||||
const mapStateToProps = (state, { account, withReplies = false }) => {
|
||||
const mapStateToProps = (state, { account, commentsOnly = false }) => {
|
||||
const accountId = !!account ? account.getIn(['id'], null) : -1
|
||||
|
||||
const path = withReplies ? `${accountId}:with_replies` : accountId
|
||||
const path = commentsOnly ? `${accountId}:comments_only` : accountId
|
||||
|
||||
console.log("commentsOnly, path:", commentsOnly, path)
|
||||
|
||||
return {
|
||||
accountId,
|
||||
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
|
||||
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
|
||||
featuredStatusIds: commentsOnly ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
|
||||
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
||||
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
|
||||
}
|
||||
|
@ -38,33 +40,33 @@ class AccountTimeline extends ImmutablePureComponent {
|
|||
featuredStatusIds: ImmutablePropTypes.list,
|
||||
isLoading: PropTypes.bool,
|
||||
hasMore: PropTypes.bool,
|
||||
withReplies: PropTypes.bool,
|
||||
commentsOnly: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { accountId, withReplies } = this.props
|
||||
const { accountId, commentsOnly } = this.props
|
||||
|
||||
if (accountId && accountId !== -1) {
|
||||
this.props.dispatch(fetchAccountIdentityProofs(accountId))
|
||||
|
||||
if (!withReplies) {
|
||||
if (!commentsOnly) {
|
||||
this.props.dispatch(expandAccountFeaturedTimeline(accountId))
|
||||
}
|
||||
|
||||
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }))
|
||||
this.props.dispatch(expandAccountTimeline(accountId, { commentsOnly }))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId) || nextProps.withReplies !== this.props.withReplies) {
|
||||
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId) || nextProps.commentsOnly !== this.props.commentsOnly) {
|
||||
this.props.dispatch(fetchAccountIdentityProofs(nextProps.accountId))
|
||||
|
||||
if (!nextProps.withReplies) {
|
||||
if (!nextProps.commentsOnly) {
|
||||
this.props.dispatch(expandAccountFeaturedTimeline(nextProps.accountId))
|
||||
}
|
||||
|
||||
this.props.dispatch(expandAccountTimeline(nextProps.accountId, { withReplies: nextProps.withReplies }))
|
||||
this.props.dispatch(expandAccountTimeline(nextProps.accountId, { commentsOnly: nextProps.commentsOnly }))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +74,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
|||
if (this.props.accountId && this.props.accountId !== -1) {
|
||||
this.props.dispatch(expandAccountTimeline(this.props.accountId, {
|
||||
maxId,
|
||||
withReplies: this.props.withReplies
|
||||
commentsOnly: this.props.commentsOnly
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +91,8 @@ class AccountTimeline extends ImmutablePureComponent {
|
|||
|
||||
if (!account) return null
|
||||
|
||||
console.log("statusIds:", statusIds)
|
||||
|
||||
return (
|
||||
<StatusList
|
||||
scrollKey='account_timeline'
|
||||
|
|
|
@ -193,8 +193,8 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
selectionStart = selectionEnd;
|
||||
}
|
||||
|
||||
this.autosuggestTextarea.textbox.setSelectionRange(selectionStart, selectionEnd);
|
||||
this.autosuggestTextarea.textbox.focus();
|
||||
// this.autosuggestTextarea.textbox.setSelectionRange(selectionStart, selectionEnd);
|
||||
// this.autosuggestTextarea.textbox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,6 +332,14 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
</div>
|
||||
}
|
||||
|
||||
{ /*
|
||||
(isUploading || hasGif) &&
|
||||
<div className={[_s.default, _s.px15].join(' ')}>
|
||||
<UploadForm replyToId={replyToId} />
|
||||
</div>
|
||||
*/
|
||||
}
|
||||
|
||||
{
|
||||
!edit && hasPoll &&
|
||||
<div className={[_s.default, _s.px15].join(' ')}>
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ProgressBar from '../../../../components/progress_bar'
|
||||
import Upload from '../media_upload_item'
|
||||
import SensitiveMediaButton from '../sensitive_media_button'
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
mediaIds: state.getIn(['compose', 'media_attachments']).map(item => item.get('id')),
|
||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||
uploadProgress: state.getIn(['compose', 'progress']),
|
||||
});
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
class GifForm extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
mediaIds: ImmutablePropTypes.list.isRequired,
|
||||
isUploading: PropTypes.bool,
|
||||
uploadProgress: PropTypes.number,
|
||||
};
|
||||
|
||||
render () {
|
||||
const {
|
||||
mediaIds,
|
||||
isUploading,
|
||||
uploadProgress,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<div className={_s.default}>
|
||||
<div className={[_s.default, _s.flexRow, _s.flexWrap].join(' ')}>
|
||||
<Upload id={id} key={id} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ProgressBar from '../../../../components/progress_bar'
|
||||
import Upload from '../media_upload_item'
|
||||
import SensitiveMediaButton from '../sensitive_media_button'
|
||||
import ProgressBar from '../../../components/progress_bar'
|
||||
import Upload from './media_upload_item'
|
||||
import SensitiveMediaButton from './sensitive_media_button'
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
mediaIds: state.getIn(['compose', 'media_attachments']).map(item => item.get('id')),
|
|
@ -1 +0,0 @@
|
|||
export { default } from './upload_form'
|
|
@ -1,10 +0,0 @@
|
|||
.compose-form-upload-wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.compose-form-uploads-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 5px;
|
||||
flex-wrap: wrap;
|
||||
}
|
|
@ -111,7 +111,6 @@ const makeMapStateToProps = () => {
|
|||
ancestorsIds,
|
||||
descendantsIds,
|
||||
askReplyConfirmation: state.getIn(['compose', 'text']).trim().length !== 0,
|
||||
domain: state.getIn(['meta', 'domain']),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -135,7 +134,7 @@ class Status extends ImmutablePureComponent {
|
|||
descendantsIds: ImmutablePropTypes.list,
|
||||
intl: PropTypes.object.isRequired,
|
||||
askReplyConfirmation: PropTypes.bool,
|
||||
domain: PropTypes.string.isRequired,
|
||||
contextType: PropTypes.string,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -412,7 +411,8 @@ class Status extends ImmutablePureComponent {
|
|||
ancestorsIds,
|
||||
descendantsIds,
|
||||
intl,
|
||||
domain
|
||||
contextType,
|
||||
commentsLimited,
|
||||
} = this.props
|
||||
|
||||
let ancestors, descendants
|
||||
|
@ -441,11 +441,13 @@ class Status extends ImmutablePureComponent {
|
|||
toggleSensitive: this.handleHotkeyToggleSensitive,
|
||||
};
|
||||
|
||||
console.log("descendantsIds.size > 0:", descendantsIds.size > 0)
|
||||
|
||||
return (
|
||||
<div ref={this.setRef} className={_s.mb15}>
|
||||
<Block>
|
||||
{
|
||||
/* ancestors */
|
||||
/* : todo : ancestors if is comment */
|
||||
}
|
||||
|
||||
<HotKeys handlers={handlers}>
|
||||
|
@ -453,13 +455,10 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
<StatusContainer
|
||||
id={status.get('id')}
|
||||
contextType={'timelineId'}
|
||||
showThread
|
||||
borderless={descendantsIds && descendantsIds.size > 0}
|
||||
contextType={contextType}
|
||||
// onOpenVideo={this.handleOpenVideo}
|
||||
// onOpenMedia={this.handleOpenMedia}
|
||||
// onToggleHidden={this.handleToggleHidden}
|
||||
// domain={domain}
|
||||
// showMedia={this.state.showMedia}
|
||||
// onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||
/>
|
||||
|
@ -472,7 +471,10 @@ class Status extends ImmutablePureComponent {
|
|||
<div className={[_s.default, _s.mr10, _s.ml10, _s.mb10, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}/>
|
||||
}
|
||||
|
||||
<CommentList descendants={descendantsIds} />
|
||||
<CommentList
|
||||
commentsLimited={commentsLimited}
|
||||
descendants={descendantsIds}
|
||||
/>
|
||||
</Block>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -26,13 +26,12 @@ export default class ProfileLayout extends ImmutablePureComponent {
|
|||
noHeader
|
||||
noComposeButton
|
||||
>
|
||||
|
||||
<div className={[_s.default, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween, _s.pl15, _s.py15].join(' ')}>
|
||||
<div className={[_s.default, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween, _s.pr15].join(' ')}>
|
||||
<div className={[_s.default, _s.z1, _s.width100PC].join(' ')}>
|
||||
|
||||
<ProfileHeader account={account} />
|
||||
|
||||
<div className={[_s.default, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween, _s.pr15, _s.py15].join(' ')}>
|
||||
<div className={[_s.default, _s.width100PC, _s.flexRow, _s.justifyContentSpaceBetween, _s.py15].join(' ')}>
|
||||
<div className={[_s.default, _s.width645PX, _s.z1].join(' ')}>
|
||||
<div className={_s.default}>
|
||||
{children}
|
||||
|
@ -40,7 +39,7 @@ export default class ProfileLayout extends ImmutablePureComponent {
|
|||
</div>
|
||||
|
||||
<div className={[_s.default, _s.width340PX].join(' ')}>
|
||||
<Sticky top={15} enabled>
|
||||
<Sticky top={63} enabled>
|
||||
<div className={[_s.default, _s.width340PX].join(' ')}>
|
||||
{layout}
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Fragment } from 'react'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import queryString from 'query-string'
|
||||
import { setFilter } from '../actions/notifications'
|
||||
import PageTitle from '../features/ui/util/page_title'
|
||||
import LinkFooter from '../components/link_footer'
|
||||
|
@ -8,13 +9,22 @@ import NotificationFilterPanel from '../components/panel/notification_filter_pan
|
|||
import TrendsPanel from '../components/panel/trends_panel'
|
||||
import DefaultLayout from '../layouts/default_layout'
|
||||
|
||||
const filters = [
|
||||
'all',
|
||||
'mention',
|
||||
'favourite',
|
||||
'reblog',
|
||||
'poll',
|
||||
'follow',
|
||||
]
|
||||
|
||||
const messages = defineMessages({
|
||||
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
||||
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
||||
likes: { id: 'likes', defaultMessage: 'Likes' },
|
||||
reposts: { id: 'reposts', defaultMessage: 'Reposts' },
|
||||
polls: { id: 'polls', defaultMessage: 'Poll' },
|
||||
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
||||
mention: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
||||
favourite: { id: 'likes', defaultMessage: 'Likes' },
|
||||
reblog: { id: 'reposts', defaultMessage: 'Reposts' },
|
||||
poll: { id: 'polls', defaultMessage: 'Poll' },
|
||||
follow: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
||||
all: { id: 'notifications.filter.all', defaultMessage: 'All' },
|
||||
})
|
||||
|
||||
|
@ -39,16 +49,31 @@ class NotificationsPage extends PureComponent {
|
|||
}
|
||||
|
||||
static propTypes = {
|
||||
setFilter: PropTypes.func.isRequired,
|
||||
selectedFilter: PropTypes.string.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
notificationCount: PropTypes.number.isRequired,
|
||||
setFilter: PropTypes.func.isRequired,
|
||||
selectedFilter: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
// : todo : on pop change filter active type
|
||||
componentDidMount() {
|
||||
this.checkForQueryStringChange(this.context.router.route.location)
|
||||
}
|
||||
|
||||
onClick(notificationType) {
|
||||
this.props.setFilter('active', notificationType)
|
||||
checkForQueryStringChange = (location) => {
|
||||
try {
|
||||
const qp = queryString.parse(location.search)
|
||||
const view = `${qp.view}`.toLowerCase()
|
||||
if (filters.indexOf(view) > -1) {
|
||||
this.onChangeActiveFilter(view)
|
||||
}
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
onChangeActiveFilter(notificationType) {
|
||||
this.props.setFilter(notificationType)
|
||||
|
||||
if (notificationType === 'all') {
|
||||
this.context.router.history.push('/notifications')
|
||||
|
@ -59,44 +84,17 @@ class NotificationsPage extends PureComponent {
|
|||
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
children,
|
||||
intl,
|
||||
notificationCount,
|
||||
selectedFilter,
|
||||
notificationCount
|
||||
} = this.props
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
title: intl.formatMessage(messages.all),
|
||||
onClick: () => this.onClick('all'),
|
||||
active: selectedFilter === 'all',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage(messages.mentions),
|
||||
onClick: () => this.onClick('mention'),
|
||||
active: selectedFilter === 'mention',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage(messages.likes),
|
||||
onClick: () => this.onClick('favourite'),
|
||||
active: selectedFilter === 'favourite',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage(messages.reposts),
|
||||
onClick: () => this.onClick('reblog'),
|
||||
active: selectedFilter === 'reblog',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage(messages.polls),
|
||||
onClick: () => this.onClick('poll'),
|
||||
active: selectedFilter === 'poll',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage(messages.follows),
|
||||
onClick: () => this.onClick('follow'),
|
||||
active: selectedFilter === 'follow',
|
||||
},
|
||||
]
|
||||
const tabs = filters.map((filter) => ({
|
||||
title: intl.formatMessage(messages[filter]),
|
||||
onClick: () => this.onChangeActiveFilter(filter),
|
||||
active: selectedFilter === filter,
|
||||
}))
|
||||
|
||||
return (
|
||||
<DefaultLayout
|
||||
|
|
|
@ -61,7 +61,8 @@ class ProfilePage extends ImmutablePureComponent {
|
|||
params: { username },
|
||||
} = this.props
|
||||
|
||||
const title = !!account ? account.get('display_name') : username
|
||||
const name = !!account ? account.get('display_name_html') : ''
|
||||
console.log("name:", name, account)
|
||||
|
||||
return (
|
||||
<ProfileLayout
|
||||
|
@ -75,7 +76,7 @@ class ProfilePage extends ImmutablePureComponent {
|
|||
</Fragment>
|
||||
)}
|
||||
>
|
||||
<PageTitle path={title} />
|
||||
<PageTitle path={`${name} (@${username})`} />
|
||||
{
|
||||
!account && <ColumnIndicator type='loading' />
|
||||
}
|
||||
|
|
|
@ -224,6 +224,7 @@ export default function notifications(state = initialState, action) {
|
|||
case NOTIFICATIONS_FILTER_SET:
|
||||
return state.withMutations(mutable => {
|
||||
mutable.set('items', ImmutableList()).set('hasMore', true)
|
||||
console.log("NOTIFICATIONS_FILTER_SET:", action.path, action.value)
|
||||
mutable.setIn(['filter', action.path], action.value)
|
||||
})
|
||||
case NOTIFICATIONS_SCROLL_TOP:
|
||||
|
|
|
@ -16,6 +16,7 @@ const initialState = ImmutableMap({
|
|||
results: [],
|
||||
chosenUrl: '',
|
||||
searchText: '',
|
||||
next: 0,
|
||||
loading: false,
|
||||
error: false,
|
||||
})
|
||||
|
@ -27,7 +28,8 @@ export default function (state = initialState, action) {
|
|||
return state.set('loading', true)
|
||||
case GIF_RESULTS_FETCH_SUCCESS:
|
||||
return state.withMutations(map => {
|
||||
map.set('results', action.results);
|
||||
map.set('results', action.data.results);
|
||||
map.set('next', action.data.next);
|
||||
map.set('error', false);
|
||||
map.set('loading', false);
|
||||
});
|
||||
|
|
|
@ -356,6 +356,11 @@ body {
|
|||
background-color: #DE2960;
|
||||
}
|
||||
|
||||
.backgroundColorDangerDark_onHover:hover {
|
||||
background-color: #c72c5b;
|
||||
}
|
||||
|
||||
|
||||
.colorPrimary {
|
||||
color: #2d3436;
|
||||
}
|
||||
|
|
|
@ -41,19 +41,28 @@ class Notification < ApplicationRecord
|
|||
validates :account_id, uniqueness: { scope: [:activity_type, :activity_id] }
|
||||
validates :activity_type, inclusion: { in: TYPE_CLASS_MAP.values }
|
||||
|
||||
scope :browserable, ->(exclude_types = [], account_id = nil) {
|
||||
scope :browserable, ->(exclude_types = [], account_id = nil, only_verified = false, only_following = false) {
|
||||
types = TYPE_CLASS_MAP.values - activity_types_from_types(exclude_types + [:follow_request])
|
||||
# if account_id.nil?
|
||||
puts "-----VERTS------"
|
||||
Notification.includes(:from_account).where(activity_type: types, accounts: {
|
||||
is_verified: true
|
||||
})
|
||||
# joins(:account).where({ 'from_account.id' => 6 })
|
||||
# is_verified: false
|
||||
# )
|
||||
# els
|
||||
# where(activity_type: types, from_account_id: account_id)
|
||||
# end
|
||||
|
||||
# Notification.includes(:from_account).where(activity_type: types, accounts: {
|
||||
# is_verified: true
|
||||
# })
|
||||
|
||||
theOptions = { :activity_type => types }
|
||||
|
||||
if !account_id.nil?
|
||||
theOptions.from_account_id = account_id
|
||||
end
|
||||
|
||||
if only_verified
|
||||
theOptions[:accounts] = {
|
||||
:is_verified => true
|
||||
}
|
||||
|
||||
Notification.includes(:from_account).where(theOptions)
|
||||
else
|
||||
where(theOptions)
|
||||
end
|
||||
}
|
||||
|
||||
cache_associated :from_account, status: STATUS_INCLUDES, mention: [status: STATUS_INCLUDES], favourite: [:account, status: STATUS_INCLUDES], follow: :account, poll: [status: STATUS_INCLUDES]
|
||||
|
|
|
@ -87,7 +87,8 @@ class Status < ApplicationRecord
|
|||
scope :remote, -> { where(local: false).or(where.not(uri: nil)) }
|
||||
scope :local, -> { where(local: true).or(where(uri: nil)) }
|
||||
|
||||
scope :without_replies, -> { where('statuses.reply = FALSE OR statuses.in_reply_to_account_id = statuses.account_id') }
|
||||
scope :only_replies, -> { where('statuses.reply = TRUE') }
|
||||
scope :without_replies, -> { where('statuses.reply = FALSE') }
|
||||
scope :without_reblogs, -> { where('statuses.reblog_of_id IS NULL') }
|
||||
scope :with_public_visibility, -> { where(visibility: :public) }
|
||||
scope :tagged_with, ->(tag) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag }) }
|
||||
|
|
|
@ -329,12 +329,16 @@ Rails.application.routes.draw do
|
|||
resources :gab_trends, only: [:index]
|
||||
resources :streaming, only: [:index]
|
||||
resources :custom_emojis, only: [:index]
|
||||
resources :gifs, only: [:index]
|
||||
resources :suggestions, only: [:index, :destroy]
|
||||
resources :scheduled_statuses, only: [:index, :show, :update, :destroy]
|
||||
resources :preferences, only: [:index]
|
||||
resources :trends, only: [:index]
|
||||
|
||||
namespace :gifs do
|
||||
get :categories
|
||||
get :search
|
||||
end
|
||||
|
||||
resources :conversations, only: [:index, :destroy] do
|
||||
member do
|
||||
post :read
|
||||
|
@ -462,6 +466,7 @@ Rails.application.routes.draw do
|
|||
|
||||
get '/tags/:tag', to: 'react#react'
|
||||
get '/:username/with_replies', to: 'accounts#show', username: username_regex, as: :short_account_with_replies
|
||||
get '/:username/comments_only', to: 'accounts#show', username: username_regex, as: :short_account_comments_only
|
||||
get '/:username/media', to: 'accounts#show', username: username_regex, as: :short_account_media
|
||||
get '/:username/tagged/:tag', to: 'accounts#show', username: username_regex, as: :short_account_tag
|
||||
get '/:username/posts/:statusId/reblogs', to: 'statuses#show', username: username_regex
|
||||
|
|
Loading…
Reference in New Issue