Progress
This commit is contained in:
parent
143725b5bd
commit
567894f614
@ -2,25 +2,25 @@ import api from '../api';
|
||||
import { importFetchedAccounts, importFetchedStatus } from './importer';
|
||||
import { me } from '../initial_state';
|
||||
|
||||
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
|
||||
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
|
||||
export const REBLOG_FAIL = 'REBLOG_FAIL';
|
||||
export const REPOST_REQUEST = 'REPOST_REQUEST';
|
||||
export const REPOST_SUCCESS = 'REPOST_SUCCESS';
|
||||
export const REPOST_FAIL = 'REPOST_FAIL';
|
||||
|
||||
export const FAVORITE_REQUEST = 'FAVORITE_REQUEST';
|
||||
export const FAVORITE_SUCCESS = 'FAVORITE_SUCCESS';
|
||||
export const FAVORITE_FAIL = 'FAVORITE_FAIL';
|
||||
|
||||
export const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST';
|
||||
export const UNREBLOG_SUCCESS = 'UNREBLOG_SUCCESS';
|
||||
export const UNREBLOG_FAIL = 'UNREBLOG_FAIL';
|
||||
export const UNREPOST_REQUEST = 'UNREPOST_REQUEST';
|
||||
export const UNREPOST_SUCCESS = 'UNREPOST_SUCCESS';
|
||||
export const UNREPOST_FAIL = 'UNREPOST_FAIL';
|
||||
|
||||
export const UNFAVORITE_REQUEST = 'UNFAVORITE_REQUEST';
|
||||
export const UNFAVORITE_SUCCESS = 'UNFAVORITE_SUCCESS';
|
||||
export const UNFAVORITE_FAIL = 'UNFAVORITE_FAIL';
|
||||
|
||||
export const REBLOGS_FETCH_REQUEST = 'REBLOGS_FETCH_REQUEST';
|
||||
export const REBLOGS_FETCH_SUCCESS = 'REBLOGS_FETCH_SUCCESS';
|
||||
export const REBLOGS_FETCH_FAIL = 'REBLOGS_FETCH_FAIL';
|
||||
export const REPOSTS_FETCH_REQUEST = 'REPOSTS_FETCH_REQUEST';
|
||||
export const REPOSTS_FETCH_SUCCESS = 'REPOSTS_FETCH_SUCCESS';
|
||||
export const REPOSTS_FETCH_FAIL = 'REPOSTS_FETCH_FAIL';
|
||||
|
||||
export const PIN_REQUEST = 'PIN_REQUEST';
|
||||
export const PIN_SUCCESS = 'PIN_SUCCESS';
|
||||
@ -30,119 +30,119 @@ export const UNPIN_REQUEST = 'UNPIN_REQUEST';
|
||||
export const UNPIN_SUCCESS = 'UNPIN_SUCCESS';
|
||||
export const UNPIN_FAIL = 'UNPIN_FAIL';
|
||||
|
||||
export function reblog(status) {
|
||||
export function repost(status) {
|
||||
return function (dispatch, getState) {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(reblogRequest(status));
|
||||
dispatch(repostRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/reblog`).then(function (response) {
|
||||
// The reblog API method returns a new status wrapped around the original. In this case we are only
|
||||
// interested in how the original is modified, hence passing it skipping the wrapper
|
||||
dispatch(importFetchedStatus(response.data.reblog));
|
||||
dispatch(reblogSuccess(status));
|
||||
dispatch(repostSuccess(status));
|
||||
}).catch(function (error) {
|
||||
dispatch(reblogFail(status, error));
|
||||
dispatch(repostFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unreblog(status) {
|
||||
export function unrepost(status) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unreblogRequest(status));
|
||||
dispatch(unrepostRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(unreblogSuccess(status));
|
||||
dispatch(unrepostSuccess(status));
|
||||
}).catch(error => {
|
||||
dispatch(unreblogFail(status, error));
|
||||
dispatch(unrepostFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function reblogRequest(status) {
|
||||
export function repostRequest(status) {
|
||||
return {
|
||||
type: REBLOG_REQUEST,
|
||||
type: REPOST_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function reblogSuccess(status) {
|
||||
export function repostSuccess(status) {
|
||||
return {
|
||||
type: REBLOG_SUCCESS,
|
||||
type: REPOST_SUCCESS,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function reblogFail(status, error) {
|
||||
export function repostFail(status, error) {
|
||||
return {
|
||||
type: REBLOG_FAIL,
|
||||
type: REPOST_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unreblogRequest(status) {
|
||||
export function unrepostRequest(status) {
|
||||
return {
|
||||
type: UNREBLOG_REQUEST,
|
||||
type: UNREPOST_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unreblogSuccess(status) {
|
||||
export function unrepostSuccess(status) {
|
||||
return {
|
||||
type: UNREBLOG_SUCCESS,
|
||||
type: UNREPOST_SUCCESS,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unreblogFail(status, error) {
|
||||
export function unrepostFail(status, error) {
|
||||
return {
|
||||
type: UNREBLOG_FAIL,
|
||||
type: UNREPOST_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function favourite(status) {
|
||||
export function favorite(status) {
|
||||
return function (dispatch, getState) {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(favouriteRequest(status));
|
||||
dispatch(favoriteRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) {
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(favouriteSuccess(status));
|
||||
dispatch(favoriteSuccess(status));
|
||||
}).catch(function (error) {
|
||||
dispatch(favouriteFail(status, error));
|
||||
dispatch(favoriteFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unfavourite(status) {
|
||||
export function unfavorite(status) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unfavouriteRequest(status));
|
||||
dispatch(unfavoriteRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => {
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(unfavouriteSuccess(status));
|
||||
dispatch(unfavoriteSuccess(status));
|
||||
}).catch(error => {
|
||||
dispatch(unfavouriteFail(status, error));
|
||||
dispatch(unfavoriteFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function favouriteRequest(status) {
|
||||
export function favoriteRequest(status) {
|
||||
return {
|
||||
type: FAVORITE_REQUEST,
|
||||
status: status,
|
||||
@ -150,7 +150,7 @@ export function favouriteRequest(status) {
|
||||
};
|
||||
};
|
||||
|
||||
export function favouriteSuccess(status) {
|
||||
export function favoriteSuccess(status) {
|
||||
return {
|
||||
type: FAVORITE_SUCCESS,
|
||||
status: status,
|
||||
@ -158,7 +158,7 @@ export function favouriteSuccess(status) {
|
||||
};
|
||||
};
|
||||
|
||||
export function favouriteFail(status, error) {
|
||||
export function favoriteFail(status, error) {
|
||||
return {
|
||||
type: FAVORITE_FAIL,
|
||||
status: status,
|
||||
@ -167,7 +167,7 @@ export function favouriteFail(status, error) {
|
||||
};
|
||||
};
|
||||
|
||||
export function unfavouriteRequest(status) {
|
||||
export function unfavoriteRequest(status) {
|
||||
return {
|
||||
type: UNFAVORITE_REQUEST,
|
||||
status: status,
|
||||
@ -175,7 +175,7 @@ export function unfavouriteRequest(status) {
|
||||
};
|
||||
};
|
||||
|
||||
export function unfavouriteSuccess(status) {
|
||||
export function unfavoriteSuccess(status) {
|
||||
return {
|
||||
type: UNFAVORITE_SUCCESS,
|
||||
status: status,
|
||||
@ -183,7 +183,7 @@ export function unfavouriteSuccess(status) {
|
||||
};
|
||||
};
|
||||
|
||||
export function unfavouriteFail(status, error) {
|
||||
export function unfavoriteFail(status, error) {
|
||||
return {
|
||||
type: UNFAVORITE_FAIL,
|
||||
status: status,
|
||||
@ -192,39 +192,39 @@ export function unfavouriteFail(status, error) {
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchReblogs(id) {
|
||||
export function fetchReposts(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchReblogsRequest(id));
|
||||
dispatch(fetchRepostsRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => {
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(fetchReblogsSuccess(id, response.data));
|
||||
dispatch(fetchRepostsSuccess(id, response.data));
|
||||
}).catch(error => {
|
||||
dispatch(fetchReblogsFail(id, error));
|
||||
dispatch(fetchRepostsFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchReblogsRequest(id) {
|
||||
export function fetchRepostsRequest(id) {
|
||||
return {
|
||||
type: REBLOGS_FETCH_REQUEST,
|
||||
type: REPOSTS_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchReblogsSuccess(id, accounts) {
|
||||
export function fetchRepostsSuccess(id, accounts) {
|
||||
return {
|
||||
type: REBLOGS_FETCH_SUCCESS,
|
||||
type: REPOSTS_FETCH_SUCCESS,
|
||||
id,
|
||||
accounts,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchReblogsFail(id, error) {
|
||||
export function fetchRepostsFail(id, error) {
|
||||
return {
|
||||
type: REBLOGS_FETCH_FAIL,
|
||||
type: REPOSTS_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
@ -2,18 +2,18 @@ import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||
import api, { getLinks } from '../api';
|
||||
import { Map as ImmutableMap, List as ImmutableList, toJS } from 'immutable';
|
||||
|
||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
||||
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
||||
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
|
||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
||||
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
||||
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
|
||||
export const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE';
|
||||
export const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE';
|
||||
export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
|
||||
|
||||
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
|
||||
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
|
||||
export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
|
||||
export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
|
||||
|
||||
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
|
||||
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
|
||||
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
|
||||
|
||||
export const MAX_QUEUED_ITEMS = 40;
|
||||
@ -94,9 +94,9 @@ export function dequeueTimeline(timeline, expandFunc, optionalExpandArgs) {
|
||||
|
||||
export function deleteFromTimelines(id) {
|
||||
return (dispatch, getState) => {
|
||||
const accountId = getState().getIn(['statuses', id, 'account']);
|
||||
const accountId = getState().getIn(['statuses', id, 'account']);
|
||||
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')]);
|
||||
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null);
|
||||
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null);
|
||||
|
||||
dispatch({
|
||||
type: TIMELINE_DELETE,
|
||||
@ -114,7 +114,7 @@ export function clearTimeline(timeline) {
|
||||
};
|
||||
};
|
||||
|
||||
const noOp = () => {};
|
||||
const noOp = () => { };
|
||||
|
||||
const parseTags = (tags = {}, mode) => {
|
||||
return (tags[mode] || []).map((tag) => {
|
||||
@ -152,20 +152,20 @@ 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 expandPublicTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
|
||||
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { 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 expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
|
||||
export const expandPublicTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
|
||||
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { 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 expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
|
||||
export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 40 });
|
||||
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) => {
|
||||
export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 20 });
|
||||
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) => {
|
||||
return expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, {
|
||||
max_id: maxId,
|
||||
any: parseTags(tags, 'any'),
|
||||
all: parseTags(tags, 'all'),
|
||||
none: parseTags(tags, 'none'),
|
||||
any: parseTags(tags, 'any'),
|
||||
all: parseTags(tags, 'all'),
|
||||
none: parseTags(tags, 'none'),
|
||||
}, done);
|
||||
};
|
||||
|
||||
|
132
app/javascript/gabsocial/components/media_item.js
Normal file
132
app/javascript/gabsocial/components/media_item.js
Normal file
@ -0,0 +1,132 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { decode } from 'blurhash'
|
||||
import { autoPlayGif, displayMedia } from '../initial_state'
|
||||
import Icon from './icon'
|
||||
import Image from './image'
|
||||
import Text from './text'
|
||||
|
||||
export default class MediaItem extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
attachment: ImmutablePropTypes.map.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
|
||||
loaded: false,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.attachment.get('blurhash')) {
|
||||
this._decode()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.attachment.get('blurhash') !== this.props.attachment.get('blurhash') && this.props.attachment.get('blurhash')) {
|
||||
this._decode()
|
||||
}
|
||||
}
|
||||
|
||||
_decode() {
|
||||
const hash = this.props.attachment.get('blurhash')
|
||||
const pixels = decode(hash, 160, 160)
|
||||
|
||||
if (pixels && this.canvas) {
|
||||
const ctx = this.canvas.getContext('2d')
|
||||
const imageData = new ImageData(pixels, 160, 160)
|
||||
|
||||
ctx.putImageData(imageData, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
setCanvasRef = c => {
|
||||
this.canvas = c
|
||||
}
|
||||
|
||||
handleImageLoad = () => {
|
||||
this.setState({ loaded: true })
|
||||
}
|
||||
|
||||
hoverToPlay() {
|
||||
return !autoPlayGif && ['gifv', 'video'].indexOf(this.props.attachment.get('type')) !== -1
|
||||
}
|
||||
|
||||
render() {
|
||||
const { attachment } = this.props
|
||||
const { visible, loaded } = this.state
|
||||
|
||||
const status = attachment.get('status')
|
||||
const title = status.get('spoiler_text') || attachment.get('description')
|
||||
|
||||
const attachmentType = attachment.get('type')
|
||||
let badge = null
|
||||
|
||||
if (attachmentType === 'video') {
|
||||
const duration = attachment.getIn(['meta', 'duration'])
|
||||
badge = (duration / 60).toFixed(2)
|
||||
} else if (attachmentType === 'gifv') {
|
||||
badge = 'GIF'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.width25PC, _s.paddingTop25PC].join(' ')}>
|
||||
<div className={[_s.default, _s.positionAbsolute, _s.top0, _s.height100PC, _s.width100PC, _s.paddingVertical5PX, _s.paddingHorizontal5PX].join(' ')}>
|
||||
<NavLink
|
||||
to={status.get('url')} /* : todo : */
|
||||
title={title}
|
||||
className={[_s.default, _s.width100PC, _s.height100PC, _s.border1PX, _s.borderColorSecondary, _s.overflowHidden].join(' ')}
|
||||
>
|
||||
{
|
||||
(!loaded || !visible) &&
|
||||
<canvas
|
||||
height='100%'
|
||||
width='100%'
|
||||
ref={this.setCanvasRef}
|
||||
className={[_s.default, _s.width100PC, _s.height100PC, _s.z2].join(' ')}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
visible &&
|
||||
<Image
|
||||
height='100%'
|
||||
src={attachment.get('preview_url')}
|
||||
alt={attachment.get('description')}
|
||||
title={attachment.get('description')}
|
||||
onLoad={this.handleImageLoad}
|
||||
className={_s.z1}
|
||||
/>
|
||||
}
|
||||
|
||||
<div className={[_s.default, _s.alignItemsCenter, _s.justifyContentCenter, _s.height100PC, _s.width100PC, _s.z3, _s.positionAbsolute].join(' ')}>
|
||||
{
|
||||
!visible &&
|
||||
<Icon
|
||||
id='hidden'
|
||||
width='22px'
|
||||
height='22px'
|
||||
className={[_s.fillColorWhite].join('')}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
!!badge &&
|
||||
<div className={[_s.default, _s.positionAbsolute, _s.radiusSmall, _s.backgroundColorOpaque, _s.paddingHorizontal5PX, _s.paddingVertical5PX, _s.marginRight5PX, _s.marginVertical5PX, _s.bottom0, _s.right0].join(' ')}>
|
||||
<Text size='extraSmall' color='white'>
|
||||
{badge}
|
||||
</Text>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -24,7 +24,7 @@ class BoostModal extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
onReblog: PropTypes.func.isRequired,
|
||||
onRepost: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
@ -33,8 +33,8 @@ class BoostModal extends ImmutablePureComponent {
|
||||
this.button.focus();
|
||||
}
|
||||
|
||||
handleReblog = () => {
|
||||
this.props.onReblog(this.props.status);
|
||||
handleRepost = () => {
|
||||
this.props.onRepost(this.props.status);
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ class BoostModal extends ImmutablePureComponent {
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
<Button text={intl.formatMessage(buttonText)} onClick={this.handleReblog} ref={this.setRef} />
|
||||
<Button text={intl.formatMessage(buttonText)} onClick={this.handleRepost} ref={this.setRef} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -11,7 +11,7 @@ const messages = defineMessages({
|
||||
reply: { id: 'keyboard_shortcuts.reply', defaultMessage: 'reply' },
|
||||
mention: { id: 'keyboard_shortcuts.mention', defaultMessage: 'mention author' },
|
||||
profile: { id: 'keyboard_shortcuts.profile', defaultMessage: 'open author\'s profile' },
|
||||
favourite: { id: 'keyboard_shortcuts.favourite', defaultMessage: 'favorite' },
|
||||
favorite: { id: 'keyboard_shortcuts.favorite', defaultMessage: 'favorite' },
|
||||
boost: { id: 'keyboard_shortcuts.boost', defaultMessage: 'repost' },
|
||||
enter: { id: 'keyboard_shortcuts.enter', defaultMessage: 'open status' },
|
||||
toggle_hidden: { id: 'keyboard_shortcuts.toggle_hidden', defaultMessage: 'show/hide text behind CW' },
|
||||
@ -28,7 +28,7 @@ const messages = defineMessages({
|
||||
notifications: { id: 'keyboard_shortcuts.notifications', defaultMessage: 'open notifications column' },
|
||||
direct: { id: 'keyboard_shortcuts.direct', defaultMessage: 'open direct messages column' },
|
||||
start: { id: 'keyboard_shortcuts.start', defaultMessage: 'open "get started" column' },
|
||||
favourites: { id: 'keyboard_shortcuts.favourites', defaultMessage: 'open favorites list' },
|
||||
favorites: { id: 'keyboard_shortcuts.favorites', defaultMessage: 'open favorites list' },
|
||||
pinned: { id: 'keyboard_shortcuts.pinned', defaultMessage: 'open pinned gabs list' },
|
||||
my_profile: { id: 'keyboard_shortcuts.my_profile', defaultMessage: 'open your profile' },
|
||||
blocked: { id: 'keyboard_shortcuts.blocked', defaultMessage: 'open blocked users list' },
|
||||
@ -65,7 +65,7 @@ class HotkeysModal extends ImmutablePureComponent {
|
||||
<HotKeysModalRow hotkey='r' action={intl.formatMessage(messages.reply)} />
|
||||
<HotKeysModalRow hotkey='m' action={intl.formatMessage(messages.mention)} />
|
||||
<HotKeysModalRow hotkey='p' action={intl.formatMessage(messages.profile)} />
|
||||
<HotKeysModalRow hotkey='f' action={intl.formatMessage(messages.favourite)} />
|
||||
<HotKeysModalRow hotkey='f' action={intl.formatMessage(messages.favorite)} />
|
||||
<HotKeysModalRow hotkey='b' action={intl.formatMessage(messages.boost)} />
|
||||
<HotKeysModalRow hotkey='enter, o' action={intl.formatMessage(messages.enter)} />
|
||||
<HotKeysModalRow hotkey='x' action={intl.formatMessage(messages.toggle_hidden)} />
|
||||
@ -108,7 +108,7 @@ class HotkeysModal extends ImmutablePureComponent {
|
||||
</thead>
|
||||
<tbody>
|
||||
<HotKeysModalRow hotkey='g + s' action={intl.formatMessage(messages.start)} />
|
||||
<HotKeysModalRow hotkey='g + f' action={intl.formatMessage(messages.favourites)} />
|
||||
<HotKeysModalRow hotkey='g + f' action={intl.formatMessage(messages.favorites)} />
|
||||
<HotKeysModalRow hotkey='g + p' action={intl.formatMessage(messages.pinned)} />
|
||||
<HotKeysModalRow hotkey='g + u' action={intl.formatMessage(messages.my_profile)} />
|
||||
<HotKeysModalRow hotkey='g + b' action={intl.formatMessage(messages.blocked)} />
|
||||
|
@ -51,7 +51,7 @@ class ProfileStatsPanel extends ImmutablePureComponent {
|
||||
account.get('id') === me &&
|
||||
<UserStat
|
||||
title={intl.formatMessage(messages.favorites)}
|
||||
value={shortNumberFormat(account.get('favourite_count'))}
|
||||
value={shortNumberFormat(account.get('favourite_count'))} /* : todo : */
|
||||
to={`/${account.get('acct')}/favorites`}
|
||||
/>
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ export default class StatusOptionsPopover extends PureComponent {
|
||||
// menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||
// } else {
|
||||
// if (status.get('visibility') === 'private') {
|
||||
// menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick });
|
||||
// menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleRepostClick });
|
||||
// }
|
||||
// }
|
||||
|
||||
|
@ -73,7 +73,7 @@ export default class ScrollableList extends PureComponent {
|
||||
this.scrollToTopOnMouseIdle = false;
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
this.window = window;
|
||||
this.documentElement = document.scrollingElement || document.documentElement;
|
||||
|
||||
@ -97,7 +97,7 @@ export default class ScrollableList extends PureComponent {
|
||||
this.setScrollTop(newScrollTop);
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps, prevState, snapshot) {
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
// Reset the scroll position when a new child comes in in order not to
|
||||
// jerk the scrollbar around if you're already scrolled down the page.
|
||||
if (snapshot !== null) {
|
||||
@ -105,12 +105,12 @@ export default class ScrollableList extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
attachScrollListener () {
|
||||
attachScrollListener() {
|
||||
this.window.addEventListener('scroll', this.handleScroll);
|
||||
this.window.addEventListener('wheel', this.handleWheel);
|
||||
}
|
||||
|
||||
detachScrollListener () {
|
||||
detachScrollListener() {
|
||||
this.window.removeEventListener('scroll', this.handleScroll);
|
||||
this.window.removeEventListener('wheel', this.handleWheel);
|
||||
}
|
||||
@ -139,16 +139,16 @@ export default class ScrollableList extends PureComponent {
|
||||
this.lastScrollWasSynthetic = false;
|
||||
}
|
||||
}, 150, {
|
||||
trailing: true,
|
||||
});
|
||||
trailing: true,
|
||||
});
|
||||
|
||||
handleWheel = throttle(() => {
|
||||
this.scrollToTopOnMouseIdle = false;
|
||||
}, 150, {
|
||||
trailing: true,
|
||||
});
|
||||
trailing: true,
|
||||
});
|
||||
|
||||
getSnapshotBeforeUpdate (prevProps) {
|
||||
getSnapshotBeforeUpdate(prevProps) {
|
||||
const someItemInserted = React.Children.count(prevProps.children) > 0 &&
|
||||
React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
|
||||
this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
|
||||
@ -166,21 +166,21 @@ export default class ScrollableList extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
this.clearMouseIdleTimer();
|
||||
this.detachScrollListener();
|
||||
this.detachIntersectionObserver();
|
||||
}
|
||||
|
||||
attachIntersectionObserver () {
|
||||
attachIntersectionObserver() {
|
||||
this.intersectionObserverWrapper.connect();
|
||||
}
|
||||
|
||||
detachIntersectionObserver () {
|
||||
detachIntersectionObserver() {
|
||||
this.intersectionObserverWrapper.disconnect();
|
||||
}
|
||||
|
||||
getFirstChildKey (props) {
|
||||
getFirstChildKey(props) {
|
||||
const { children } = props;
|
||||
let firstChild = children;
|
||||
|
||||
@ -198,7 +198,7 @@ export default class ScrollableList extends PureComponent {
|
||||
this.props.onLoadMore();
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { children, scrollKey, showLoading, isLoading, hasMore, emptyMessage, onLoadMore } = this.props;
|
||||
const childrenCount = React.Children.count(children);
|
||||
|
||||
@ -207,28 +207,33 @@ export default class ScrollableList extends PureComponent {
|
||||
const loadMore = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
|
||||
|
||||
if (showLoading) {
|
||||
return ( <ColumnIndicator type='loading' /> );
|
||||
return <ColumnIndicator type='loading' />
|
||||
} else if (isLoading || childrenCount > 0 || hasMore || !emptyMessage) {
|
||||
return (
|
||||
<div className='scrollable-list' onMouseMove={this.handleMouseMove}>
|
||||
<div onMouseMove={this.handleMouseMove}>
|
||||
<div role='feed'>
|
||||
{React.Children.map(this.props.children, (child, index) => (
|
||||
<IntersectionObserverArticle
|
||||
key={child.key}
|
||||
id={child.key}
|
||||
index={index}
|
||||
listLength={childrenCount}
|
||||
intersectionObserverWrapper={this.intersectionObserverWrapper}
|
||||
saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null}
|
||||
>
|
||||
{React.cloneElement(child, {
|
||||
getScrollPosition: this.getScrollPosition,
|
||||
updateScrollBottom: this.updateScrollBottom,
|
||||
cachedMediaWidth: this.state.cachedMediaWidth,
|
||||
cacheMediaWidth: this.cacheMediaWidth,
|
||||
})}
|
||||
</IntersectionObserverArticle>
|
||||
))}
|
||||
{
|
||||
!!this.props.children &&
|
||||
React.Children.map(this.props.children, (child, index) => (
|
||||
<IntersectionObserverArticle
|
||||
key={child.key}
|
||||
id={child.key}
|
||||
index={index}
|
||||
listLength={childrenCount}
|
||||
intersectionObserverWrapper={this.intersectionObserverWrapper}
|
||||
saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null}
|
||||
>
|
||||
{
|
||||
React.cloneElement(child, {
|
||||
getScrollPosition: this.getScrollPosition,
|
||||
updateScrollBottom: this.updateScrollBottom,
|
||||
cachedMediaWidth: this.state.cachedMediaWidth,
|
||||
cacheMediaWidth: this.cacheMediaWidth,
|
||||
})
|
||||
}
|
||||
</IntersectionObserverArticle>
|
||||
))
|
||||
}
|
||||
|
||||
{loadMore}
|
||||
</div>
|
||||
@ -236,7 +241,7 @@ export default class ScrollableList extends PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
return ( <ColumnIndicator type='error' message={emptyMessage} /> );
|
||||
return <ColumnIndicator type='error' message={emptyMessage} />
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -76,8 +76,8 @@ class Status extends ImmutablePureComponent {
|
||||
onReply: PropTypes.func,
|
||||
onShowRevisions: PropTypes.func,
|
||||
onQuote: PropTypes.func,
|
||||
onFavourite: PropTypes.func,
|
||||
onReblog: PropTypes.func,
|
||||
onFavorite: PropTypes.func,
|
||||
onRepost: PropTypes.func,
|
||||
onDelete: PropTypes.func,
|
||||
onEdit: PropTypes.func,
|
||||
onDirect: PropTypes.func,
|
||||
@ -212,12 +212,12 @@ class Status extends ImmutablePureComponent {
|
||||
this.props.onReply(this._properStatus(), this.context.router.history);
|
||||
};
|
||||
|
||||
handleHotkeyFavourite = () => {
|
||||
this.props.onFavourite(this._properStatus());
|
||||
handleHotkeyFavorite = () => {
|
||||
this.props.onFavorite(this._properStatus());
|
||||
};
|
||||
|
||||
handleHotkeyBoost = e => {
|
||||
this.props.onReblog(this._properStatus(), e);
|
||||
this.props.onRepost(this._properStatus(), e);
|
||||
};
|
||||
|
||||
handleHotkeyMention = e => {
|
||||
@ -424,7 +424,7 @@ class Status extends ImmutablePureComponent {
|
||||
|
||||
const handlers = this.props.muted ? {} : {
|
||||
reply: this.handleHotkeyReply,
|
||||
favourite: this.handleHotkeyFavourite,
|
||||
favorite: this.handleHotkeyFavorite,
|
||||
boost: this.handleHotkeyBoost,
|
||||
mention: this.handleHotkeyMention,
|
||||
open: this.handleHotkeyOpen,
|
||||
|
@ -63,8 +63,8 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
onOpenUnauthorizedModal: PropTypes.func.isRequired,
|
||||
onReply: PropTypes.func,
|
||||
onQuote: PropTypes.func,
|
||||
onFavourite: PropTypes.func,
|
||||
onReblog: PropTypes.func,
|
||||
onFavorite: PropTypes.func,
|
||||
onRepost: PropTypes.func,
|
||||
onDelete: PropTypes.func,
|
||||
onMention: PropTypes.func,
|
||||
onMute: PropTypes.func,
|
||||
@ -101,17 +101,17 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
handleFavouriteClick = () => {
|
||||
handleFavoriteClick = () => {
|
||||
if (me) {
|
||||
this.props.onFavourite(this.props.status)
|
||||
this.props.onFavorite(this.props.status)
|
||||
} else {
|
||||
this.props.onOpenUnauthorizedModal()
|
||||
}
|
||||
}
|
||||
|
||||
handleReblogClick = e => {
|
||||
handleRepostClick = e => {
|
||||
if (me) {
|
||||
this.props.onReblog(this.props.status, e)
|
||||
this.props.onRepost(this.props.status, e)
|
||||
} else {
|
||||
this.props.onOpenUnauthorizedModal()
|
||||
}
|
||||
@ -129,7 +129,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
const reblogCount = status.get('reblogs_count')
|
||||
const reblogTitle = !publicStatus ? formatMessage(messages.cannot_reblog) : formatMessage(messages.reblog)
|
||||
|
||||
const favoriteCount = status.get('favourites_count')
|
||||
const favoriteCount = status.get('favorites_count') // : todo :
|
||||
|
||||
const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
|
||||
<IconButton className='status-action-bar-button' title={formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />
|
||||
@ -139,8 +139,8 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
{
|
||||
title: formatMessage(messages.like),
|
||||
icon: 'like',
|
||||
active: !!status.get('favourited'),
|
||||
onClick: this.handleFavouriteClick,
|
||||
active: !!status.get('favorited'),
|
||||
onClick: this.handleFavoriteClick,
|
||||
},
|
||||
{
|
||||
title: formatMessage(messages.comment),
|
||||
@ -153,13 +153,13 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
icon: (status.get('visibility') === 'private') ? 'lock' : 'repost',
|
||||
disabled: !publicStatus,
|
||||
active: !!status.get('reblogged'),
|
||||
onClick: this.handleReblogClick,
|
||||
onClick: this.handleRepostClick,
|
||||
},
|
||||
{
|
||||
title: formatMessage(messages.share),
|
||||
icon: 'share',
|
||||
active: false,
|
||||
onClick: this.handleFavouriteClick,
|
||||
onClick: this.handleFavoriteClick,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -122,7 +122,7 @@ export default class StatusHeader extends ImmutablePureComponent {
|
||||
menu.push({ text: formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||
} else {
|
||||
if (status.get('visibility') === 'private') {
|
||||
menu.push({ text: formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick });
|
||||
menu.push({ text: formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleRepostClick });
|
||||
}
|
||||
}
|
||||
menu.push({ text: formatMessage(messages.delete), action: this.handleDeleteClick });
|
||||
|
@ -7,9 +7,9 @@ import {
|
||||
} from '../actions/compose';
|
||||
import {
|
||||
reblog,
|
||||
favourite,
|
||||
favorite,
|
||||
unreblog,
|
||||
unfavourite,
|
||||
unfavorite,
|
||||
pin,
|
||||
unpin,
|
||||
} from '../actions/interactions';
|
||||
@ -88,19 +88,19 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
});
|
||||
},
|
||||
|
||||
onModalReblog (status) {
|
||||
onModalRepost (status) {
|
||||
if (status.get('reblogged')) {
|
||||
dispatch(unreblog(status));
|
||||
dispatch(unrepost(status));
|
||||
} else {
|
||||
dispatch(reblog(status));
|
||||
dispatch(repost(status));
|
||||
}
|
||||
},
|
||||
|
||||
onReblog (status, e) {
|
||||
onRepost (status, e) {
|
||||
if (e.shiftKey || !boostModal) {
|
||||
this.onModalReblog(status);
|
||||
this.onModalRepost(status);
|
||||
} else {
|
||||
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
|
||||
dispatch(openModal('BOOST', { status, onRepost: this.onModalRepost }));
|
||||
}
|
||||
},
|
||||
|
||||
@ -108,11 +108,11 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
dispatch(openModal('STATUS_REVISION', { status }));
|
||||
},
|
||||
|
||||
onFavourite (status) {
|
||||
onFavorite (status) {
|
||||
if (status.get('favourited')) {
|
||||
dispatch(unfavourite(status));
|
||||
dispatch(unfavorite(status));
|
||||
} else {
|
||||
dispatch(favourite(status));
|
||||
dispatch(favorite(status));
|
||||
}
|
||||
},
|
||||
|
||||
|
125
app/javascript/gabsocial/features/account_gallery.js
Normal file
125
app/javascript/gabsocial/features/account_gallery.js
Normal file
@ -0,0 +1,125 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { injectIntl, defineMessages } from 'react-intl'
|
||||
import { expandAccountMediaTimeline } from '../actions/timelines'
|
||||
import { getAccountGallery } from '../selectors'
|
||||
import ColumnIndicator from '../components/column_indicator'
|
||||
import MediaItem from '../components/media_item'
|
||||
import LoadMore from '../components/load_more'
|
||||
import Block from '../components/block'
|
||||
|
||||
const messages = defineMessages({
|
||||
none: { id: 'account_gallery.none', defaultMessage: 'No media to show.' },
|
||||
})
|
||||
|
||||
const mapStateToProps = (state, { account }) => {
|
||||
const accountId = !!account ? account.get('id') : -1
|
||||
|
||||
return {
|
||||
accountId,
|
||||
attachments: getAccountGallery(state, accountId),
|
||||
isLoading: state.getIn(['timelines', `account:${accountId}:media`, 'isLoading']),
|
||||
hasMore: state.getIn(['timelines', `account:${accountId}:media`, 'hasMore']),
|
||||
}
|
||||
}
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
@injectIntl
|
||||
class AccountGallery extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
account: ImmutablePropTypes.map,
|
||||
accountId: PropTypes.string,
|
||||
attachments: ImmutablePropTypes.list.isRequired,
|
||||
isLoading: PropTypes.bool,
|
||||
hasMore: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { accountId } = this.props
|
||||
|
||||
if (accountId) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(accountId))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.accountId && nextProps.accountId !== this.props.accountId) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId))
|
||||
}
|
||||
}
|
||||
|
||||
handleScrollToBottom = () => {
|
||||
if (this.props.hasMore) {
|
||||
this.handleLoadMore(this.props.attachments.size > 0 ? this.props.attachments.last().getIn(['status', 'id']) : undefined)
|
||||
}
|
||||
}
|
||||
|
||||
handleScroll = e => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||
const offset = scrollHeight - scrollTop - clientHeight
|
||||
|
||||
if (150 > offset && !this.props.isLoading) {
|
||||
this.handleScrollToBottom()
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
if (this.props.accountId && this.props.accountId !== -1) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, { maxId }))
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadOlder = e => {
|
||||
e.preventDefault()
|
||||
this.handleScrollToBottom()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
attachments,
|
||||
isLoading,
|
||||
hasMore,
|
||||
intl,
|
||||
account
|
||||
} = this.props
|
||||
|
||||
if (!account) return null
|
||||
|
||||
return (
|
||||
<Block>
|
||||
<div
|
||||
role='feed'
|
||||
onScroll={this.handleScroll}
|
||||
className={[_s.default, _s.flexRow, _s.flexWrap, _s.heightMin50VH, _s.paddingVertical5PX, _s.paddingHorizontal5PX].join(' ')}
|
||||
>
|
||||
|
||||
{
|
||||
attachments.map((attachment) => (
|
||||
<MediaItem key={attachment.get('id')} attachment={attachment} />
|
||||
))
|
||||
}
|
||||
|
||||
{
|
||||
isLoading && attachments.size === 0 &&
|
||||
<ColumnIndicator type='loading' />
|
||||
}
|
||||
|
||||
{ /*
|
||||
attachments.size == 0 &&
|
||||
<ColumnIndicator type='empty' message={intl.formatMessage(messages.none)} />
|
||||
*/ }
|
||||
|
||||
{
|
||||
hasMore && !(isLoading && attachments.size === 0) &&
|
||||
<LoadMore visible={!isLoading} onClick={this.handleLoadOlder} />
|
||||
}
|
||||
</div>
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
import {
|
||||
fetchAccount,
|
||||
fetchAccountByUsername,
|
||||
} from '../../actions/accounts';
|
||||
import { openModal } from '../../actions/modal';
|
||||
import { expandAccountMediaTimeline } from '../../actions/timelines';
|
||||
import { me } from '../../initial_state';
|
||||
import { getAccountGallery } from '../../selectors';
|
||||
import ColumnIndicator from '../../components/column_indicator';
|
||||
import MediaItem from './components/media_item';
|
||||
import LoadMore from '../../components/load_more';
|
||||
|
||||
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' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, { mediaType, params: { username } }) => {
|
||||
const accounts = state.getIn(['accounts']);
|
||||
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase());
|
||||
|
||||
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 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));
|
||||
|
||||
return {
|
||||
accountId,
|
||||
unavailable,
|
||||
accountUsername,
|
||||
isAccount: !!state.getIn(['accounts', accountId]),
|
||||
attachments: getAccountGallery(state, accountId),
|
||||
isLoading: state.getIn(['timelines', `account:${accountId}:media`, 'isLoading']),
|
||||
hasMore: state.getIn(['timelines', `account:${accountId}:media`, 'hasMore']),
|
||||
};
|
||||
};
|
||||
|
||||
class LoadMoreMedia extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
maxId: PropTypes.string,
|
||||
onLoadMore: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
handleLoadMore = () => {
|
||||
this.props.onLoadMore(this.props.maxId);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<LoadMore
|
||||
disabled={this.props.disabled}
|
||||
onClick={this.handleLoadMore}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
@injectIntl
|
||||
class AccountGallery extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
attachments: ImmutablePropTypes.list.isRequired,
|
||||
isLoading: PropTypes.bool,
|
||||
hasMore: PropTypes.bool,
|
||||
isAccount: PropTypes.bool,
|
||||
unavailable: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
width: 323,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { params: { username }, accountId } = this.props;
|
||||
|
||||
if (accountId && accountId !== -1) {
|
||||
this.props.dispatch(fetchAccount(accountId));
|
||||
this.props.dispatch(expandAccountMediaTimeline(accountId));
|
||||
} else {
|
||||
this.props.dispatch(fetchAccountByUsername(username));
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) {
|
||||
this.props.dispatch(fetchAccount(nextProps.params.accountId));
|
||||
this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId));
|
||||
}
|
||||
}
|
||||
|
||||
handleScrollToBottom = () => {
|
||||
if (this.props.hasMore) {
|
||||
this.handleLoadMore(this.props.attachments.size > 0 ? this.props.attachments.last().getIn(['status', 'id']) : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
handleScroll = e => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
||||
const offset = scrollHeight - scrollTop - clientHeight;
|
||||
|
||||
if (150 > offset && !this.props.isLoading) {
|
||||
this.handleScrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
if (this.props.accountId && this.props.accountId !== -1) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, { maxId }));
|
||||
}
|
||||
};
|
||||
|
||||
handleLoadOlder = e => {
|
||||
e.preventDefault();
|
||||
this.handleScrollToBottom();
|
||||
}
|
||||
|
||||
handleOpenMedia = attachment => {
|
||||
if (attachment.get('type') === 'video') {
|
||||
this.props.dispatch(openModal('VIDEO', { media: attachment, status: attachment.get('status') }));
|
||||
} else {
|
||||
const media = attachment.getIn(['status', 'media_attachments']);
|
||||
const index = media.findIndex(x => x.get('id') === attachment.get('id'));
|
||||
|
||||
this.props.dispatch(openModal('MEDIA', { media, index, status: attachment.get('status') }));
|
||||
}
|
||||
}
|
||||
|
||||
handleRef = c => {
|
||||
if (c) {
|
||||
this.setState({ width: c.offsetWidth });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { attachments, isLoading, hasMore, isAccount, accountId, unavailable, accountUsername, intl } = this.props;
|
||||
const { width } = this.state;
|
||||
|
||||
if (!isAccount && accountId !== -1) {
|
||||
return <ColumnIndicator type='missing' />
|
||||
} else if (accountId === -1 || (!attachments && isLoading)) {
|
||||
return <ColumnIndicator type='loading' />
|
||||
} else if (unavailable) {
|
||||
return <ColumnIndicator type='error' message={intl.formatMessage(messages.error)} />
|
||||
}
|
||||
|
||||
let loadOlder = null
|
||||
|
||||
if (hasMore && !(isLoading && attachments.size === 0)) {
|
||||
loadOlder = <LoadMore visible={!isLoading} onClick={this.handleLoadOlder} />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='scrollable-list scrollable-list--flex' onScroll={this.handleScroll}>
|
||||
<div role='feed' className='account-gallery__container' ref={this.handleRef}>
|
||||
{
|
||||
attachments.map((attachment, index) => attachment === null ? (
|
||||
<LoadMoreMedia key={'more:' + attachments.getIn(index + 1, 'id')} maxId={index > 0 ? attachments.getIn(index - 1, 'id') : null} onLoadMore={this.handleLoadMore} />
|
||||
) : (
|
||||
<MediaItem key={attachment.get('id')} attachment={attachment} displayWidth={width} onOpenMedia={this.handleOpenMedia} />
|
||||
))
|
||||
}
|
||||
|
||||
{
|
||||
attachments.size == 0 &&
|
||||
<div className='empty-column-indicator'>
|
||||
<FormattedMessage id='account_gallery.none' defaultMessage='No media to show.' />
|
||||
</div>
|
||||
}
|
||||
|
||||
{loadOlder}
|
||||
</div>
|
||||
|
||||
{
|
||||
isLoading && attachments.size === 0 &&
|
||||
<div className='slist__append'>
|
||||
<ColumnIndicator type='loading' />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
.account-gallery__container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 4px 2px;
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './media_item'
|
@ -1,156 +0,0 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import classNames from 'classnames';
|
||||
import { decode } from 'blurhash';
|
||||
import Icon from '../../../../components/icon';
|
||||
import { autoPlayGif, displayMedia } from '../../../../initial_state';
|
||||
import { isIOS } from '../../../../utils/is_mobile';
|
||||
|
||||
export default class MediaItem extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
attachment: ImmutablePropTypes.map.isRequired,
|
||||
displayWidth: PropTypes.number.isRequired,
|
||||
onOpenMedia: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
|
||||
loaded: false,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
if (this.props.attachment.get('blurhash')) {
|
||||
this._decode();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (prevProps.attachment.get('blurhash') !== this.props.attachment.get('blurhash') && this.props.attachment.get('blurhash')) {
|
||||
this._decode();
|
||||
}
|
||||
}
|
||||
|
||||
_decode () {
|
||||
const hash = this.props.attachment.get('blurhash');
|
||||
const pixels = decode(hash, 32, 32);
|
||||
|
||||
if (pixels) {
|
||||
const ctx = this.canvas.getContext('2d');
|
||||
const imageData = new ImageData(pixels, 32, 32);
|
||||
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
setCanvasRef = c => {
|
||||
this.canvas = c;
|
||||
}
|
||||
|
||||
handleImageLoad = () => {
|
||||
this.setState({ loaded: true });
|
||||
}
|
||||
|
||||
handleMouseEnter = e => {
|
||||
if (this.hoverToPlay()) {
|
||||
e.target.play();
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseLeave = e => {
|
||||
if (this.hoverToPlay()) {
|
||||
e.target.pause();
|
||||
e.target.currentTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
hoverToPlay () {
|
||||
return !autoPlayGif && ['gifv', 'video'].indexOf(this.props.attachment.get('type')) !== -1;
|
||||
}
|
||||
|
||||
handleClick = e => {
|
||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.state.visible) {
|
||||
this.props.onOpenMedia(this.props.attachment);
|
||||
} else {
|
||||
this.setState({ visible: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { attachment, displayWidth } = this.props;
|
||||
const { visible, loaded } = this.state;
|
||||
|
||||
const width = `${Math.floor((displayWidth - 4) / 3) - 4}px`;
|
||||
const height = width;
|
||||
const status = attachment.get('status');
|
||||
const title = status.get('spoiler_text') || attachment.get('description');
|
||||
|
||||
let thumbnail = '';
|
||||
let icon;
|
||||
|
||||
if (attachment.get('type') === 'unknown') {
|
||||
// Skip
|
||||
} else if (attachment.get('type') === 'image') {
|
||||
const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
|
||||
const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
|
||||
const x = ((focusX / 2) + .5) * 100;
|
||||
const y = ((focusY / -2) + .5) * 100;
|
||||
|
||||
thumbnail = (
|
||||
<img
|
||||
src={attachment.get('preview_url')}
|
||||
alt={attachment.get('description')}
|
||||
title={attachment.get('description')}
|
||||
style={{ objectPosition: `${x}% ${y}%` }}
|
||||
onLoad={this.handleImageLoad}
|
||||
/>
|
||||
);
|
||||
} else if (['gifv', 'video'].indexOf(attachment.get('type')) !== -1) {
|
||||
const autoPlay = !isIOS() && autoPlayGif;
|
||||
|
||||
thumbnail = (
|
||||
<div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
|
||||
<video
|
||||
className='media-gallery__item-gifv-thumbnail'
|
||||
aria-label={attachment.get('description')}
|
||||
title={attachment.get('description')}
|
||||
role='application'
|
||||
src={attachment.get('url')}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
autoPlay={autoPlay}
|
||||
preload='auto'
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
|
||||
<span className='media-gallery__gifv__label'>GIF</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!visible) {
|
||||
icon = (
|
||||
<span className='account-gallery__item__icons'>
|
||||
<Icon id='eye-slash' />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account-gallery__item' style={{ width, height }}>
|
||||
<a href={status.get('url')} target='_blank' onClick={this.handleClick} title={title}>
|
||||
<canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })} />
|
||||
{visible && thumbnail}
|
||||
{!visible && icon}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
.account-gallery__item {
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin: 2px;
|
||||
|
||||
&__icons {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './account_gallery'
|
@ -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)}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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}
|
||||
|
@ -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')) */ }
|
||||
|
@ -1 +0,0 @@
|
||||
export { default } from './profile_info_panel'
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { debounce } from 'lodash';
|
||||
import ColumnIndicator from '../../components/column_indicator';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
import { fetchBlocks, expandBlocks } from '../../actions/blocks';
|
||||
import ScrollableList from '../../components/scrollable_list';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { debounce } from 'lodash'
|
||||
import ColumnIndicator from '../components/column_indicator'
|
||||
import AccountContainer from '../containers/account_container'
|
||||
import { fetchBlocks, expandBlocks } from '../actions/blocks'
|
||||
import ScrollableList from '../components/scrollable_list'
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.blocks', defaultMessage: 'Blocked users' },
|
@ -1 +0,0 @@
|
||||
export { default } from './blocks'
|
@ -2,10 +2,10 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { debounce } from 'lodash';
|
||||
import { fetchDomainBlocks, expandDomainBlocks } from '../../actions/domain_blocks';
|
||||
import DomainContainer from '../../containers/domain_container';
|
||||
import ColumnIndicator from '../../components/column_indicator';
|
||||
import ScrollableList from '../../components/scrollable_list';
|
||||
import { fetchDomainBlocks, expandDomainBlocks } from '../actions/domain_blocks';
|
||||
import DomainContainer from '../containers/domain_container';
|
||||
import ColumnIndicator from '../components/column_indicator';
|
||||
import ScrollableList from '../components/scrollable_list';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.domain_blocks', defaultMessage: 'Hidden domains' },
|
@ -1 +1 @@
|
||||
export { default } from './domain_blocks'
|
||||
export { default } from '../domain_blocks'
|
@ -1,7 +1,7 @@
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { fetchReblogs } from '../../actions/interactions';
|
||||
import { fetchReposts } from '../../actions/interactions';
|
||||
import { fetchStatus } from '../../actions/statuses';
|
||||
import { makeGetStatus } from '../../selectors';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
@ -33,13 +33,13 @@ class Favorites extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
||||
this.props.dispatch(fetchReposts(this.props.params.statusId));
|
||||
this.props.dispatch(fetchStatus(this.props.params.statusId));
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
||||
this.props.dispatch(fetchReblogs(nextProps.params.statusId));
|
||||
this.props.dispatch(fetchReposts(nextProps.params.statusId));
|
||||
this.props.dispatch(fetchStatus(nextProps.params.statusId));
|
||||
}
|
||||
}
|
||||
|
100
app/javascript/gabsocial/features/followers.js
Normal file
100
app/javascript/gabsocial/features/followers.js
Normal file
@ -0,0 +1,100 @@
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { debounce } from 'lodash'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import {
|
||||
fetchFollowers,
|
||||
expandFollowers,
|
||||
} from '../actions/accounts'
|
||||
import AccountContainer from '../containers/account_container'
|
||||
import ScrollableList from '../components/scrollable_list'
|
||||
import Block from '../components/block'
|
||||
import Heading from '../components/heading'
|
||||
|
||||
const mapStateToProps = (state, { account }) => {
|
||||
const accountId = !!account ? account.get('id') : -1
|
||||
|
||||
return {
|
||||
accountId,
|
||||
accountIds: state.getIn(['user_lists', 'followers', accountId, 'items']),
|
||||
hasMore: !!state.getIn(['user_lists', 'followers', accountId, 'next']),
|
||||
}
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
followers: { id: 'account.followers', defaultMessage: 'Followers' },
|
||||
empty: { id: 'account.followers.empty', defaultMessage: 'No one follows this user yet.' },
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
@injectIntl
|
||||
class Followers extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
accountId: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
hasMore: PropTypes.bool,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { accountId } = this.props
|
||||
|
||||
if (accountId && accountId !== -1) {
|
||||
this.props.dispatch(fetchFollowers(accountId))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!!nextProps.accountId && nextProps.accountId !== -1 && nextProps.accountId !== this.props.accountId) {
|
||||
this.props.dispatch(fetchFollowers(nextProps.accountId))
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
const { accountId } = this.props
|
||||
if (!!accountId && accountId !== -1) {
|
||||
this.props.dispatch(expandFollowers(accountId))
|
||||
}
|
||||
}, 300, { leading: true })
|
||||
|
||||
render() {
|
||||
const {
|
||||
account,
|
||||
accountIds,
|
||||
hasMore,
|
||||
intl
|
||||
} = this.props
|
||||
|
||||
if (!account) return null
|
||||
|
||||
return (
|
||||
<Block>
|
||||
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX, _s.justifyContentCenter, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}>
|
||||
<Heading size='h3'>
|
||||
{intl.formatMessage(messages.followers)}
|
||||
</Heading>
|
||||
</div>
|
||||
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>
|
||||
<ScrollableList
|
||||
scrollKey='followers'
|
||||
hasMore={hasMore}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={intl.formatMessage(messages.empty)}
|
||||
>
|
||||
{
|
||||
!!accountIds && accountIds.map((id) => (
|
||||
<AccountContainer key={`follower-${id}`} id={id} withNote={false} compact />
|
||||
))
|
||||
}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { debounce } from 'lodash'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ColumnIndicator from '../../components/column_indicator'
|
||||
import {
|
||||
fetchAccount,
|
||||
fetchFollowers,
|
||||
expandFollowers,
|
||||
fetchAccountByUsername,
|
||||
} from '../../actions/accounts'
|
||||
import { me } from '../../initial_state'
|
||||
import AccountContainer from '../../containers/account_container'
|
||||
import ScrollableList from '../../components/scrollable_list'
|
||||
import Block from '../../components/block'
|
||||
import Heading from '../../components/heading'
|
||||
|
||||
const mapStateToProps = (state, { params: { username } }) => {
|
||||
const accounts = state.getIn(['accounts'])
|
||||
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase())
|
||||
|
||||
let accountId = -1
|
||||
if (accountFetchError) {
|
||||
accountId = null
|
||||
} else {
|
||||
let account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase())
|
||||
accountId = account ? account.getIn(['id'], null) : -1
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
return {
|
||||
accountId,
|
||||
unavailable,
|
||||
isAccount: !!state.getIn(['accounts', accountId]),
|
||||
accountIds: state.getIn(['user_lists', 'followers', accountId, 'items']),
|
||||
hasMore: !!state.getIn(['user_lists', 'followers', accountId, 'next']),
|
||||
}
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
followers: { id: 'account.followers', defaultMessage: 'Followers' },
|
||||
empty: { id: 'account.followers.empty', defaultMessage: 'No one follows this user yet.' },
|
||||
unavailable: { id: 'empty_column.account_unavailable', defaultMessage: 'Profile unavailable' },
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
@injectIntl
|
||||
class Followers extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
hasMore: PropTypes.bool,
|
||||
isAccount: PropTypes.bool,
|
||||
unavailable: PropTypes.bool,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { params: { username }, accountId } = this.props
|
||||
|
||||
if (accountId && accountId !== -1) {
|
||||
this.props.dispatch(fetchAccount(accountId))
|
||||
this.props.dispatch(fetchFollowers(accountId))
|
||||
} else {
|
||||
this.props.dispatch(fetchAccountByUsername(username))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) {
|
||||
this.props.dispatch(fetchAccount(nextProps.accountId))
|
||||
this.props.dispatch(fetchFollowers(nextProps.accountId))
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
if (this.props.accountId && this.props.accountId !== -1) {
|
||||
this.props.dispatch(expandFollowers(this.props.accountId))
|
||||
}
|
||||
}, 300, { leading: true })
|
||||
|
||||
render() {
|
||||
const {
|
||||
accountIds,
|
||||
hasMore,
|
||||
isAccount,
|
||||
accountId,
|
||||
unavailable,
|
||||
intl
|
||||
} = this.props
|
||||
|
||||
if (!isAccount && accountId !== -1) {
|
||||
return <ColumnIndicator type='missing' />
|
||||
} else if (accountId === -1 || (!accountIds)) {
|
||||
return <ColumnIndicator type='loading' />
|
||||
} else if (unavailable) {
|
||||
return <ColumnIndicator type='error' message={intl.formatMessage(messages.unavailable)} />
|
||||
}
|
||||
|
||||
return (
|
||||
<Block>
|
||||
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX, _s.justifyContentCenter, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}>
|
||||
<Heading size='h3'>
|
||||
{intl.formatMessage(messages.followers)}
|
||||
</Heading>
|
||||
</div>
|
||||
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>
|
||||
<ScrollableList
|
||||
scrollKey='followers'
|
||||
hasMore={hasMore}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={intl.formatMessage(messages.empty)}
|
||||
>
|
||||
{
|
||||
accountIds.map((id, i) => (
|
||||
<AccountContainer key={id} id={id} withNote={false} compact />
|
||||
))
|
||||
}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './followers'
|
100
app/javascript/gabsocial/features/following.js
Normal file
100
app/javascript/gabsocial/features/following.js
Normal file
@ -0,0 +1,100 @@
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { debounce } from 'lodash'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import {
|
||||
fetchFollowing,
|
||||
expandFollowing,
|
||||
} from '../actions/accounts'
|
||||
import AccountContainer from '../containers/account_container'
|
||||
import ScrollableList from '../components/scrollable_list'
|
||||
import Block from '../components/block'
|
||||
import Heading from '../components/heading'
|
||||
|
||||
const mapStateToProps = (state, { account }) => {
|
||||
const accountId = !!account ? account.get('id') : -1
|
||||
|
||||
return {
|
||||
accountId,
|
||||
accountIds: state.getIn(['user_lists', 'following', accountId, 'items']),
|
||||
hasMore: !!state.getIn(['user_lists', 'following', accountId, 'next']),
|
||||
}
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
follows: { id: 'account.follows', defaultMessage: 'Follows' },
|
||||
empty: { id: 'account.follows.empty', defaultMessage: 'This user doesn\'t follow anyone yet.' },
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
@injectIntl
|
||||
class Following extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
account: ImmutablePropTypes.map,
|
||||
accountId: PropTypes.string,
|
||||
hasMore: PropTypes.bool,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { accountId } = this.props
|
||||
|
||||
if (!!accountId && accountId !== -1) {
|
||||
this.props.dispatch(fetchFollowing(accountId))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!!nextProps.accountId && nextProps.accountId !== -1 && nextProps.accountId !== this.props.accountId) {
|
||||
this.props.dispatch(fetchFollowing(nextProps.accountId))
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
const { accountId } = this.props
|
||||
if (!!accountId && accountId !== -1) {
|
||||
this.props.dispatch(expandFollowing(accountId))
|
||||
}
|
||||
}, 300, { leading: true })
|
||||
|
||||
render() {
|
||||
const {
|
||||
account,
|
||||
accountIds,
|
||||
hasMore,
|
||||
intl
|
||||
} = this.props
|
||||
|
||||
if (!account) return null
|
||||
|
||||
return (
|
||||
<Block>
|
||||
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX, _s.justifyContentCenter, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}>
|
||||
<Heading size='h3'>
|
||||
{intl.formatMessage(messages.follows)}
|
||||
</Heading>
|
||||
</div>
|
||||
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>
|
||||
<ScrollableList
|
||||
scrollKey='following'
|
||||
hasMore={hasMore}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={intl.formatMessage(messages.empty)}
|
||||
>
|
||||
{
|
||||
!!accountIds && accountIds.map((id) => (
|
||||
<AccountContainer key={`following-${id}`} id={id} withNote={false} compact />
|
||||
))
|
||||
}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
import { Fragment } from 'react'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { debounce } from 'lodash'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import {
|
||||
fetchAccount,
|
||||
fetchFollowing,
|
||||
expandFollowing,
|
||||
fetchAccountByUsername,
|
||||
} from '../../actions/accounts'
|
||||
import { me } from '../../initial_state'
|
||||
import AccountContainer from '../../containers/account_container'
|
||||
import ColumnIndicator from '../../components/column_indicator'
|
||||
import ScrollableList from '../../components/scrollable_list'
|
||||
import Block from '../../components/block'
|
||||
import Divider from '../../components/divider'
|
||||
import Heading from '../../components/heading'
|
||||
|
||||
const mapStateToProps = (state, { params: { username } }) => {
|
||||
const accounts = state.getIn(['accounts'])
|
||||
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase())
|
||||
|
||||
let accountId = -1
|
||||
if (accountFetchError) {
|
||||
accountId = null
|
||||
} else {
|
||||
let account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase())
|
||||
accountId = account ? account.getIn(['id'], null) : -1
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
return {
|
||||
accountId,
|
||||
unavailable,
|
||||
isAccount: !!state.getIn(['accounts', accountId]),
|
||||
accountIds: state.getIn(['user_lists', 'following', accountId, 'items']),
|
||||
hasMore: !!state.getIn(['user_lists', 'following', accountId, 'next']),
|
||||
}
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
follows: { id: 'account.follows', defaultMessage: 'Follows' },
|
||||
empty: { id: 'account.follows.empty', defaultMessage: 'This user doesn\'t follow anyone yet.' },
|
||||
unavailable: { id: 'empty_column.account_unavailable', defaultMessage: 'Profile unavailable' },
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
@injectIntl
|
||||
class Following extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
hasMore: PropTypes.bool,
|
||||
isAccount: PropTypes.bool,
|
||||
unavailable: PropTypes.bool,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { params: { username }, accountId } = this.props
|
||||
|
||||
if (accountId && accountId !== -1) {
|
||||
this.props.dispatch(fetchAccount(accountId))
|
||||
this.props.dispatch(fetchFollowing(accountId))
|
||||
} else {
|
||||
this.props.dispatch(fetchAccountByUsername(username))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) {
|
||||
this.props.dispatch(fetchAccount(nextProps.accountId))
|
||||
this.props.dispatch(fetchFollowing(nextProps.accountId))
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
if (this.props.accountId && this.props.accountId !== -1) {
|
||||
this.props.dispatch(expandFollowing(this.props.accountId))
|
||||
}
|
||||
}, 300, { leading: true })
|
||||
|
||||
render() {
|
||||
const {
|
||||
accountIds,
|
||||
hasMore,
|
||||
isAccount,
|
||||
accountId,
|
||||
unavailable,
|
||||
intl
|
||||
} = this.props
|
||||
|
||||
if (!isAccount && accountId !== -1) {
|
||||
return <ColumnIndicator type='missing' />
|
||||
} else if (accountId === -1 || (!accountIds)) {
|
||||
return <ColumnIndicator type='loading' />
|
||||
} else if (unavailable) {
|
||||
return <ColumnIndicator type='error' message={intl.formatMessage(messages.unavailable)} />
|
||||
}
|
||||
|
||||
return (
|
||||
<Block>
|
||||
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX, _s.justifyContentCenter, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}>
|
||||
<Heading size='h3'>
|
||||
{intl.formatMessage(messages.follows)}
|
||||
</Heading>
|
||||
</div>
|
||||
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>
|
||||
<ScrollableList
|
||||
scrollKey='following'
|
||||
hasMore={hasMore}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={intl.formatMessage(messages.empty)}
|
||||
>
|
||||
{
|
||||
accountIds.map((id) => (
|
||||
<AccountContainer key={id} id={id} withNote={false} compact />
|
||||
))
|
||||
}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1 +1 @@
|
||||
export { default } from './following'
|
||||
export { default } from '../following'
|
@ -1,4 +1,4 @@
|
||||
import ColumnIndicator from '../../components/column_indicator';
|
||||
import ColumnIndicator from '../components/column_indicator';
|
||||
|
||||
export default class GenericNotFound extends PureComponent {
|
||||
|
@ -1 +1 @@
|
||||
export { default } from './generic_not_found'
|
||||
export { default } from '../generic_not_found'
|
@ -3,11 +3,11 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { injectIntl, defineMessages } from 'react-intl'
|
||||
import { Link } from 'react-router-dom'
|
||||
import classNames from 'classnames'
|
||||
import { connectGroupStream } from '../../actions/streaming'
|
||||
import { expandGroupTimeline } from '../../actions/timelines'
|
||||
import StatusListContainer from '../../containers/status_list_container'
|
||||
import { connectGroupStream } from '../actions/streaming'
|
||||
import { expandGroupTimeline } from '../actions/timelines'
|
||||
import StatusListContainer from '../containers/status_list_container'
|
||||
// import ColumnSettingsContainer from './containers/column_settings_container'
|
||||
import ColumnIndicator from '../../components/column_indicator'
|
||||
import ColumnIndicator from '../components/column_indicator'
|
||||
|
||||
const messages = defineMessages({
|
||||
tabLatest: { id: 'group.timeline.tab_latest', defaultMessage: 'Latest' },
|
@ -1 +1 @@
|
||||
export { default } from './group_timeline'
|
||||
export { default } from '../group_timeline'
|
||||
|
@ -1,8 +1,8 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { fetchGroups } from '../../actions/groups'
|
||||
import Block from '../../components/block'
|
||||
import GroupCollectionItem from '../../components/group_collection_item'
|
||||
import { fetchGroups } from '../actions/groups'
|
||||
import Block from '../components/block'
|
||||
import GroupCollectionItem from '../components/group_collection_item'
|
||||
|
||||
const mapStateToProps = (state, { activeTab }) => ({
|
||||
groupIds: state.getIn(['group_lists', activeTab]),
|
@ -1 +1 @@
|
||||
export { default } from './groups_collection'
|
||||
export { default } from '../groups_collection'
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { FormattedMessage } from 'react-intl'
|
||||
import { isEqual } from 'lodash'
|
||||
import { expandHashtagTimeline, clearTimeline } from '../../actions/timelines'
|
||||
import { connectHashtagStream } from '../../actions/streaming'
|
||||
import StatusListContainer from '../../containers/status_list_container'
|
||||
import { expandHashtagTimeline, clearTimeline } from '../actions/timelines'
|
||||
import { connectHashtagStream } from '../actions/streaming'
|
||||
import StatusListContainer from '../containers/status_list_container'
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
hasUnread: state.getIn(['timelines', `hashtag:${props.params.id}`, 'unread']) > 0,
|
@ -1 +0,0 @@
|
||||
export { default } from './hashtag_timeline'
|
@ -115,10 +115,10 @@ class FrameInteractions extends Component {
|
||||
|
||||
<div>
|
||||
<h3>
|
||||
<FormattedMessage id='introduction.interactions.favourite.headline' defaultMessage='Favorite' />
|
||||
<FormattedMessage id='introduction.interactions.favorite.headline' defaultMessage='Favorite' />
|
||||
</h3>
|
||||
<p>
|
||||
<FormattedMessage id='introduction.interactions.favourite.text' defaultMessage='You can save a gab for later, and let the author know that you liked it, by favouriting it.' />
|
||||
<FormattedMessage id='introduction.interactions.favorite.text' defaultMessage='You can save a gab for later, and let the author know that you liked it, by favoriting it.' />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,13 +1,13 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
import { connectListStream } from '../../actions/streaming';
|
||||
import { expandListTimeline } from '../../actions/timelines';
|
||||
import { fetchList, deleteList } from '../../actions/lists';
|
||||
import { openModal } from '../../actions/modal';
|
||||
import StatusListContainer from '../../containers/status_list_container';
|
||||
import ColumnIndicator from '../../components/column_indicator';
|
||||
import Button from '../../components/button';
|
||||
import { connectListStream } from '../actions/streaming';
|
||||
import { expandListTimeline } from '../actions/timelines';
|
||||
import { fetchList, deleteList } from '../actions/lists';
|
||||
import { openModal } from '../actions/modal';
|
||||
import StatusListContainer from '../containers/status_list_container';
|
||||
import ColumnIndicator from '../components/column_indicator';
|
||||
import Button from '../components/button';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteMessage: { id: 'confirmations.delete_list.message', defaultMessage: 'Are you sure you want to permanently delete this list?' },
|
@ -1 +0,0 @@
|
||||
export { default } from './list_timeline'
|
@ -2,10 +2,10 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { debounce } from 'lodash';
|
||||
import { fetchMutes, expandMutes } from '../../actions/mutes';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
import ColumnIndicator from '../../components/column_indicator';
|
||||
import ScrollableList from '../../components/scrollable_list';
|
||||
import { fetchMutes, expandMutes } from '../actions/mutes';
|
||||
import AccountContainer from '../containers/account_container';
|
||||
import ColumnIndicator from '../components/column_indicator';
|
||||
import ScrollableList from '../components/scrollable_list';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.mutes', defaultMessage: 'Muted users' },
|
@ -1 +1 @@
|
||||
export { default } from './mutes'
|
||||
export { default } from '../mutes'
|
@ -51,12 +51,12 @@ export default class ColumnSettings extends ImmutablePureComponent {
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'follow']} onChange={onChange} label={soundStr} />
|
||||
</div>
|
||||
|
||||
<div role='group' aria-labelledby='notifications-favourite'>
|
||||
<FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favorites:' />
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'favourite']} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'favourite']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'favourite']} onChange={onChange} label={soundStr} />
|
||||
<div role='group' aria-labelledby='notifications-favorite'>
|
||||
<FormattedMessage id='notifications.column_settings.favorite' defaultMessage='Favorites:' />
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favorite']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'favorite']} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'favorite']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'favorite']} onChange={onChange} label={soundStr} />
|
||||
</div>
|
||||
|
||||
<div role='group' aria-labelledby='notifications-mention'>
|
||||
|
@ -29,8 +29,8 @@ class Notification extends ImmutablePureComponent {
|
||||
onMoveUp: PropTypes.func.isRequired,
|
||||
onMoveDown: PropTypes.func.isRequired,
|
||||
onMention: PropTypes.func.isRequired,
|
||||
onFavourite: PropTypes.func.isRequired,
|
||||
onReblog: PropTypes.func.isRequired,
|
||||
onFavorite: PropTypes.func.isRequired,
|
||||
onRepost: PropTypes.func.isRequired,
|
||||
onToggleHidden: PropTypes.func.isRequired,
|
||||
status: ImmutablePropTypes.map,
|
||||
intl: PropTypes.object.isRequired,
|
||||
@ -72,14 +72,14 @@ class Notification extends ImmutablePureComponent {
|
||||
onMention(notification.get('account'), this.context.router.history);
|
||||
}
|
||||
|
||||
handleHotkeyFavourite = () => {
|
||||
handleHotkeyFavorite = () => {
|
||||
const { status } = this.props;
|
||||
if (status) this.props.onFavourite(status);
|
||||
if (status) this.props.onFavorite(status);
|
||||
}
|
||||
|
||||
handleHotkeyBoost = e => {
|
||||
const { status } = this.props;
|
||||
if (status) this.props.onReblog(status, e);
|
||||
if (status) this.props.onRepost(status, e);
|
||||
}
|
||||
|
||||
handleHotkeyToggleHidden = () => {
|
||||
@ -90,7 +90,7 @@ class Notification extends ImmutablePureComponent {
|
||||
getHandlers() {
|
||||
return {
|
||||
reply: this.handleMention,
|
||||
favourite: this.handleHotkeyFavourite,
|
||||
favorite: this.handleHotkeyFavorite,
|
||||
boost: this.handleHotkeyBoost,
|
||||
mention: this.handleMention,
|
||||
open: this.handleOpen,
|
||||
@ -108,7 +108,7 @@ class Notification extends ImmutablePureComponent {
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification--follow focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.follow', defaultMessage: '{name} followed you' }, { name: account.get('acct') }), notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<div className='notification__favorite-icon-wrapper'>
|
||||
<Icon id='user-plus' fixedWidth />
|
||||
</div>
|
||||
|
||||
@ -140,19 +140,19 @@ class Notification extends ImmutablePureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
renderFavourite(notification, link) {
|
||||
renderFavorite(notification, link) {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification--favourite focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.favourite', defaultMessage: '{name} favorited your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div className='notification notification--favorite focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.favorite', defaultMessage: '{name} favorited your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<div className='notification__favorite-icon-wrapper'>
|
||||
<Icon id='star' className='star-icon' fixedWidth />
|
||||
</div>
|
||||
|
||||
<span title={notification.get('created_at')}>
|
||||
<FormattedMessage id='notification.favourite' defaultMessage='{name} favorited your status' values={{ name: link }} />
|
||||
<FormattedMessage id='notification.favorite' defaultMessage='{name} favorited your status' values={{ name: link }} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -173,14 +173,14 @@ class Notification extends ImmutablePureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
renderReblog(notification, link) {
|
||||
renderRepost(notification, link) {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification--reblog focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.reblog', defaultMessage: '{name} reposted your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<div className='notification__favorite-icon-wrapper'>
|
||||
<Icon id='retweet' fixedWidth />
|
||||
</div>
|
||||
|
||||
@ -212,7 +212,7 @@ class Notification extends ImmutablePureComponent {
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification--poll focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' }), notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<div className='notification__favorite-icon-wrapper'>
|
||||
<Icon id='tasks' fixedWidth />
|
||||
</div>
|
||||
|
||||
@ -261,9 +261,9 @@ class Notification extends ImmutablePureComponent {
|
||||
// case 'mention':
|
||||
// return this.renderMention(notification);
|
||||
case 'favourite':
|
||||
return this.renderFavourite(notification, link);
|
||||
return this.renderFavorite(notification, link);
|
||||
// case 'reblog':
|
||||
// return this.renderReblog(notification, link);
|
||||
// return this.renderRepost(notification, link);
|
||||
// case 'poll':
|
||||
// return this.renderPoll(notification);
|
||||
}
|
||||
|
@ -1,59 +0,0 @@
|
||||
.notification {
|
||||
|
||||
|
||||
&--favourite {
|
||||
.status.status-direct {
|
||||
background: transparent;
|
||||
|
||||
.icon-button.disabled {
|
||||
color: lighten($action-button-color, 13%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--follow {}
|
||||
|
||||
&--reblog {}
|
||||
|
||||
&--poll {}
|
||||
|
||||
&__message {
|
||||
margin: 0 10px 0 68px;
|
||||
padding: 8px 0 0;
|
||||
cursor: default;
|
||||
color: $gab-secondary-text;
|
||||
position: relative;
|
||||
|
||||
@include text-sizing(15px, 400, 22px);
|
||||
|
||||
.fa {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
|
||||
>span {
|
||||
display: inline;
|
||||
|
||||
@include text-overflow(nowrap);
|
||||
}
|
||||
}
|
||||
|
||||
&__display-name {
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: $primary-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&__favourite-icon-wrapper {
|
||||
left: -26px;
|
||||
position: absolute;
|
||||
|
||||
.star-icon {
|
||||
color: $gold-star;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
||||
favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favorites' },
|
||||
favorites: { id: 'notifications.filter.favorites', defaultMessage: 'Favorites' },
|
||||
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Reposts' },
|
||||
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
|
||||
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
||||
@ -65,9 +65,9 @@ class NotificationFilterBar extends PureComponent {
|
||||
icon: 'at',
|
||||
},
|
||||
{
|
||||
className: selectedFilter === 'favourite' ? 'active' : '',
|
||||
onClick: this.onClick('favourite'),
|
||||
title: intl.formatMessage(messages.favourites),
|
||||
className: selectedFilter === 'favorite' ? 'active' : '',
|
||||
onClick: this.onClick('favorite'),
|
||||
title: intl.formatMessage(messages.favorites),
|
||||
icon: 'star',
|
||||
},
|
||||
{
|
||||
|
@ -2,9 +2,9 @@ import { openModal } from '../../../actions/modal'
|
||||
import { mentionCompose } from '../../../actions/compose'
|
||||
import {
|
||||
reblog,
|
||||
favourite,
|
||||
favorite,
|
||||
unreblog,
|
||||
unfavourite,
|
||||
unfavorite,
|
||||
} from '../../../actions/interactions'
|
||||
import {
|
||||
hideStatus,
|
||||
@ -38,27 +38,27 @@ const mapDispatchToProps = dispatch => ({
|
||||
dispatch(mentionCompose(account, router))
|
||||
},
|
||||
|
||||
onModalReblog (status) {
|
||||
dispatch(reblog(status))
|
||||
onModalRepost (status) {
|
||||
dispatch(repost(status))
|
||||
},
|
||||
|
||||
onReblog (status, e) {
|
||||
onRepost (status, e) {
|
||||
if (status.get('reblogged')) {
|
||||
dispatch(unreblog(status))
|
||||
dispatch(unrepost(status))
|
||||
} else {
|
||||
if (e.shiftKey || !boostModal) {
|
||||
this.onModalReblog(status)
|
||||
this.onModalRepost(status)
|
||||
} else {
|
||||
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }))
|
||||
dispatch(openModal('BOOST', { status, onRepost: this.onModalRepost }))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onFavourite (status) {
|
||||
if (status.get('favourited')) {
|
||||
dispatch(unfavourite(status))
|
||||
onFavorite (status) {
|
||||
if (status.get('favorited')) {
|
||||
dispatch(unfavorite(status))
|
||||
} else {
|
||||
dispatch(favourite(status))
|
||||
dispatch(favorite(status))
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1 +1 @@
|
||||
export { default } from './reblogs'
|
||||
export { default } from '../reblogs'
|
@ -1,12 +1,12 @@
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { fetchReblogs } from '../../actions/interactions';
|
||||
import { fetchStatus } from '../../actions/statuses';
|
||||
import { makeGetStatus } from '../../selectors';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
import ColumnIndicator from '../../components/column_indicator';
|
||||
import ScrollableList from '../../components/scrollable_list';
|
||||
import { fetchReposts } from '../actions/interactions';
|
||||
import { fetchStatus } from '../actions/statuses';
|
||||
import { makeGetStatus } from '../selectors';
|
||||
import AccountContainer from '../containers/account_container';
|
||||
import ColumnIndicator from '../components/column_indicator';
|
||||
import ScrollableList from '../components/scrollable_list';
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const getStatus = makeGetStatus();
|
||||
@ -23,7 +23,7 @@ const mapStateToProps = (state, props) => {
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
class Reblogs extends ImmutablePureComponent {
|
||||
class Reposts extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
@ -33,13 +33,13 @@ class Reblogs extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
||||
this.props.dispatch(fetchReposts(this.props.params.statusId));
|
||||
this.props.dispatch(fetchStatus(this.props.params.statusId));
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
||||
this.props.dispatch(fetchReblogs(nextProps.params.statusId));
|
||||
this.props.dispatch(fetchReposts(nextProps.params.statusId));
|
||||
this.props.dispatch(fetchStatus(nextProps.params.statusId));
|
||||
}
|
||||
}
|
||||
@ -48,19 +48,21 @@ class Reblogs extends ImmutablePureComponent {
|
||||
const { accountIds, status } = this.props;
|
||||
|
||||
if (!accountIds) {
|
||||
return (<ColumnIndicator type='loading' />);
|
||||
return <ColumnIndicator type='loading' />
|
||||
} else if (!status) {
|
||||
return (<ColumnIndicator type='missing' />);
|
||||
return <ColumnIndicator type='missing' />
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollableList
|
||||
scrollKey='reblogs'
|
||||
scrollKey='reposts'
|
||||
emptyMessage={<FormattedMessage id='status.reblogs.empty' defaultMessage='No one has reposted this gab yet. When someone does, they will show up here.' />}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withNote={false} />
|
||||
)}
|
||||
{
|
||||
accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withNote={false} />
|
||||
)
|
||||
}
|
||||
</ScrollableList>
|
||||
);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
// import SearchContainer from '../compose/containers/search_container';
|
||||
import SearchResultsContainer from '../compose/containers/search_results_container';
|
||||
import SearchResultsContainer from './compose/containers/search_results_container';
|
||||
|
||||
export default class Search extends PureComponent {
|
||||
|
@ -1,57 +0,0 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
value: state.getIn(['search', 'value']),
|
||||
submitted: state.getIn(['search', 'submitted']),
|
||||
});
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
class Header extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string,
|
||||
submitted: PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
submittedValue: '',
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.submitted) {
|
||||
const submittedValue = nextProps.value;
|
||||
this.setState({submittedValue})
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { submittedValue } = this.state;
|
||||
|
||||
if (!submittedValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='search-header'>
|
||||
<div className='search-header__text-container'>
|
||||
<h1 className='search-header__title-text'>
|
||||
{submittedValue}
|
||||
</h1>
|
||||
</div>
|
||||
<div className='search-header__type-filters'>
|
||||
<div className='search-header__type-filters-tabs'>
|
||||
{ /* <SectionHeadlineBar
|
||||
items={[
|
||||
{
|
||||
to: '/search',
|
||||
title: <FormattedMessage id='search_results.top' defaultMessage='Top' />
|
||||
}
|
||||
]}
|
||||
/> */ }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
.search-header {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
&__text-container {
|
||||
display: none;
|
||||
padding: 25px 0;
|
||||
background-color: lighten($ui-base-color, 6%);
|
||||
|
||||
@media (min-width:895px) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&__title-text {
|
||||
color: $primary-text-color;
|
||||
padding-left: 20px;
|
||||
max-width: 1200px;
|
||||
|
||||
@include text-sizing(27px, bold, 32px);
|
||||
@include text-overflow(nowrap);
|
||||
@include margin-center;
|
||||
|
||||
@media (min-width:895px) and (max-width:1190px) {
|
||||
max-width: 900px;
|
||||
}
|
||||
}
|
||||
|
||||
&__type-filters-tabs {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
|
||||
@include margin-center;
|
||||
|
||||
@media screen and (max-width:895px) {
|
||||
max-width: 580px;
|
||||
}
|
||||
|
||||
@media (min-width:895px) and (max-width:1190px) {
|
||||
max-width: 900px;
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './header'
|
@ -1 +1 @@
|
||||
export { default } from './search'
|
||||
export { default } from '../search'
|
@ -6,9 +6,9 @@ import {
|
||||
} from '../../../actions/compose';
|
||||
import {
|
||||
reblog,
|
||||
favourite,
|
||||
favorite,
|
||||
unreblog,
|
||||
unfavourite,
|
||||
unfavorite,
|
||||
pin,
|
||||
unpin,
|
||||
} from '../../../actions/interactions';
|
||||
@ -65,27 +65,27 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
});
|
||||
},
|
||||
|
||||
onModalReblog (status) {
|
||||
dispatch(reblog(status));
|
||||
onModalRepost (status) {
|
||||
dispatch(repost(status));
|
||||
},
|
||||
|
||||
onReblog (status, e) {
|
||||
onRepost (status, e) {
|
||||
if (status.get('reblogged')) {
|
||||
dispatch(unreblog(status));
|
||||
dispatch(unrepost(status));
|
||||
} else {
|
||||
if (e.shiftKey || !boostModal) {
|
||||
this.onModalReblog(status);
|
||||
this.onModalRepost(status);
|
||||
} else {
|
||||
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
|
||||
dispatch(openModal('BOOST', { status, onRepost: this.onModalRepost }));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onFavourite (status) {
|
||||
if (status.get('favourited')) {
|
||||
dispatch(unfavourite(status));
|
||||
onFavorite (status) {
|
||||
if (status.get('favorited')) {
|
||||
dispatch(unfavorite(status));
|
||||
} else {
|
||||
dispatch(favourite(status));
|
||||
dispatch(favorite(status));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -6,8 +6,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import { fetchStatus } from '../../actions/statuses';
|
||||
import {
|
||||
favourite,
|
||||
unfavourite,
|
||||
favorite,
|
||||
unfavorite,
|
||||
reblog,
|
||||
unreblog,
|
||||
pin,
|
||||
@ -155,11 +155,11 @@ class Status extends ImmutablePureComponent {
|
||||
this.setState({ showMedia: !this.state.showMedia });
|
||||
}
|
||||
|
||||
handleFavouriteClick = (status) => {
|
||||
handleFavoriteClick = (status) => {
|
||||
if (status.get('favourited')) {
|
||||
this.props.dispatch(unfavourite(status));
|
||||
this.props.dispatch(unfavorite(status));
|
||||
} else {
|
||||
this.props.dispatch(favourite(status));
|
||||
this.props.dispatch(favorite(status));
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,18 +184,18 @@ class Status extends ImmutablePureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
handleModalReblog = (status) => {
|
||||
this.props.dispatch(reblog(status));
|
||||
handleModalRepost = (status) => {
|
||||
this.props.dispatch(repost(status));
|
||||
}
|
||||
|
||||
handleReblogClick = (status, e) => {
|
||||
handleRepostClick = (status, e) => {
|
||||
if (status.get('reblogged')) {
|
||||
this.props.dispatch(unreblog(status));
|
||||
this.props.dispatch(unrepost(status));
|
||||
} else {
|
||||
if ((e && e.shiftKey) || !boostModal) {
|
||||
this.handleModalReblog(status);
|
||||
this.handleModalRepost(status);
|
||||
} else {
|
||||
this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
|
||||
this.props.dispatch(openModal('BOOST', { status, onRepost: this.handleModalRepost }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -298,12 +298,12 @@ class Status extends ImmutablePureComponent {
|
||||
this.handleReplyClick(this.props.status);
|
||||
}
|
||||
|
||||
handleHotkeyFavourite = () => {
|
||||
this.handleFavouriteClick(this.props.status);
|
||||
handleHotkeyFavorite = () => {
|
||||
this.handleFavoriteClick(this.props.status);
|
||||
}
|
||||
|
||||
handleHotkeyBoost = () => {
|
||||
this.handleReblogClick(this.props.status);
|
||||
this.handleRepostClick(this.props.status);
|
||||
}
|
||||
|
||||
handleHotkeyMention = e => {
|
||||
@ -432,7 +432,7 @@ class Status extends ImmutablePureComponent {
|
||||
moveUp: this.handleHotkeyMoveUp,
|
||||
moveDown: this.handleHotkeyMoveDown,
|
||||
reply: this.handleHotkeyReply,
|
||||
favourite: this.handleHotkeyFavourite,
|
||||
favorite: this.handleHotkeyFavorite,
|
||||
boost: this.handleHotkeyBoost,
|
||||
mention: this.handleHotkeyMention,
|
||||
openProfile: this.handleHotkeyOpenProfile,
|
||||
@ -481,11 +481,11 @@ class Status extends ImmutablePureComponent {
|
||||
showThread
|
||||
/>
|
||||
|
||||
<ActionBar
|
||||
{/*<ActionBar
|
||||
status={status}
|
||||
onReply={this.handleReplyClick}
|
||||
onFavourite={this.handleFavouriteClick}
|
||||
onReblog={this.handleReblogClick}
|
||||
onFavorite={this.handleFavoriteClick}
|
||||
onRepost={this.handleRepostClick}
|
||||
onDelete={this.handleDeleteClick}
|
||||
onDirect={this.handleDirectClick}
|
||||
onMention={this.handleMentionClick}
|
||||
@ -495,7 +495,7 @@ class Status extends ImmutablePureComponent {
|
||||
onReport={this.handleReport}
|
||||
onPin={this.handlePin}
|
||||
onEmbed={this.handleEmbed}
|
||||
/>
|
||||
/>*/}
|
||||
</div>
|
||||
</HotKeys>
|
||||
|
||||
|
@ -182,8 +182,10 @@ class SwitchingArea extends PureComponent {
|
||||
<Redirect from='/@:username' to='/:username' exact />
|
||||
<WrappedRoute path='/:username' publicRoute exact page={ProfilePage} component={AccountTimeline} content={children} />
|
||||
|
||||
{ /*
|
||||
<Redirect from='/@:username/comments' to='/:username/comments' />
|
||||
<WrappedRoute path='/:username/comments' page={ProfilePage} component={AccountTimeline} content={children} componentParams={{ commentsOnly: true }} />
|
||||
*/ }
|
||||
|
||||
<Redirect from='/@:username/followers' to='/:username/followers' />
|
||||
<WrappedRoute path='/:username/followers' page={ProfilePage} component={Followers} content={children} />
|
||||
@ -191,11 +193,8 @@ class SwitchingArea extends PureComponent {
|
||||
<Redirect from='/@:username/following' to='/:username/following' />
|
||||
<WrappedRoute path='/:username/following' page={ProfilePage} component={Following} content={children} />
|
||||
|
||||
<Redirect from='/@:username/photos' to='/:username/photos' />
|
||||
<WrappedRoute path='/:username/photos' page={ProfilePage} component={AccountGallery} content={children} componentParams={{ mediaType: 'photo' }} />
|
||||
|
||||
<Redirect from='/@:username/videos' to='/:username/videos' />
|
||||
<WrappedRoute path='/:username/videos' page={ProfilePage} component={AccountGallery} content={children} componentParams={{ mediaType: 'video' }} />
|
||||
<Redirect from='/@:username/media' to='/:username/media' />
|
||||
<WrappedRoute path='/:username/media' page={ProfilePage} component={AccountGallery} content={children} />
|
||||
|
||||
<Redirect from='/@:username/favorites' to='/:username/favorites' />
|
||||
<WrappedRoute path='/:username/favorites' page={ProfilePage} component={FavoritedStatuses} content={children} />
|
||||
@ -203,8 +202,8 @@ class SwitchingArea extends PureComponent {
|
||||
<Redirect from='/@:username/posts/:statusId' to='/:username/posts/:statusId' exact />
|
||||
<WrappedRoute path='/:username/posts/:statusId' publicRoute exact page={BasicPage} component={Status} content={children} componentParams={{ title: 'Status' }} />
|
||||
|
||||
<Redirect from='/@:username/posts/:statusId/reblogs' to='/:username/posts/:statusId/reblogs' />
|
||||
<WrappedRoute path='/:username/posts/:statusId/reblogs' page={BasicPage} component={Reblogs} content={children} componentParams={{ title: 'Reblogs' }} />
|
||||
<Redirect from='/@:username/posts/:statusId/reposts' to='/:username/posts/:statusId/reposts' />
|
||||
<WrappedRoute path='/:username/posts/:statusId/reposts' page={BasicPage} component={Reposts} content={children} componentParams={{ title: 'Reposts' }} />
|
||||
|
||||
<Redirect from='/@:username/posts/:statusId/favorites' to='/:username/posts/:statusId/favorites' />
|
||||
<WrappedRoute path='/:username/posts/:statusId/favorites' page={BasicPage} component={Favorites} content={children} componentParams={{ title: 'Favorites' }} />
|
||||
|
@ -1,47 +0,0 @@
|
||||
.ui {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0 0 100px 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 630px) and (max-height: 400px) {
|
||||
.is-composing {
|
||||
|
||||
.tabs-bar,
|
||||
.search {
|
||||
margin-top: -50px;
|
||||
}
|
||||
|
||||
.navigation-bar {
|
||||
padding-bottom: 0;
|
||||
|
||||
&>a:first-child {
|
||||
margin: -100px 10px 0 -50px;
|
||||
}
|
||||
|
||||
&__profile {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
&__profile-edit {
|
||||
position: absolute;
|
||||
margin-top: -60px;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
.icon-button.close {
|
||||
pointer-events: auto;
|
||||
opacity: 1;
|
||||
transform: scale(1.0, 1.0) translate(0, 0);
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
.compose__action-bar .icon-button {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transform: scale(0.0, 1.0) translate(100%, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -114,8 +114,8 @@ export function Notifications() {
|
||||
return import(/* webpackChunkName: "features/notifications" */'../../notifications')
|
||||
}
|
||||
|
||||
export function Reblogs() {
|
||||
return import(/* webpackChunkName: "features/reblogs" */'../../reblogs')
|
||||
export function Reposts() {
|
||||
return import(/* webpackChunkName: "features/reblogs" */'../../reposts')
|
||||
}
|
||||
|
||||
export function ReportModal() {
|
||||
|
@ -29,12 +29,8 @@ export default class ProfileLayout extends ImmutablePureComponent {
|
||||
title: 'Comments',
|
||||
},
|
||||
{
|
||||
to: `/${account.get('acct')}/photos`,
|
||||
title: 'Photos',
|
||||
},
|
||||
{
|
||||
to: `/${account.get('acct')}/videos`,
|
||||
title: 'Videos',
|
||||
to: `/${account.get('acct')}/media`,
|
||||
title: 'Media',
|
||||
},
|
||||
{
|
||||
to: '',
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's gabs with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Gorffen tiwtorial!",
|
||||
"introduction.interactions.favourite.headline": "Ffefryn",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Hwb",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Ateb",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Slut tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favorisere",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Svar",
|
||||
|
@ -1507,7 +1507,7 @@
|
||||
"id": "introduction.interactions.favourite.headline"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "You can save a gab for later, and let the author know that you liked it, by favouriting it.",
|
||||
"defaultMessage": "You can save a gab for later, and let the author know that you liked it, by favoriting it.",
|
||||
"id": "introduction.interactions.favourite.text"
|
||||
},
|
||||
{
|
||||
|
@ -170,7 +170,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favorite",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's gabs with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's gabs with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Boost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish toot-orial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's gabs with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a gab for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's gabs with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -169,7 +169,7 @@
|
||||
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
|
||||
"introduction.interactions.action": "Finish tutorial!",
|
||||
"introduction.interactions.favourite.headline": "Favourite",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
|
||||
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favoriting it.",
|
||||
"introduction.interactions.reblog.headline": "Repost",
|
||||
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by reposting them.",
|
||||
"introduction.interactions.reply.headline": "Reply",
|
||||
|
@ -3,25 +3,30 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { fetchAccountByUsername } from '../actions/accounts'
|
||||
import { makeGetAccount } from '../selectors'
|
||||
import { me } from '../initial_state'
|
||||
import LinkFooter from '../components/link_footer'
|
||||
import ProfileStatsPanel from '../components/panel/profile_stats_panel'
|
||||
import ProfileInfoPanel from '../components/panel/profile_info_panel'
|
||||
import MediaGalleryPanel from '../components/panel/media_gallery_panel'
|
||||
import ColumnIndicator from '../components/column_indicator'
|
||||
import ProfileLayout from '../layouts/profile_layout'
|
||||
|
||||
const mapStateToProps = (state, { params: { username }, withReplies = false }) => {
|
||||
const mapStateToProps = (state, { params: { username } }) => {
|
||||
const accounts = state.getIn(['accounts'])
|
||||
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase())
|
||||
const account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase())
|
||||
|
||||
let account = null
|
||||
if (!accountFetchError) {
|
||||
account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase())
|
||||
}
|
||||
const accountId = !!account ? account.get('id') : -1
|
||||
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 getAccount = makeGetAccount()
|
||||
|
||||
return {
|
||||
account: account ? getAccount(state, account.get('id')) : null,
|
||||
unavailable,
|
||||
account: accountId !== -1 ? getAccount(state, accountId) : null,
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,10 +34,11 @@ export default
|
||||
@connect(mapStateToProps)
|
||||
class ProfilePage extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
params: PropTypes.object.isRequired,
|
||||
account: ImmutablePropTypes.map,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
unavailable: PropTypes.bool.isRequired,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
@ -41,7 +47,11 @@ class ProfilePage extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account } = this.props
|
||||
const {
|
||||
account,
|
||||
children,
|
||||
unavailable
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<ProfileLayout
|
||||
@ -55,7 +65,15 @@ class ProfilePage extends ImmutablePureComponent {
|
||||
</Fragment>
|
||||
)}
|
||||
>
|
||||
{ this.props.children }
|
||||
{
|
||||
!account && <ColumnIndicator type='loading' />
|
||||
}
|
||||
{
|
||||
!!account && !unavailable &&
|
||||
React.cloneElement(children, {
|
||||
account,
|
||||
})
|
||||
}
|
||||
</ProfileLayout>
|
||||
)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
FOLLOW_REQUEST_REJECT_SUCCESS,
|
||||
} from '../actions/accounts';
|
||||
import {
|
||||
REBLOGS_FETCH_SUCCESS,
|
||||
REPOSTS_FETCH_SUCCESS,
|
||||
} from '../actions/interactions';
|
||||
import {
|
||||
BLOCKS_FETCH_SUCCESS,
|
||||
@ -63,7 +63,7 @@ export default function userLists(state = initialState, action) {
|
||||
return normalizeList(state, 'following', action.id, action.accounts, action.next);
|
||||
case FOLLOWING_EXPAND_SUCCESS:
|
||||
return appendToList(state, 'following', action.id, action.accounts, action.next);
|
||||
case REBLOGS_FETCH_SUCCESS:
|
||||
case REPOSTS_FETCH_SUCCESS:
|
||||
return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
|
||||
case FOLLOW_REQUESTS_FETCH_SUCCESS:
|
||||
return state.setIn(['follow_requests', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
|
||||
|
@ -103,7 +103,7 @@ const handlePush = (event) => {
|
||||
options.image = undefined;
|
||||
options.actions = [actionExpand(preferred_locale)];
|
||||
} else if (notification.type === 'mention') {
|
||||
options.actions = [actionReblog(preferred_locale), actionFavourite(preferred_locale)];
|
||||
options.actions = [actionRepost(preferred_locale), actionFavorite(preferred_locale)];
|
||||
}
|
||||
|
||||
return notify(options);
|
||||
@ -127,13 +127,13 @@ const actionExpand = preferred_locale => ({
|
||||
title: formatMessage('status.show_more', preferred_locale),
|
||||
});
|
||||
|
||||
const actionReblog = preferred_locale => ({
|
||||
const actionRepost = preferred_locale => ({
|
||||
action: 'reblog',
|
||||
icon: '/web-push-icon_reblog.png',
|
||||
title: formatMessage('status.reblog', preferred_locale),
|
||||
});
|
||||
|
||||
const actionFavourite = preferred_locale => ({
|
||||
const actionFavorite = preferred_locale => ({
|
||||
action: 'favourite',
|
||||
icon: '/web-push-icon_favourite.png',
|
||||
title: formatMessage('status.favourite', preferred_locale),
|
||||
@ -151,7 +151,7 @@ const expandNotification = notification => {
|
||||
|
||||
newNotification.body = newNotification.data.hiddenBody;
|
||||
newNotification.image = newNotification.data.hiddenImage;
|
||||
newNotification.actions = [actionReblog(notification.data.preferred_locale), actionFavourite(notification.data.preferred_locale)];
|
||||
newNotification.actions = [actionRepost(notification.data.preferred_locale), actionFavorite(notification.data.preferred_locale)];
|
||||
|
||||
return self.registration.showNotification(newNotification.title, newNotification);
|
||||
};
|
||||
|
@ -409,6 +409,10 @@ body {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.heightMin50VH {
|
||||
min-height: 50vh;
|
||||
}
|
||||
|
||||
.height100VH {
|
||||
height: 100vh;
|
||||
}
|
||||
@ -437,6 +441,10 @@ body {
|
||||
height: 122px;
|
||||
}
|
||||
|
||||
.height158PX {
|
||||
height: 158px;
|
||||
}
|
||||
|
||||
.height220PX {
|
||||
height: 220px;
|
||||
}
|
||||
@ -469,18 +477,22 @@ body {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.width100PC {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.width72PX {
|
||||
width: 72px;
|
||||
}
|
||||
|
||||
.width100PC {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.width50PC {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.width25PC {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.maxWidth100PC {
|
||||
max-width: 100%;
|
||||
}
|
||||
@ -743,6 +755,10 @@ body {
|
||||
padding-top: 56.25%;
|
||||
}
|
||||
|
||||
.paddingTop25PC {
|
||||
padding-top: 25%;
|
||||
}
|
||||
|
||||
.paddingTop10PX {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user