This commit is contained in:
mgabdev
2020-03-04 17:26:01 -05:00
parent 143725b5bd
commit 567894f614
109 changed files with 976 additions and 1643 deletions

View File

@@ -1,55 +1,30 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { List as ImmutableList } from 'immutable';
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
import { fetchAccount, fetchAccountByUsername } from '../../actions/accounts';
import { expandAccountFeaturedTimeline, expandAccountTimeline } from '../../actions/timelines';
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
import { me } from '../../initial_state';
import StatusList from '../../components/status_list/status_list';
import ColumnIndicator from '../../components/column_indicator';
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { List as ImmutableList } from 'immutable'
import { injectIntl, defineMessages } from 'react-intl'
import { expandAccountFeaturedTimeline, expandAccountTimeline } from '../../actions/timelines'
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs'
import StatusList from '../../components/status_list/status_list'
const messages = defineMessages({
posts: { id: 'account.posts', defaultMessage: 'Gabs' },
postsWithReplies: { id: 'account.posts_with_replies', defaultMessage: 'Gabs and replies' },
media: { id: 'account.media', defaultMessage: 'Media' },
error: { id: 'empty_column.account_unavailable', defaultMessage: 'Profile unavailable' },
});
empty: { id: 'empty_column.account_timeline', defaultMessage: 'No gabs here!' },
})
const emptyList = ImmutableList();
const emptyList = ImmutableList()
const mapStateToProps = (state, { params: { username }, withReplies = false }) => {
const accounts = state.getIn(['accounts']);
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase());
const mapStateToProps = (state, { account, withReplies = false }) => {
const accountId = !!account ? account.getIn(['id'], null) : -1
let accountId = -1;
let accountUsername = username;
if (accountFetchError) {
accountId = null;
} else {
let account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase());
accountId = account ? account.getIn(['id'], null) : -1;
accountUsername = account ? account.getIn(['acct'], '') : '';
}
const path = withReplies ? `${accountId}:with_replies` : accountId;
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false);
const isLocked = state.getIn(['accounts', accountId, 'locked'], false);
const isFollowing = state.getIn(['relationships', accountId, 'following'], false);
const unavailable = (me == accountId) ? false : (isBlocked || (isLocked && !isFollowing));
const path = withReplies ? `${accountId}:with_replies` : accountId
return {
accountId,
unavailable,
accountUsername,
isAccount: !!state.getIn(['accounts', accountId]),
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
};
};
}
}
export default
@connect(mapStateToProps)
@@ -64,57 +39,56 @@ class AccountTimeline extends ImmutablePureComponent {
isLoading: PropTypes.bool,
hasMore: PropTypes.bool,
withReplies: PropTypes.bool,
isAccount: PropTypes.bool,
unavailable: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
}
componentWillMount() {
const { params: { username }, accountId, withReplies } = this.props;
const { accountId, withReplies } = this.props
if (accountId && accountId !== -1) {
this.props.dispatch(fetchAccount(accountId));
this.props.dispatch(fetchAccountIdentityProofs(accountId));
this.props.dispatch(fetchAccountIdentityProofs(accountId))
if (!withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(accountId));
this.props.dispatch(expandAccountFeaturedTimeline(accountId))
}
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
} else {
this.props.dispatch(fetchAccountByUsername(username));
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }))
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId) || nextProps.withReplies !== this.props.withReplies) {
this.props.dispatch(fetchAccount(nextProps.accountId));
this.props.dispatch(fetchAccountIdentityProofs(nextProps.accountId));
this.props.dispatch(fetchAccountIdentityProofs(nextProps.accountId))
if (!nextProps.withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(nextProps.accountId));
this.props.dispatch(expandAccountFeaturedTimeline(nextProps.accountId))
}
this.props.dispatch(expandAccountTimeline(nextProps.accountId, { withReplies: nextProps.withReplies }));
this.props.dispatch(expandAccountTimeline(nextProps.accountId, { withReplies: nextProps.withReplies }))
}
}
handleLoadMore = maxId => {
if (this.props.accountId && this.props.accountId !== -1) {
this.props.dispatch(expandAccountTimeline(this.props.accountId, { maxId, withReplies: this.props.withReplies }));
this.props.dispatch(expandAccountTimeline(this.props.accountId, {
maxId,
withReplies: this.props.withReplies
}))
}
}
render() {
const { statusIds, featuredStatusIds, isLoading, hasMore, isAccount, accountId, unavailable, accountUsername, intl } = this.props;
const {
account,
statusIds,
featuredStatusIds,
isLoading,
hasMore,
intl
} = this.props
if (!account) return null
if (!isAccount && accountId !== -1) {
return <ColumnIndicator type='missing' />
} else if (accountId === -1 || (!statusIds && isLoading)) {
return <ColumnIndicator type='loading' />
} else if (unavailable) {
return <ColumnIndicator type='error' message={intl.formatMessage(messages.error)} />
}
return (
<StatusList
scrollKey='account_timeline'
@@ -123,9 +97,9 @@ class AccountTimeline extends ImmutablePureComponent {
isLoading={isLoading}
hasMore={hasMore}
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No gabs here!' />}
emptyMessage={intl.formatMessage(messages.empty)}
/>
);
)
}
}

View File

@@ -12,7 +12,7 @@ export default class Header extends ImmutablePureComponent {
onBlock: PropTypes.func.isRequired,
onMention: PropTypes.func.isRequired,
onDirect: PropTypes.func.isRequired,
onReblogToggle: PropTypes.func.isRequired,
onRepostToggle: PropTypes.func.isRequired,
onReport: PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired,
onBlockDomain: PropTypes.func.isRequired,
@@ -47,8 +47,8 @@ export default class Header extends ImmutablePureComponent {
this.props.onReport(this.props.account);
}
handleReblogToggle = () => {
this.props.onReblogToggle(this.props.account);
handleRepostToggle = () => {
this.props.onRepostToggle(this.props.account);
}
handleMute = () => {
@@ -94,7 +94,7 @@ export default class Header extends ImmutablePureComponent {
onBlock={this.handleBlock}
onMention={this.handleMention}
onDirect={this.handleDirect}
onReblogToggle={this.handleReblogToggle}
onRepostToggle={this.handleRepostToggle}
onReport={this.handleReport}
onMute={this.handleMute}
onBlockDomain={this.handleBlockDomain}

View File

@@ -114,9 +114,9 @@ class Header extends ImmutablePureComponent {
} else {
if (account.getIn(['relationship', 'following'])) {
if (account.getIn(['relationship', 'showing_reblogs'])) {
menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onRepostToggle });
} else {
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onRepostToggle });
}
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList });
@@ -279,7 +279,7 @@ class Header extends ImmutablePureComponent {
<NavLink exact activeClassName='active' to={`/${account.get('acct')}/favorites`} title={intl.formatNumber(account.get('favourite_count'))}>
{ /* : TODO : shortNumberFormat(account.get('favourite_count')) */ }
<span></span>
<FormattedMessage id='navigation_bar.favourites' defaultMessage='Favorites' />
<FormattedMessage id='navigation_bar.favorites' defaultMessage='Favorites' />
</NavLink>
<NavLink exact activeClassName='active' to={`/${account.get('acct')}/pins`} title={intl.formatNumber(account.get('pinned_count'))}>
{ /* : TODO : shortNumberFormat(account.get('pinned_count')) */ }

View File

@@ -1 +0,0 @@
export { default } from './profile_info_panel'

View File

@@ -1,135 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { List as ImmutableList } from 'immutable';
import Icon from '../../../../components/icon';
import VerifiedIcon from '../../../../components/verified_icon';
import Badge from '../../../../components/badge';
const messages = defineMessages({
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
bot: { id: 'account.badges.bot', defaultMessage: 'Bot' },
memberSince: { id: 'account.member_since', defaultMessage:'Member since {date}'},
});
const dateFormatOptions = {
month: 'short',
day: 'numeric',
year: 'numeric',
hour12: false,
hour: '2-digit',
minute: '2-digit',
};
const mapStateToProps = (state, { account }) => {
const identity_proofs = account ? state.getIn(['identity_proofs', account.get('id')], ImmutableList()) : ImmutableList();
return {
identity_proofs,
domain: state.getIn(['meta', 'domain']),
};
};
export default
@connect(mapStateToProps)
@injectIntl
class ProfileInfoPanel extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map,
identity_proofs: ImmutablePropTypes.list,
intl: PropTypes.object.isRequired,
username: PropTypes.string,
};
render () {
const { account, intl, identity_proofs, username } = this.props;
if (!account) {
return (
<div className='profile-info-panel'>
<div className='profile-info-panel__content'>
<div className='profile-info-panel-content__name'>
<h1>
<span/>
<small>@{username}</small>
</h1>
</div>
</div>
</div>
);
}
const lockedIcon = account.get('locked') ? (<Icon id='lock' title={intl.formatMessage(messages.account_locked)} />) : '';
const badge = account.get('bot') ? (<div className='account-role bot'>{intl.formatMessage(messages.bot)}</div>) : null;
const content = { __html: account.get('note_emojified') };
const fields = account.get('fields');
const acct = account.get('acct');
const displayNameHtml = { __html: account.get('display_name_html') };
const memberSinceDate = intl.formatDate(account.get('created_at'), { month: 'long', year: 'numeric' });
return (
<div className='profile-info-panel'>
<div className='profile-info-panel__content'>
<div className='profile-info-panel-content__name'>
<h1>
<span dangerouslySetInnerHTML={displayNameHtml} />
{account.get('is_verified') && <VerifiedIcon />}
{badge}
<small>@{acct} {lockedIcon}</small>
</h1>
</div>
<div className='profile-info-panel-content__badges'>
{account.get('is_pro') && <Badge type='pro' />}
{account.get('is_donor') && <Badge type='donor' />}
{account.get('is_investor') && <Badge type='investor' />}
<div className='profile-info-panel-content__badges__join-date'>
<Icon id="calendar"/>
{intl.formatMessage(messages.memberSince, {
date: memberSinceDate
})}
</div>
</div>
{
(account.get('note').length > 0 && account.get('note') !== '<p></p>') &&
<div className='profile-info-panel-content__bio' dangerouslySetInnerHTML={content} />
}
{(fields.size > 0 || identity_proofs.size > 0) && (
<div className='profile-info-panel-content__fields'>
{identity_proofs.map((proof, i) => (
<dl className='test' key={i}>
<dt dangerouslySetInnerHTML={{ __html: proof.get('provider') }} />
<dd className='verified'>
<a href={proof.get('proof_url')} target='_blank' rel='noopener'>
<span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
<Icon id='check' className='verified__mark' />
</span>
</a>
<a href={proof.get('profile_url')} target='_blank' rel='noopener'>
<span dangerouslySetInnerHTML={{ __html: ' ' + proof.get('provider_username') }} />
</a>
</dd>
</dl>
))}
{fields.map((pair, i) => (
<dl className='profile-info-panel-content__fields__item' key={i}>
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
<dd className={pair.get('verified_at') && 'verified'} title={pair.get('value_plain')}>
{pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
</dd>
</dl>
))}
</div>
)}
</div>
</div>
);
}
}

View File

@@ -1,133 +0,0 @@
.profile-info-panel {
display: flex;
position: relative;
&__content {
display: flex;
flex-direction: column;
flex: 1 1;
@media (min-width:895px) {
padding-top: 60px;
}
}
}
.profile-info-panel-content {
display: flex;
&__badges {
display: flex;
margin: 5px 0;
flex-direction: row;
flex-wrap: wrap;
&__join-date {
display: block;
margin-top: 5px;
.fa {
margin-right: 8px;
}
span {
color: $primary-text-color;
@include text-sizing(15px, 400, 1.25);
body.theme-gabsocial-light & {
color: $gab-default-text-light;
}
}
}
}
&__name {
display: block;
.account-role {
vertical-align: top;
}
.emojione {
@include size(22px);
}
h1 {
span:first-of-type {
color: #ffffff;
@include text-overflow(nowrap);
@include text-sizing(20px, 600, 1.25);
body.theme-gabsocial-light & {
color: $gab-default-text-light;
}
}
small {
display: block;
color: $secondary-text-color;
@include text-sizing(16px, 400, 1.5);
@include text-overflow;
}
}
}
&__bio {
display: block;
flex: 1 1;
color: $primary-text-color;
margin: 15px 0;
@include text-sizing(15px, 400, 1.25);
a {
color: lighten($ui-highlight-color, 8%);
}
}
&__fields {
display: flex;
flex-direction: column;
border-top: 1px solid lighten($ui-base-color, 12%);
padding: 10px 0;
margin: 5px 0;
@media screen and (max-width:895px) {
border-bottom: 1px solid lighten($ui-base-color, 12%);
}
a {
color: lighten($ui-highlight-color, 8%);
}
dl:first-child .verified {
border-radius: 0 4px 0 0;
}
.verified a {
color: $valid-value-color;
}
&__item {
display: flex;
padding: 2px 0;
margin: 2px 0;
flex: 1 1;
* {
@include text-sizing(15px, 400, 24px);
}
dt {
min-width: 26px;
}
dd {
padding-left: 4px;
}
}
}
}

View File

@@ -83,7 +83,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(directCompose(account, router));
},
onReblogToggle (account) {
onRepostToggle (account) {
if (account.getIn(['relationship', 'showing_reblogs'])) {
dispatch(followAccount(account.get('id'), false));
} else {