Progress
This commit is contained in:
parent
3ca4ffcc6b
commit
c6aa4e08a1
@ -2,13 +2,13 @@ import api, { getLinks } from '../api';
|
|||||||
import { importFetchedStatuses } from './importer';
|
import { importFetchedStatuses } from './importer';
|
||||||
import { me } from '../initial_state';
|
import { me } from '../initial_state';
|
||||||
|
|
||||||
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
|
export const FAVORITED_STATUSES_FETCH_REQUEST = 'FAVORITED_STATUSES_FETCH_REQUEST';
|
||||||
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
|
export const FAVORITED_STATUSES_FETCH_SUCCESS = 'FAVORITED_STATUSES_FETCH_SUCCESS';
|
||||||
export const FAVOURITED_STATUSES_FETCH_FAIL = 'FAVOURITED_STATUSES_FETCH_FAIL';
|
export const FAVORITED_STATUSES_FETCH_FAIL = 'FAVORITED_STATUSES_FETCH_FAIL';
|
||||||
|
|
||||||
export const FAVOURITED_STATUSES_EXPAND_REQUEST = 'FAVOURITED_STATUSES_EXPAND_REQUEST';
|
export const FAVORITED_STATUSES_EXPAND_REQUEST = 'FAVORITED_STATUSES_EXPAND_REQUEST';
|
||||||
export const FAVOURITED_STATUSES_EXPAND_SUCCESS = 'FAVOURITED_STATUSES_EXPAND_SUCCESS';
|
export const FAVORITED_STATUSES_EXPAND_SUCCESS = 'FAVORITED_STATUSES_EXPAND_SUCCESS';
|
||||||
export const FAVOURITED_STATUSES_EXPAND_FAIL = 'FAVOURITED_STATUSES_EXPAND_FAIL';
|
export const FAVORITED_STATUSES_EXPAND_FAIL = 'FAVORITED_STATUSES_EXPAND_FAIL';
|
||||||
|
|
||||||
export function fetchFavoritedStatuses() {
|
export function fetchFavoritedStatuses() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -32,14 +32,14 @@ export function fetchFavoritedStatuses() {
|
|||||||
|
|
||||||
export function fetchFavoritedStatusesRequest() {
|
export function fetchFavoritedStatusesRequest() {
|
||||||
return {
|
return {
|
||||||
type: FAVOURITED_STATUSES_FETCH_REQUEST,
|
type: FAVORITED_STATUSES_FETCH_REQUEST,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function fetchFavoritedStatusesSuccess(statuses, next) {
|
export function fetchFavoritedStatusesSuccess(statuses, next) {
|
||||||
return {
|
return {
|
||||||
type: FAVOURITED_STATUSES_FETCH_SUCCESS,
|
type: FAVORITED_STATUSES_FETCH_SUCCESS,
|
||||||
statuses,
|
statuses,
|
||||||
next,
|
next,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
@ -48,7 +48,7 @@ export function fetchFavoritedStatusesSuccess(statuses, next) {
|
|||||||
|
|
||||||
export function fetchFavoritedStatusesFail(error) {
|
export function fetchFavoritedStatusesFail(error) {
|
||||||
return {
|
return {
|
||||||
type: FAVOURITED_STATUSES_FETCH_FAIL,
|
type: FAVORITED_STATUSES_FETCH_FAIL,
|
||||||
error,
|
error,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
@ -78,13 +78,13 @@ export function expandFavoritedStatuses() {
|
|||||||
|
|
||||||
export function expandFavoritedStatusesRequest() {
|
export function expandFavoritedStatusesRequest() {
|
||||||
return {
|
return {
|
||||||
type: FAVOURITED_STATUSES_EXPAND_REQUEST,
|
type: FAVORITED_STATUSES_EXPAND_REQUEST,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function expandFavoritedStatusesSuccess(statuses, next) {
|
export function expandFavoritedStatusesSuccess(statuses, next) {
|
||||||
return {
|
return {
|
||||||
type: FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
type: FAVORITED_STATUSES_EXPAND_SUCCESS,
|
||||||
statuses,
|
statuses,
|
||||||
next,
|
next,
|
||||||
};
|
};
|
||||||
@ -92,7 +92,7 @@ export function expandFavoritedStatusesSuccess(statuses, next) {
|
|||||||
|
|
||||||
export function expandFavoritedStatusesFail(error) {
|
export function expandFavoritedStatusesFail(error) {
|
||||||
return {
|
return {
|
||||||
type: FAVOURITED_STATUSES_EXPAND_FAIL,
|
type: FAVORITED_STATUSES_EXPAND_FAIL,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
};
|
26
app/javascript/gabsocial/assets/error_icon.js
Normal file
26
app/javascript/gabsocial/assets/error_icon.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const ErrorIcon = ({
|
||||||
|
className = '',
|
||||||
|
width = '32px',
|
||||||
|
height = '32px',
|
||||||
|
viewBox = '0 0 64 64',
|
||||||
|
title = 'Error',
|
||||||
|
}) => (
|
||||||
|
<svg
|
||||||
|
className={className}
|
||||||
|
version='1.1'
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
x='0px'
|
||||||
|
y='0px'
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
viewBox={viewBox}
|
||||||
|
xmlSpace='preserve'
|
||||||
|
aria-label={title}
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<path d="M 54.667969 9.371094 C 42.179688 -3.121094 21.855469 -3.121094 9.367188 9.367188 C -3.125 21.855469 -3.121094 42.179688 9.367188 54.667969 C 21.855469 67.160156 42.179688 67.160156 54.667969 54.667969 C 67.15625 42.179688 67.15625 21.855469 54.667969 9.371094 Z M 51.175781 51.175781 C 40.613281 61.738281 23.425781 61.738281 12.863281 51.175781 C 2.296875 40.613281 2.296875 23.421875 12.863281 12.859375 C 23.425781 2.296875 40.609375 2.296875 51.175781 12.863281 C 61.738281 23.425781 61.738281 40.613281 51.175781 51.175781 Z M 46.5 44.679688 C 46.898438 45.597656 46.476562 46.664062 45.558594 47.058594 C 44.640625 47.460938 43.574219 47.035156 43.175781 46.117188 C 41.429688 42.078125 37.300781 39.46875 32.65625 39.46875 C 27.90625 39.46875 23.753906 42.078125 22.078125 46.113281 C 21.789062 46.808594 21.113281 47.230469 20.40625 47.230469 C 20.175781 47.230469 19.9375 47.183594 19.710938 47.089844 C 18.789062 46.707031 18.351562 45.648438 18.734375 44.722656 C 20.972656 39.332031 26.441406 35.847656 32.65625 35.847656 C 38.746094 35.847656 44.179688 39.3125 46.5 44.679688 Z M 20.070312 23.347656 C 20.070312 21.28125 21.746094 19.605469 23.8125 19.605469 C 25.878906 19.605469 27.558594 21.28125 27.558594 23.347656 C 27.558594 25.417969 25.878906 27.09375 23.8125 27.09375 C 21.746094 27.09375 20.070312 25.417969 20.070312 23.347656 Z M 37.046875 23.347656 C 37.046875 21.28125 38.722656 19.605469 40.789062 19.605469 C 42.859375 19.605469 44.535156 21.28125 44.535156 23.347656 C 44.535156 25.417969 42.859375 27.09375 40.789062 27.09375 C 38.722656 27.09375 37.046875 25.417969 37.046875 23.347656 Z M 37.046875 23.347656" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default ErrorIcon
|
@ -9,15 +9,20 @@ import CloseIcon from './close_icon'
|
|||||||
import CommentIcon from './comment_icon'
|
import CommentIcon from './comment_icon'
|
||||||
import DissenterIcon from './dissenter_icon'
|
import DissenterIcon from './dissenter_icon'
|
||||||
import EllipsisIcon from './ellipsis_icon'
|
import EllipsisIcon from './ellipsis_icon'
|
||||||
|
import ErrorIcon from './error_icon'
|
||||||
import GlobeIcon from './globe_icon'
|
import GlobeIcon from './globe_icon'
|
||||||
import GroupIcon from './group_icon'
|
import GroupIcon from './group_icon'
|
||||||
import HomeIcon from './home_icon'
|
import HomeIcon from './home_icon'
|
||||||
import LikeIcon from './like_icon'
|
import LikeIcon from './like_icon'
|
||||||
|
import LinkIcon from './link_icon'
|
||||||
import ListIcon from './list_icon'
|
import ListIcon from './list_icon'
|
||||||
import LoadingIcon from './loading_icon'
|
import LoadingIcon from './loading_icon'
|
||||||
import MediaIcon from './media_icon'
|
import MediaIcon from './media_icon'
|
||||||
|
import MissingIcon from './missing_icon'
|
||||||
import MoreIcon from './more_icon'
|
import MoreIcon from './more_icon'
|
||||||
import NotificationsIcon from './notifications_icon'
|
import NotificationsIcon from './notifications_icon'
|
||||||
|
import PinIcon from './pin_icon'
|
||||||
|
import PlayIcon from './play_icon'
|
||||||
import PollIcon from './poll_icon'
|
import PollIcon from './poll_icon'
|
||||||
import RepostIcon from './repost_icon'
|
import RepostIcon from './repost_icon'
|
||||||
import SearchIcon from './search_icon'
|
import SearchIcon from './search_icon'
|
||||||
@ -40,15 +45,20 @@ export {
|
|||||||
CommentIcon,
|
CommentIcon,
|
||||||
DissenterIcon,
|
DissenterIcon,
|
||||||
EllipsisIcon,
|
EllipsisIcon,
|
||||||
|
ErrorIcon,
|
||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
GroupIcon,
|
GroupIcon,
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
LikeIcon,
|
LikeIcon,
|
||||||
|
LinkIcon,
|
||||||
ListIcon,
|
ListIcon,
|
||||||
LoadingIcon,
|
LoadingIcon,
|
||||||
MediaIcon,
|
MediaIcon,
|
||||||
|
MissingIcon,
|
||||||
MoreIcon,
|
MoreIcon,
|
||||||
NotificationsIcon,
|
NotificationsIcon,
|
||||||
|
PinIcon,
|
||||||
|
PlayIcon,
|
||||||
PollIcon,
|
PollIcon,
|
||||||
RepostIcon,
|
RepostIcon,
|
||||||
SearchIcon,
|
SearchIcon,
|
||||||
|
27
app/javascript/gabsocial/assets/link_icon.js
Normal file
27
app/javascript/gabsocial/assets/link_icon.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const LinkIcon = ({
|
||||||
|
className = '',
|
||||||
|
width = '16px',
|
||||||
|
height = '16px',
|
||||||
|
viewBox = '0 0 64 64',
|
||||||
|
title = 'Link',
|
||||||
|
}) => (
|
||||||
|
<svg
|
||||||
|
className={className}
|
||||||
|
version='1.1'
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
x='0px'
|
||||||
|
y='0px'
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
viewBox={viewBox}
|
||||||
|
xmlSpace='preserve'
|
||||||
|
aria-label={title}
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<path d="M 26.53125 48.78125 L 18.992188 56.320312 C 15.863281 59.449219 10.800781 59.449219 7.675781 56.324219 C 4.554688 53.199219 4.554688 48.132812 7.675781 45.011719 L 22.761719 29.925781 C 25.886719 26.800781 30.949219 26.800781 34.074219 29.925781 C 35.117188 30.964844 36.804688 30.964844 37.84375 29.925781 C 38.886719 28.882812 38.886719 27.195312 37.84375 26.152344 C 32.636719 20.945312 24.199219 20.945312 18.992188 26.152344 L 3.90625 41.238281 C -1.300781 46.445312 -1.300781 54.886719 3.90625 60.09375 C 9.113281 65.300781 17.554688 65.300781 22.761719 60.09375 L 30.304688 52.550781 C 31.34375 51.511719 31.34375 49.820312 30.304688 48.78125 C 29.261719 47.738281 27.574219 47.738281 26.53125 48.78125 Z M 26.53125 48.78125" />
|
||||||
|
<path d="M 60.09375 3.90625 C 54.886719 -1.300781 46.445312 -1.300781 41.238281 3.90625 L 32.1875 12.953125 C 31.148438 13.996094 31.148438 15.683594 32.1875 16.722656 C 33.230469 17.765625 34.917969 17.765625 35.960938 16.722656 L 45.007812 7.675781 C 48.132812 4.550781 53.199219 4.550781 56.324219 7.675781 C 59.445312 10.800781 59.445312 15.863281 56.324219 18.988281 L 39.730469 35.578125 C 36.605469 38.703125 31.542969 38.703125 28.417969 35.578125 C 27.378906 34.539062 25.691406 34.539062 24.648438 35.578125 C 23.605469 36.621094 23.605469 38.308594 24.648438 39.351562 C 29.855469 44.558594 38.296875 44.558594 43.503906 39.351562 L 60.09375 22.757812 C 65.300781 17.550781 65.300781 9.113281 60.09375 3.90625 Z M 60.09375 3.90625" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default LinkIcon
|
29
app/javascript/gabsocial/assets/missing_icon.js
Normal file
29
app/javascript/gabsocial/assets/missing_icon.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
const MissingIcon = ({
|
||||||
|
className = '',
|
||||||
|
width = '32px',
|
||||||
|
height = '32px',
|
||||||
|
viewBox = '0 0 84 84',
|
||||||
|
title = 'Missing',
|
||||||
|
}) => (
|
||||||
|
<svg
|
||||||
|
className={className}
|
||||||
|
version='1.1'
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
x='0px'
|
||||||
|
y='0px'
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
viewBox={viewBox}
|
||||||
|
xmlSpace='preserve'
|
||||||
|
aria-label={title}
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<path d="M 30.21875 0 L 12.601562 17.621094 L 12.601562 84 L 71.398438 84 L 71.398438 0 Z M 29.398438 4.78125 L 29.398438 16.800781 L 17.378906 16.800781 Z M 68.601562 81.199219 L 15.398438 81.199219 L 15.398438 19.601562 L 32.199219 19.601562 L 32.199219 2.800781 L 68.601562 2.800781 Z M 68.601562 81.199219" />
|
||||||
|
<path d="M 34.757812 33.257812 L 30.800781 37.21875 L 26.839844 33.257812 L 24.859375 35.238281 L 28.820312 39.199219 L 24.859375 43.160156 L 26.839844 45.140625 L 30.800781 41.179688 L 34.757812 45.140625 L 36.738281 43.160156 L 32.78125 39.199219 L 36.738281 35.238281 Z M 34.757812 33.257812" />
|
||||||
|
<path d="M 47.839844 45.140625 L 51.800781 41.179688 L 55.757812 45.140625 L 57.738281 43.160156 L 53.78125 39.199219 L 57.738281 35.238281 L 55.757812 33.257812 L 51.800781 37.21875 L 47.839844 33.257812 L 45.859375 35.238281 L 49.820312 39.199219 L 45.859375 43.160156 Z M 47.839844 45.140625" />
|
||||||
|
<path d="M 29.808594 57.808594 L 31.789062 59.789062 L 33.089844 58.488281 C 38.015625 53.578125 45.984375 53.578125 50.910156 58.488281 L 52.210938 59.789062 L 54.191406 57.808594 L 52.890625 56.507812 C 46.871094 50.503906 37.128906 50.503906 31.109375 56.507812 Z M 29.808594 57.808594" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default MissingIcon
|
26
app/javascript/gabsocial/assets/pin_icon.js
Normal file
26
app/javascript/gabsocial/assets/pin_icon.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const PinIcon = ({
|
||||||
|
className = '',
|
||||||
|
width = '16px',
|
||||||
|
height = '16px',
|
||||||
|
viewBox = '0 0 32 32',
|
||||||
|
title = 'Pin',
|
||||||
|
}) => (
|
||||||
|
<svg
|
||||||
|
className={className}
|
||||||
|
version='1.1'
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
x='0px'
|
||||||
|
y='0px'
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
viewBox={viewBox}
|
||||||
|
xmlSpace='preserve'
|
||||||
|
aria-label={title}
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<path d="M 25.984375 19.042969 C 25.984375 19.914062 25.277344 20.621094 24.402344 20.621094 L 18.996094 20.621094 L 16.789062 31.398438 C 16.714844 31.765625 16.390625 32.03125 16.015625 32.03125 C 15.640625 32.03125 15.316406 31.765625 15.242188 31.398438 L 13.03125 20.621094 L 7.628906 20.621094 C 6.753906 20.621094 6.046875 19.914062 6.046875 19.042969 C 6.046875 16.636719 7.4375 14.488281 9.617188 13.050781 L 9.617188 8.335938 L 8.148438 8.335938 C 7.277344 8.335938 6.570312 7.628906 6.570312 6.753906 L 6.570312 1.582031 C 6.570312 0.707031 7.277344 0 8.148438 0 L 23.882812 0 C 24.757812 0 25.460938 0.707031 25.460938 1.582031 L 25.460938 6.753906 C 25.460938 7.628906 24.757812 8.335938 23.882812 8.335938 L 22.414062 8.335938 L 22.414062 13.050781 C 24.59375 14.488281 25.984375 16.636719 25.984375 19.042969 Z M 25.984375 19.042969" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default PinIcon
|
26
app/javascript/gabsocial/assets/play_icon.js
Normal file
26
app/javascript/gabsocial/assets/play_icon.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const PlayIcon = ({
|
||||||
|
className = '',
|
||||||
|
width = '16px',
|
||||||
|
height = '16px',
|
||||||
|
viewBox = '0 0 64 64',
|
||||||
|
title = 'Play',
|
||||||
|
}) => (
|
||||||
|
<svg
|
||||||
|
className={className}
|
||||||
|
version='1.1'
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
x='0px'
|
||||||
|
y='0px'
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
viewBox={viewBox}
|
||||||
|
xmlSpace='preserve'
|
||||||
|
aria-label={title}
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<path d="M 52.492188 26.058594 L 16.941406 1.71875 C 15.300781 0.59375 13.644531 0 12.269531 0 C 9.613281 0 7.96875 2.132812 7.96875 5.703125 L 7.96875 58.304688 C 7.96875 61.871094 9.609375 64 12.261719 64 C 13.640625 64 15.265625 63.402344 16.914062 62.277344 L 52.476562 37.941406 C 54.765625 36.371094 56.03125 34.261719 56.03125 31.996094 C 56.03125 29.734375 54.78125 27.625 52.492188 26.058594 Z M 52.492188 26.058594" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default PlayIcon
|
@ -36,6 +36,7 @@ class Account extends ImmutablePureComponent {
|
|||||||
actionIcon: PropTypes.string,
|
actionIcon: PropTypes.string,
|
||||||
actionTitle: PropTypes.string,
|
actionTitle: PropTypes.string,
|
||||||
onActionClick: PropTypes.func,
|
onActionClick: PropTypes.func,
|
||||||
|
compact: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFollow = () => {
|
handleFollow = () => {
|
||||||
@ -63,7 +64,15 @@ class Account extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { account, intl, hidden, onActionClick, actionIcon, actionTitle } = this.props
|
const {
|
||||||
|
account,
|
||||||
|
intl,
|
||||||
|
hidden,
|
||||||
|
onActionClick,
|
||||||
|
actionIcon,
|
||||||
|
actionTitle,
|
||||||
|
compact
|
||||||
|
} = this.props
|
||||||
|
|
||||||
if (!account) return null
|
if (!account) return null
|
||||||
|
|
||||||
@ -76,6 +85,7 @@ class Account extends ImmutablePureComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const avatarSize = compact ? 42 : 52
|
||||||
let buttons
|
let buttons
|
||||||
|
|
||||||
if (onActionClick && actionIcon) {
|
if (onActionClick && actionIcon) {
|
||||||
@ -109,6 +119,50 @@ class Account extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// : todo : clean up
|
||||||
|
|
||||||
|
if (compact) {
|
||||||
|
return (
|
||||||
|
<div className={[_s.default, _s.marginTop5PX, _s.marginBottom15PX].join(' ')}>
|
||||||
|
<div className={[_s.default, _s.flexRow].join(' ')}>
|
||||||
|
|
||||||
|
<NavLink
|
||||||
|
className={[_s.default, _s.noUnderline].join(' ')}
|
||||||
|
title={account.get('acct')}
|
||||||
|
to={`/${account.get('acct')}`}
|
||||||
|
>
|
||||||
|
<Avatar account={account} size={avatarSize} />
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
<div className={[_s.default, _s.alignItemsStart, _s.paddingHorizontal10PX].join(' ')}>
|
||||||
|
<NavLink
|
||||||
|
className={[_s.default, _s.noUnderline].join(' ')}
|
||||||
|
title={account.get('acct')}
|
||||||
|
to={`/${account.get('acct')}`}
|
||||||
|
>
|
||||||
|
<DisplayName account={account} multiline />
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
|
||||||
|
<Button
|
||||||
|
outline
|
||||||
|
narrow
|
||||||
|
color='brand'
|
||||||
|
backgroundColor='none'
|
||||||
|
className={_s.marginTop5PX}
|
||||||
|
>
|
||||||
|
<Text color='inherit'>
|
||||||
|
{intl.formatMessage(messages.follow)}
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.marginTop5PX, _s.marginBottom15PX].join(' ')}>
|
<div className={[_s.default, _s.marginTop5PX, _s.marginBottom15PX].join(' ')}>
|
||||||
<div className={[_s.default, _s.flexRow].join(' ')}>
|
<div className={[_s.default, _s.flexRow].join(' ')}>
|
||||||
@ -118,7 +172,7 @@ class Account extends ImmutablePureComponent {
|
|||||||
title={account.get('acct')}
|
title={account.get('acct')}
|
||||||
to={`/${account.get('acct')}`}
|
to={`/${account.get('acct')}`}
|
||||||
>
|
>
|
||||||
<Avatar account={account} size={52} />
|
<Avatar account={account} size={avatarSize} />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<div className={[_s.default, _s.alignItemsStart, _s.paddingHorizontal10PX].join(' ')}>
|
<div className={[_s.default, _s.alignItemsStart, _s.paddingHorizontal10PX].join(' ')}>
|
||||||
@ -129,6 +183,7 @@ class Account extends ImmutablePureComponent {
|
|||||||
>
|
>
|
||||||
<DisplayName account={account} />
|
<DisplayName account={account} />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
outline
|
outline
|
||||||
narrow
|
narrow
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './account';
|
export { default } from './account'
|
@ -1 +1 @@
|
|||||||
export { default } from './autosuggest_account';
|
export { default } from './autosuggest_account'
|
@ -1 +1 @@
|
|||||||
export { default } from './autosuggest_emoji';
|
export { default } from './autosuggest_emoji'
|
@ -1 +1 @@
|
|||||||
export { default } from './autosuggest_textbox';
|
export { default } from './autosuggest_textbox'
|
@ -1 +1 @@
|
|||||||
export { default } from './bundle_column_error';
|
export { default } from './bundle_column_error'
|
@ -81,6 +81,10 @@ export default class Button extends PureComponent {
|
|||||||
|
|
||||||
const theIcon = !!icon ? <Icon id={icon} width={iconWidth} height={iconWidth} className={iconClassName} /> : undefined
|
const theIcon = !!icon ? <Icon id={icon} width={iconWidth} height={iconWidth} className={iconClassName} /> : undefined
|
||||||
|
|
||||||
|
if (backgroundColor === 'tertiary') {
|
||||||
|
console.log("className:", className)
|
||||||
|
}
|
||||||
|
|
||||||
// : todo :
|
// : todo :
|
||||||
const classes = cx(className, {
|
const classes = cx(className, {
|
||||||
default: 1,
|
default: 1,
|
||||||
@ -93,6 +97,7 @@ export default class Button extends PureComponent {
|
|||||||
backgroundColorBrand: backgroundColor === COLORS.brand,
|
backgroundColorBrand: backgroundColor === COLORS.brand,
|
||||||
backgroundTransparent: backgroundColor === COLORS.none,
|
backgroundTransparent: backgroundColor === COLORS.none,
|
||||||
backgroundSubtle2: backgroundColor === COLORS.tertiary,
|
backgroundSubtle2: backgroundColor === COLORS.tertiary,
|
||||||
|
backgroundSubtle: backgroundColor === COLORS.secondary,
|
||||||
|
|
||||||
colorPrimary: color === COLORS.primary,
|
colorPrimary: color === COLORS.primary,
|
||||||
colorSecondary: color === COLORS.secondary,
|
colorSecondary: color === COLORS.secondary,
|
||||||
@ -131,9 +136,9 @@ export default class Button extends PureComponent {
|
|||||||
) : children
|
) : children
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
|
disabled,
|
||||||
className: classes,
|
className: classes,
|
||||||
ref: this.setRef,
|
ref: this.setRef,
|
||||||
disabled: disabled,
|
|
||||||
to: to || undefined,
|
to: to || undefined,
|
||||||
href: href || undefined,
|
href: href || undefined,
|
||||||
onClick: this.handleClick || undefined,
|
onClick: this.handleClick || undefined,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl'
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
import Icon from './icon'
|
import Icon from './icon'
|
||||||
|
import Text from './text'
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
loading: { id: 'loading_indicator.label', defaultMessage: 'Loading..' },
|
loading: { id: 'loading_indicator.label', defaultMessage: 'Loading..' },
|
||||||
@ -30,12 +31,16 @@ class ColumnIndicator extends PureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.width100PC, _s.justifyContentCenter, _s.alignItemsCenter, _s.paddingVertical15PX].join(' ')}>
|
<div className={[_s.default, _s.width100PC, _s.justifyContentCenter, _s.alignItemsCenter, _s.paddingVertical15PX].join(' ')}>
|
||||||
<Icon id={type} width='52px' height='52px' />
|
<Icon id={type} width='44px' height='44px' />
|
||||||
{
|
{
|
||||||
type !== 'loading' &&
|
type !== 'loading' &&
|
||||||
<span className={[_s.default, _s.marginTop10PX, _s.text, _s.displayFlex, _s.colorBrand, _s.fontWeightNormal, _s.fontSize14PX].join(' ')}>
|
<Text
|
||||||
|
align='center'
|
||||||
|
size='medium'
|
||||||
|
className={_s.marginTop10PX}
|
||||||
|
>
|
||||||
{title}
|
{title}
|
||||||
</span>
|
</Text>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './column_inline_form';
|
export { default } from './column_inline_form'
|
@ -31,12 +31,12 @@ class DisplayName extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleMouseEnter = debounce(() => {
|
handleMouseEnter = debounce(() => {
|
||||||
console.log("SHOW - USER POPOVER")
|
// console.log("SHOW - USER POPOVER")
|
||||||
this.props.openUserInfoPopover()
|
// this.props.openUserInfoPopover()
|
||||||
}, 50, { leading: true })
|
}, 50, { leading: true })
|
||||||
|
|
||||||
handleMouseLeave = () => {
|
handleMouseLeave = () => {
|
||||||
console.log("HIDE - USER POPOVER")
|
// console.log("HIDE - USER POPOVER")
|
||||||
// this.props.closeUserInfoPopover()
|
// this.props.closeUserInfoPopover()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,25 @@
|
|||||||
|
import classnames from 'classnames/bind'
|
||||||
|
|
||||||
|
const cx = classnames.bind(_s)
|
||||||
|
|
||||||
export default class Divider extends PureComponent {
|
export default class Divider extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
small: PropTypes.bool
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
|
const { small } = this.props
|
||||||
|
|
||||||
|
const classes = cx({
|
||||||
|
default: 1,
|
||||||
|
borderBottom1PX: 1,
|
||||||
|
borderColorSecondary2: 1,
|
||||||
|
width100PC: 1,
|
||||||
|
marginBottom15PX: !small,
|
||||||
|
marginVertical10PX: small,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.borderBottom1PX, _s.borderColorSecondary2, _s.marginBottom15PX, _s.width100PC].join(' ')} />
|
<div className={classes} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
export { default } from './domain';
|
export { default } from './domain'
|
@ -1,10 +1,8 @@
|
|||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
|
|
||||||
export default class ErrorBoundary extends PureComponent {
|
export default class ErrorBoundary extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
};
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
hasError: false,
|
hasError: false,
|
||||||
@ -17,22 +15,23 @@ export default class ErrorBoundary extends PureComponent {
|
|||||||
hasError: true,
|
hasError: true,
|
||||||
stackTrace: error.stack,
|
stackTrace: error.stack,
|
||||||
componentStack: info && info.componentStack,
|
componentStack: info && info.componentStack,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { hasError } = this.state;
|
const { hasError } = this.state
|
||||||
|
|
||||||
if (!hasError) return this.props.children;
|
if (!hasError) return this.props.children
|
||||||
|
|
||||||
|
// : todo : custom error page
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='error-boundary'>
|
<div className='error-boundary'>
|
||||||
<div className='error-boundary__container'>
|
<div className='error-boundary__container'>
|
||||||
<FormattedMessage id='alert.unexpected.message' defaultMessage='Error' />
|
|
||||||
<a className='error-boundary__link' href='/home'>Return Home</a>
|
<a className='error-boundary__link' href='/home'>Return Home</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './extended_video_player';
|
export { default } from './extended_video_player'
|
@ -1 +1 @@
|
|||||||
export { default } from './footer_bar';
|
export { default } from './footer_bar'
|
@ -33,6 +33,8 @@ export default class Icon extends PureComponent {
|
|||||||
return <I.DissenterIcon {...options} />
|
return <I.DissenterIcon {...options} />
|
||||||
case 'ellipsis':
|
case 'ellipsis':
|
||||||
return <I.EllipsisIcon {...options} />
|
return <I.EllipsisIcon {...options} />
|
||||||
|
case 'error':
|
||||||
|
return <I.ErrorIcon {...options} />
|
||||||
case 'globe':
|
case 'globe':
|
||||||
return <I.GlobeIcon {...options} />
|
return <I.GlobeIcon {...options} />
|
||||||
case 'group':
|
case 'group':
|
||||||
@ -41,16 +43,24 @@ export default class Icon extends PureComponent {
|
|||||||
return <I.HomeIcon {...options} />
|
return <I.HomeIcon {...options} />
|
||||||
case 'like':
|
case 'like':
|
||||||
return <I.LikeIcon {...options} />
|
return <I.LikeIcon {...options} />
|
||||||
|
case 'link':
|
||||||
|
return <I.LinkIcon {...options} />
|
||||||
case 'list':
|
case 'list':
|
||||||
return <I.ListIcon {...options} />
|
return <I.ListIcon {...options} />
|
||||||
case 'loading':
|
case 'loading':
|
||||||
return <I.LoadingIcon {...options} />
|
return <I.LoadingIcon {...options} />
|
||||||
case 'more':
|
|
||||||
return <I.MoreIcon {...options} />
|
|
||||||
case 'media':
|
case 'media':
|
||||||
return <I.MediaIcon {...options} />
|
return <I.MediaIcon {...options} />
|
||||||
|
case 'missing':
|
||||||
|
return <I.MissingIcon {...options} />
|
||||||
|
case 'more':
|
||||||
|
return <I.MoreIcon {...options} />
|
||||||
case 'notifications':
|
case 'notifications':
|
||||||
return <I.NotificationsIcon {...options} />
|
return <I.NotificationsIcon {...options} />
|
||||||
|
case 'pin':
|
||||||
|
return <I.PinIcon {...options} />
|
||||||
|
case 'play':
|
||||||
|
return <I.PlayIcon {...options} />
|
||||||
case 'poll':
|
case 'poll':
|
||||||
return <I.PollIcon {...options} />
|
return <I.PollIcon {...options} />
|
||||||
case 'repost':
|
case 'repost':
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './icon_button';
|
export { default } from './icon_button'
|
@ -1 +1 @@
|
|||||||
export { default } from './image_loader';
|
export { default } from './image_loader'
|
@ -1,5 +1,4 @@
|
|||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import Button from './button'
|
|
||||||
import Icon from './icon'
|
import Icon from './icon'
|
||||||
|
|
||||||
const cx = classNames.bind(_s)
|
const cx = classNames.bind(_s)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { injectIntl, defineMessages } from 'react-intl'
|
import { injectIntl, defineMessages } from 'react-intl'
|
||||||
|
import Button from './button'
|
||||||
import Icon from './icon'
|
import Icon from './icon'
|
||||||
|
import Text from './text'
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
|
load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
|
||||||
@ -31,16 +33,29 @@ class LoadMore extends PureComponent {
|
|||||||
const { disabled, visible, gap, intl } = this.props
|
const { disabled, visible, gap, intl } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<Button
|
||||||
className={[_s.default].join(' ')}
|
block
|
||||||
|
radiusSmall
|
||||||
|
backgroundColor='tertiary'
|
||||||
|
color='primary'
|
||||||
disabled={disabled || !visible}
|
disabled={disabled || !visible}
|
||||||
style={{ visibility: visible ? 'visible' : 'hidden' }}
|
style={{ visibility: visible ? 'visible' : 'hidden' }}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
aria-label={intl.formatMessage(messages.load_more)}
|
aria-label={intl.formatMessage(messages.load_more)}
|
||||||
>
|
>
|
||||||
{!gap && intl.formatMessage(messages.load_more)}
|
{
|
||||||
{gap && <Icon id='ellipsis-h' />}
|
!gap &&
|
||||||
</button>
|
<Text color='inherit'>
|
||||||
|
{intl.formatMessage(messages.load_more)}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
gap &&
|
||||||
|
<Text align='center'>
|
||||||
|
<Icon id='ellipsis' />
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './media_gallery';
|
export { default } from './media_gallery'
|
@ -6,6 +6,7 @@ import { cancelReplyCompose } from '../../actions/compose'
|
|||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
confirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
confirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||||
|
delete: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
||||||
})
|
})
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
@ -58,7 +59,7 @@ class ModalBase extends PureComponent {
|
|||||||
|
|
||||||
if (!composeId && composeText && type == 'COMPOSE') {
|
if (!composeId && composeText && type == 'COMPOSE') {
|
||||||
onOpenModal('CONFIRM', {
|
onOpenModal('CONFIRM', {
|
||||||
message: <FormattedMessage id='confirmations.delete.message' defaultMessage='Are you sure you want to delete this status?' />,
|
message: intl.formatMessage(messages.delete),
|
||||||
confirm: intl.formatMessage(messages.confirm),
|
confirm: intl.formatMessage(messages.confirm),
|
||||||
onConfirm: () => onCancelReplyCompose(),
|
onConfirm: () => onCancelReplyCompose(),
|
||||||
onCancel: () => onOpenModal('COMPOSE'),
|
onCancel: () => onOpenModal('COMPOSE'),
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
|
import { Fragment } from 'react'
|
||||||
import { defineMessages, injectIntl } from 'react-intl'
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import { me } from '../../initial_state'
|
|
||||||
import { shortNumberFormat } from '../../utils/numbers'
|
import { shortNumberFormat } from '../../utils/numbers'
|
||||||
import PanelLayout from './panel_layout'
|
import PanelLayout from './panel_layout'
|
||||||
import UserStat from '../user_stat'
|
import Button from '../button'
|
||||||
|
import Divider from '../divider'
|
||||||
|
import Heading from '../heading'
|
||||||
|
import Icon from '../icon'
|
||||||
|
import Text from '../text'
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
title: { id: 'about', defaultMessage: 'About' },
|
title: { id: 'about', defaultMessage: 'About' },
|
||||||
|
members: { id: 'members', defaultMessage: 'Members' },
|
||||||
})
|
})
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@ -24,7 +29,45 @@ class GroupInfoPanel extends ImmutablePureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelLayout title={intl.formatMessage(messages.title)}>
|
<PanelLayout title={intl.formatMessage(messages.title)}>
|
||||||
|
{
|
||||||
|
!!group &&
|
||||||
|
<Fragment>
|
||||||
|
|
||||||
|
<Heading size='h3'>
|
||||||
|
{group.get('title')}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
|
<Divider small />
|
||||||
|
|
||||||
|
<div className={[_s.default, _s.flexRow, _s.justifyContentCenter].join(' ')}>
|
||||||
|
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||||
|
<Icon id='group' height='14px' width='14px' />
|
||||||
|
<Text size='small' className={_s.marginLeft5PX}>
|
||||||
|
{intl.formatMessage(messages.members)}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
text
|
||||||
|
to={`/groups/${group.get('id')}/members`}
|
||||||
|
color='brand'
|
||||||
|
backgroundColor='none'
|
||||||
|
className={_s.marginLeftAuto}
|
||||||
|
>
|
||||||
|
<Text color='inherit' weight='medium' size='normal' className={_s.underline_onHover}>
|
||||||
|
{shortNumberFormat(group.get('member_count'))}
|
||||||
|
|
||||||
|
{intl.formatMessage(messages.members)}
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Divider small />
|
||||||
|
|
||||||
|
<Text>
|
||||||
|
{group.get('description')}
|
||||||
|
</Text>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
</PanelLayout>
|
</PanelLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
import { Fragment } from 'react'
|
||||||
import { defineMessages, injectIntl } from 'react-intl'
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions'
|
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions'
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import { List as ImmutableList } from 'immutable'
|
import { List as ImmutableList } from 'immutable'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import PanelLayout from './panel_layout'
|
import PanelLayout from './panel_layout'
|
||||||
|
import Divider from '../divider'
|
||||||
import Icon from '../icon'
|
import Icon from '../icon'
|
||||||
import Text from '../text'
|
import Text from '../text'
|
||||||
|
|
||||||
const cx = classNames.bind(_s)
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
title: { id: 'about', defaultMessage: 'About' },
|
title: { id: 'about', defaultMessage: 'About' },
|
||||||
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
|
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
|
||||||
@ -19,12 +18,12 @@ const messages = defineMessages({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const mapStateToProps = (state, { account }) => {
|
const mapStateToProps = (state, { account }) => {
|
||||||
const identityProofs = !!account ? state.getIn(['identity_proofs', account.get('id')], ImmutableList()) : ImmutableList();
|
const identityProofs = !!account ? state.getIn(['identity_proofs', account.get('id')], ImmutableList()) : ImmutableList()
|
||||||
return {
|
return {
|
||||||
identityProofs,
|
identityProofs,
|
||||||
domain: state.getIn(['meta', 'domain']),
|
domain: state.getIn(['meta', 'domain']),
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
@ -54,29 +53,9 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
|||||||
|
|
||||||
const fields = !account ? null : account.get('fields')
|
const fields = !account ? null : account.get('fields')
|
||||||
const content = !account ? null : { __html: account.get('note_emojified') }
|
const content = !account ? null : { __html: account.get('note_emojified') }
|
||||||
const memberSinceDate = !account ? null : intl.formatDate(account.get('created_at'), { month: 'long', year: 'numeric' });
|
const memberSinceDate = !account ? null : intl.formatDate(account.get('created_at'), { month: 'long', year: 'numeric' })
|
||||||
const hasNote = !!content ? (account.get('note').length > 0 && account.get('note') !== '<p></p>') : false
|
const hasNote = !!content ? (account.get('note').length > 0 && account.get('note') !== '<p></p>') : false
|
||||||
|
|
||||||
const lineClasses = cx({
|
|
||||||
default: 1,
|
|
||||||
flexRow: 1,
|
|
||||||
alignItemsCenter: 1,
|
|
||||||
borderTop1PX: 1,
|
|
||||||
borderColorSecondary: 1,
|
|
||||||
marginTop10PX: 1,
|
|
||||||
paddingTop10PX: 1,
|
|
||||||
})
|
|
||||||
|
|
||||||
const memberLineClasses = cx({
|
|
||||||
default: 1,
|
|
||||||
flexRow: 1,
|
|
||||||
alignItemsCenter: 1,
|
|
||||||
borderTop1PX: hasNote,
|
|
||||||
borderColorSecondary: hasNote,
|
|
||||||
marginTop10PX: hasNote,
|
|
||||||
paddingTop10PX: hasNote,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelLayout title={intl.formatMessage(messages.title)}>
|
<PanelLayout title={intl.formatMessage(messages.title)}>
|
||||||
{
|
{
|
||||||
@ -84,10 +63,13 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
|||||||
<div className={[_s.default].join(' ')}>
|
<div className={[_s.default].join(' ')}>
|
||||||
{
|
{
|
||||||
hasNote &&
|
hasNote &&
|
||||||
<div className={_s.dangerousContent} dangerouslySetInnerHTML={content} />
|
<Fragment>
|
||||||
|
<div className={_s.dangerousContent} dangerouslySetInnerHTML={content} />
|
||||||
|
<Divider small />
|
||||||
|
</Fragment>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div className={memberLineClasses}>
|
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||||
<Icon id='calendar' width='12px' height='12px' className={_s.fillcolorSecondary} />
|
<Icon id='calendar' width='12px' height='12px' className={_s.fillcolorSecondary} />
|
||||||
<Text
|
<Text
|
||||||
size='small'
|
size='small'
|
||||||
@ -104,45 +86,50 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
|||||||
|
|
||||||
{(fields.size > 0 || identityProofs.size > 0) && (
|
{(fields.size > 0 || identityProofs.size > 0) && (
|
||||||
<div className={[_s.default]}>
|
<div className={[_s.default]}>
|
||||||
|
|
||||||
{identityProofs.map((proof, i) => (
|
{identityProofs.map((proof, i) => (
|
||||||
<dl className={lineClasses} key={`profile-identity-proof-${i}`}>
|
<Fragment>
|
||||||
<dt
|
<Divider small />
|
||||||
className={_s.dangerousContent}
|
<dl className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')} key={`profile-identity-proof-${i}`}>
|
||||||
dangerouslySetInnerHTML={{ __html: proof.get('provider') }}
|
<dt
|
||||||
/>
|
className={_s.dangerousContent}
|
||||||
|
dangerouslySetInnerHTML={{ __html: proof.get('provider') }}
|
||||||
|
/>
|
||||||
|
|
||||||
{ /* : todo : */ }
|
{ /* : todo : */}
|
||||||
<dd className='verified'>
|
<dd className='verified'>
|
||||||
<a href={proof.get('proof_url')} target='_blank' rel='noopener'>
|
<a href={proof.get('proof_url')} target='_blank' rel='noopener'>
|
||||||
<span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
|
<span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
|
||||||
<Icon id='check' className='verified__mark' />
|
<Icon id='check' className='verified__mark' />
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<a href={proof.get('profile_url')} target='_blank' rel='noopener'>
|
<a href={proof.get('profile_url')} target='_blank' rel='noopener'>
|
||||||
<span
|
<span
|
||||||
className={_s.dangerousContent}
|
className={_s.dangerousContent}
|
||||||
dangerouslySetInnerHTML={{ __html: ' ' + proof.get('provider_username') }}
|
dangerouslySetInnerHTML={{ __html: ' ' + proof.get('provider_username') }}
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{
|
{
|
||||||
fields.map((pair, i) => (
|
fields.map((pair, i) => (
|
||||||
<dl className={lineClasses} key={`profile-field-${i}`}>
|
<Fragment>
|
||||||
<dt
|
<Divider small />
|
||||||
className={[_s.text, _s.dangerousContent].join('')}
|
<dl className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')} key={`profile-field-${i}`}>
|
||||||
dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }}
|
<dt
|
||||||
title={pair.get('name')}
|
className={[_s.text, _s.dangerousContent].join('')}
|
||||||
/>
|
dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }}
|
||||||
<dd
|
title={pair.get('name')}
|
||||||
className={[_s.dangerousContent, _s.marginLeftAuto].join(' ')}
|
/>
|
||||||
title={pair.get('value_plain')}
|
<dd
|
||||||
dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }}
|
className={[_s.dangerousContent, _s.marginLeftAuto].join(' ')}
|
||||||
/>
|
title={pair.get('value_plain')}
|
||||||
</dl>
|
dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }}
|
||||||
|
/>
|
||||||
|
</dl>
|
||||||
|
</Fragment>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class WhoToFollowPanel extends ImmutablePureComponent {
|
|||||||
const { intl, /* suggestions, */ dismissSuggestion } = this.props;
|
const { intl, /* suggestions, */ dismissSuggestion } = this.props;
|
||||||
// : testing!!! :
|
// : testing!!! :
|
||||||
const suggestions = [
|
const suggestions = [
|
||||||
"1",
|
"1","1","1",
|
||||||
]
|
]
|
||||||
// if (suggestions.isEmpty()) {
|
// if (suggestions.isEmpty()) {
|
||||||
// return null;
|
// return null;
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './poll';
|
export { default } from './poll'
|
@ -135,10 +135,10 @@ class PopoverBase extends ImmutablePureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div onKeyDown={this.handleKeyDown} className={containerClasses}>
|
<div onKeyDown={this.handleKeyDown} className={containerClasses}>
|
||||||
<div show={open} placement={popoverPlacement} target={this.findTarget}>
|
{ /* <div show={open} placement={popoverPlacement} target={this.findTarget}>
|
||||||
{ /* <PopoverMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} /> */}
|
<PopoverMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} />
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,9 @@ class PopoverRoot extends PureComponent {
|
|||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
getSnapshotBeforeUpdate() {
|
// getSnapshotBeforeUpdate() {
|
||||||
return { visible: !!this.props.type }
|
// return { visible: !!this.props.type }
|
||||||
}
|
// }
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
router: PropTypes.object,
|
router: PropTypes.object,
|
||||||
@ -73,25 +73,25 @@ class PopoverRoot extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDocumentClick = e => {
|
handleDocumentClick = e => {
|
||||||
if (this.node && !this.node.contains(e.target)) {
|
// if (this.node && !this.node.contains(e.target)) {
|
||||||
this.props.onClose()
|
// this.props.onClose()
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.addEventListener('click', this.handleDocumentClick, false)
|
// document.addEventListener('click', this.handleDocumentClick, false)
|
||||||
document.addEventListener('keydown', this.handleKeyDown, false)
|
// document.addEventListener('keydown', this.handleKeyDown, false)
|
||||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions)
|
// document.addEventListener('touchend', this.handleDocumentClick, listenerOptions)
|
||||||
|
|
||||||
if (this.focusedItem && this.props.openedViaKeyboard) this.focusedItem.focus()
|
// if (this.focusedItem && this.props.openedViaKeyboard) this.focusedItem.focus()
|
||||||
|
|
||||||
this.setState({ mounted: true })
|
// this.setState({ mounted: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
document.removeEventListener('click', this.handleDocumentClick, false)
|
// document.removeEventListener('click', this.handleDocumentClick, false)
|
||||||
document.removeEventListener('keydown', this.handleKeyDown, false)
|
// document.removeEventListener('keydown', this.handleKeyDown, false)
|
||||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions)
|
// document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
setRef = c => {
|
setRef = c => {
|
||||||
@ -159,13 +159,15 @@ class PopoverRoot extends PureComponent {
|
|||||||
|
|
||||||
console.log("popover root - type, visible:", type, visible)
|
console.log("popover root - type, visible:", type, visible)
|
||||||
|
|
||||||
|
// <PopoverBase className={`popover-menu ${placement}`} visible={visible} ref={this.setRef}>
|
||||||
|
// {
|
||||||
|
// visible &&
|
||||||
|
// <UserInfoPopover />
|
||||||
|
// }
|
||||||
|
// </PopoverBase>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopoverBase className={`popover-menu ${placement}`} visible={visible} ref={this.setRef}>
|
<div></div>
|
||||||
{
|
|
||||||
visible &&
|
|
||||||
<UserInfoPopover />
|
|
||||||
}
|
|
||||||
</PopoverBase>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
export { default } from './search_popout';
|
export { default } from './search_popout'
|
@ -1 +1 @@
|
|||||||
export { default } from './setting_toggle';
|
export { default } from './setting_toggle'
|
@ -1 +1 @@
|
|||||||
export { default } from './status';
|
export { default } from './status'
|
@ -321,9 +321,9 @@ class Status extends ImmutablePureComponent {
|
|||||||
prepend = (
|
prepend = (
|
||||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.borderBottom1PX, _s.borderColorSecondary, _s.paddingVertical5PX, _s.paddingHorizontal15PX].join(' ')}>
|
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.borderBottom1PX, _s.borderColorSecondary, _s.paddingVertical5PX, _s.paddingHorizontal15PX].join(' ')}>
|
||||||
<Icon
|
<Icon
|
||||||
id='thumb-tack'
|
id='pin'
|
||||||
width='12px'
|
width='10px'
|
||||||
height='12px'
|
height='10px'
|
||||||
className={_s.fillcolorSecondary}
|
className={_s.fillcolorSecondary}
|
||||||
/>
|
/>
|
||||||
<Text size='small' color='secondary' className={_s.marginLeft5PX}>
|
<Text size='small' color='secondary' className={_s.marginLeft5PX}>
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './status_action_bar';
|
export { default } from './status_action_bar'
|
@ -1 +1 @@
|
|||||||
export { default } from './status_check_box';
|
export { default } from './status_check_box'
|
@ -1 +1 @@
|
|||||||
export { default } from './status_content';
|
export { default } from './status_content'
|
@ -1 +1 @@
|
|||||||
export { default } from './status_list';
|
export { default } from './status_list'
|
@ -98,7 +98,7 @@ export default class StatusList extends ImmutablePureComponent {
|
|||||||
const { statusIds, featuredStatusIds, onLoadMore, timelineId, totalQueuedItemsCount, isLoading, isPartial, withGroupAdmin, group, promotion, promotedStatus, ...other } = this.props;
|
const { statusIds, featuredStatusIds, onLoadMore, timelineId, totalQueuedItemsCount, isLoading, isPartial, withGroupAdmin, group, promotion, promotedStatus, ...other } = this.props;
|
||||||
|
|
||||||
if (isPartial) {
|
if (isPartial) {
|
||||||
return ( <ColumnIndicator type='loading' /> );
|
return <ColumnIndicator type='loading' />
|
||||||
}
|
}
|
||||||
|
|
||||||
let scrollableContent = (isLoading || statusIds.size > 0) ? (
|
let scrollableContent = (isLoading || statusIds.size > 0) ? (
|
||||||
@ -110,7 +110,7 @@ export default class StatusList extends ImmutablePureComponent {
|
|||||||
onClick={onLoadMore}
|
onClick={onLoadMore}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<React.Fragment key={statusId}>
|
<Fragment key={statusId}>
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
id={statusId}
|
id={statusId}
|
||||||
onMoveUp={this.handleMoveUp}
|
onMoveUp={this.handleMoveUp}
|
||||||
@ -120,15 +120,16 @@ export default class StatusList extends ImmutablePureComponent {
|
|||||||
withGroupAdmin={withGroupAdmin}
|
withGroupAdmin={withGroupAdmin}
|
||||||
showThread
|
showThread
|
||||||
/>
|
/>
|
||||||
{promotedStatus && index === promotion.position && (
|
{
|
||||||
|
promotedStatus && index === promotion.position &&
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
id={promotion.status_id}
|
id={promotion.status_id}
|
||||||
contextType={timelineId}
|
contextType={timelineId}
|
||||||
promoted
|
promoted
|
||||||
showThread
|
showThread
|
||||||
/>
|
/>
|
||||||
)}
|
}
|
||||||
</React.Fragment>
|
</Fragment>
|
||||||
))
|
))
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
@ -66,9 +66,6 @@ class TabBarItem extends PureComponent {
|
|||||||
size: !!large ? 'normal' : 'small',
|
size: !!large ? 'normal' : 'small',
|
||||||
color: isCurrent ? 'brand' : large ? 'secondary' : 'primary',
|
color: isCurrent ? 'brand' : large ? 'secondary' : 'primary',
|
||||||
weight: isCurrent ? 'bold' : large ? 'medium' : 'normal',
|
weight: isCurrent ? 'bold' : large ? 'medium' : 'normal',
|
||||||
className: cx({
|
|
||||||
paddingHorizontal5PX: large,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -4,7 +4,6 @@ import { injectIntl, defineMessages } from 'react-intl'
|
|||||||
import { me } from '../initial_state'
|
import { me } from '../initial_state'
|
||||||
import ComposeFormContainer from '../features/compose/containers/compose_form_container'
|
import ComposeFormContainer from '../features/compose/containers/compose_form_container'
|
||||||
import Block from './block'
|
import Block from './block'
|
||||||
import Avatar from './avatar'
|
|
||||||
import Heading from './heading'
|
import Heading from './heading'
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl'
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames'
|
||||||
import { shortNumberFormat } from '../utils/numbers';
|
import { shortNumberFormat } from '../utils/numbers'
|
||||||
|
import Button from './button'
|
||||||
|
import Text from './text'
|
||||||
|
|
||||||
export default class TimelineQueueButtonHeader extends PureComponent {
|
export default class TimelineQueueButtonHeader extends PureComponent {
|
||||||
|
|
||||||
@ -8,21 +10,24 @@ export default class TimelineQueueButtonHeader extends PureComponent {
|
|||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
count: PropTypes.number,
|
count: PropTypes.number,
|
||||||
itemType: PropTypes.string,
|
itemType: PropTypes.string,
|
||||||
};
|
floating: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
count: 0,
|
count: 0,
|
||||||
itemType: 'item',
|
itemType: 'item',
|
||||||
};
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { count, itemType, onClick } = this.props;
|
const { count, itemType, onClick } = this.props
|
||||||
|
|
||||||
const hasItems = (count > 0);
|
const hasItems = count > 0
|
||||||
|
|
||||||
|
// : todo :
|
||||||
|
|
||||||
const classes = classNames('timeline-queue-header', {
|
const classes = classNames('timeline-queue-header', {
|
||||||
'timeline-queue-header--extended': hasItems,
|
'timeline-queue-header--extended': hasItems,
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
@ -40,7 +45,7 @@ export default class TimelineQueueButtonHeader extends PureComponent {
|
|||||||
}
|
}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
export { default } from './toggle_switch';
|
export { default } from './toggle_switch'
|
@ -16,7 +16,7 @@ export default class TrendingItem extends ImmutablePureComponent {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
trend: ImmutablePropTypes.map.isRequired,
|
trend: ImmutablePropTypes.map.isRequired,
|
||||||
};
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
hovering: false,
|
hovering: false,
|
||||||
|
@ -14,7 +14,7 @@ export default class TrendingItemCard extends ImmutablePureComponent {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
trend: ImmutablePropTypes.map.isRequired,
|
trend: ImmutablePropTypes.map.isRequired,
|
||||||
};
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
hovering: false,
|
hovering: false,
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './zoomable_image';
|
export { default } from './zoomable_image'
|
@ -1 +1 @@
|
|||||||
export { default } from './media_item';
|
export { default } from './media_item'
|
@ -1 +1 @@
|
|||||||
export { default } from './account_gallery';
|
export { default } from './account_gallery'
|
@ -1 +1 @@
|
|||||||
export { default } from './header';
|
export { default } from './header'
|
@ -1 +1 @@
|
|||||||
export { default } from './inner_header';
|
export { default } from './inner_header'
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './moved_note';
|
export { default } from './moved_note'
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './profile_info_panel';
|
export { default } from './profile_info_panel'
|
@ -1 +1 @@
|
|||||||
export { default } from './account_timeline';
|
export { default } from './account_timeline'
|
@ -1 +1 @@
|
|||||||
export { default } from './blocks';
|
export { default } from './blocks'
|
@ -1 +1 @@
|
|||||||
export { default } from './column_settings';
|
export { default } from './column_settings'
|
@ -1 +1 @@
|
|||||||
export { default } from './community_timeline';
|
export { default } from './community_timeline'
|
@ -1 +1 @@
|
|||||||
export { default } from './action_bar';
|
export { default } from './action_bar'
|
@ -1 +1 @@
|
|||||||
export { default } from './character_counter';
|
export { default } from './character_counter'
|
@ -1 +1 @@
|
|||||||
export { default } from './compose_form';
|
export { default } from './compose_form'
|
@ -1 +1 @@
|
|||||||
export { default } from './emoji_picker_dropdown';
|
export { default } from './emoji_picker_dropdown'
|
@ -1 +1 @@
|
|||||||
export { default } from './navigation_bar';
|
export { default } from './navigation_bar'
|
@ -1 +1 @@
|
|||||||
export { default } from './poll_form';
|
export { default } from './poll_form'
|
@ -1 +1 @@
|
|||||||
export { default } from './reply_indicator';
|
export { default } from './reply_indicator'
|
@ -1 +1 @@
|
|||||||
export { default } from './search_results';
|
export { default } from './search_results'
|
@ -1 +1 @@
|
|||||||
export { default } from './upload';
|
export { default } from './upload'
|
@ -1 +1 @@
|
|||||||
export { default } from './upload_form';
|
export { default } from './upload_form'
|
@ -1 +1 @@
|
|||||||
export { default } from './upload_progress';
|
export { default } from './upload_progress'
|
@ -1 +1 @@
|
|||||||
export { default } from './warning';
|
export { default } from './warning'
|
@ -1 +1 @@
|
|||||||
export { default } from './compose';
|
export { default } from './compose'
|
@ -1 +1 @@
|
|||||||
export { default } from './domain_blocks';
|
export { default } from './domain_blocks'
|
@ -2,7 +2,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { fetchFavoritedStatuses, expandFavoritedStatuses } from '../../actions/favourites';
|
import { fetchFavoritedStatuses, expandFavoritedStatuses } from '../../actions/favorites';
|
||||||
import { meUsername } from '../../initial_state';
|
import { meUsername } from '../../initial_state';
|
||||||
import StatusList from '../../components/status_list';
|
import StatusList from '../../components/status_list';
|
||||||
import ColumnIndicator from '../../components/column_indicator';
|
import ColumnIndicator from '../../components/column_indicator';
|
||||||
@ -10,15 +10,15 @@ import ColumnIndicator from '../../components/column_indicator';
|
|||||||
const mapStateToProps = (state, { params: { username } }) => {
|
const mapStateToProps = (state, { params: { username } }) => {
|
||||||
return {
|
return {
|
||||||
isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()),
|
isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()),
|
||||||
statusIds: state.getIn(['status_lists', 'favourites', 'items']),
|
statusIds: state.getIn(['status_lists', 'favorites', 'items']),
|
||||||
isLoading: state.getIn(['status_lists', 'favourites', 'isLoading'], true),
|
isLoading: state.getIn(['status_lists', 'favorites', 'isLoading'], true),
|
||||||
hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
|
hasMore: !!state.getIn(['status_lists', 'favorites', 'next']),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@connect(mapStateToProps)
|
@connect(mapStateToProps)
|
||||||
class Favourites extends ImmutablePureComponent {
|
class Favorites extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
@ -46,11 +46,11 @@ class Favourites extends ImmutablePureComponent {
|
|||||||
return (
|
return (
|
||||||
<StatusList
|
<StatusList
|
||||||
statusIds={statusIds}
|
statusIds={statusIds}
|
||||||
scrollKey='favourited_statuses'
|
scrollKey='favorited_statuses'
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={<FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite gabs yet. When you favourite one, it will show up here." />}
|
emptyMessage={<FormattedMessage id='empty_column.favorited_statuses' defaultMessage="You don't have any favorite gabs yet. When you favorite one, it will show up here." />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export { default } from './favorited_statuses'
|
@ -1 +0,0 @@
|
|||||||
export { default } from './favourited_statuses';
|
|
@ -1 +1 @@
|
|||||||
export { default } from './account_authorize';
|
export { default } from './account_authorize'
|
@ -1 +1 @@
|
|||||||
export { default } from './follow_requests';
|
export { default } from './follow_requests'
|
@ -1,34 +1,36 @@
|
|||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash'
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
import ColumnIndicator from '../../components/column_indicator';
|
import ColumnIndicator from '../../components/column_indicator'
|
||||||
import {
|
import {
|
||||||
fetchAccount,
|
fetchAccount,
|
||||||
fetchFollowers,
|
fetchFollowers,
|
||||||
expandFollowers,
|
expandFollowers,
|
||||||
fetchAccountByUsername,
|
fetchAccountByUsername,
|
||||||
} from '../../actions/accounts';
|
} from '../../actions/accounts'
|
||||||
import { me } from '../../initial_state';
|
import { me } from '../../initial_state'
|
||||||
import AccountContainer from '../../containers/account_container';
|
import AccountContainer from '../../containers/account_container'
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list'
|
||||||
|
import Block from '../../components/block'
|
||||||
|
import Heading from '../../components/heading'
|
||||||
|
|
||||||
const mapStateToProps = (state, { params: { username } }) => {
|
const mapStateToProps = (state, { params: { username } }) => {
|
||||||
const accounts = state.getIn(['accounts']);
|
const accounts = state.getIn(['accounts'])
|
||||||
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase());
|
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase())
|
||||||
|
|
||||||
let accountId = -1;
|
let accountId = -1
|
||||||
if (accountFetchError) {
|
if (accountFetchError) {
|
||||||
accountId = null;
|
accountId = null
|
||||||
} else {
|
} else {
|
||||||
let account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase());
|
let account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase())
|
||||||
accountId = account ? account.getIn(['id'], null) : -1;
|
accountId = account ? account.getIn(['id'], null) : -1
|
||||||
}
|
}
|
||||||
|
|
||||||
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false);
|
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false)
|
||||||
const isLocked = state.getIn(['accounts', accountId, 'locked'], false);
|
const isLocked = state.getIn(['accounts', accountId, 'locked'], false)
|
||||||
const isFollowing = state.getIn(['relationships', accountId, 'following'], false);
|
const isFollowing = state.getIn(['relationships', accountId, 'following'], false)
|
||||||
const unavailable = (me == accountId) ? false : (isBlocked || (isLocked && !isFollowing));
|
const unavailable = (me == accountId) ? false : (isBlocked || (isLocked && !isFollowing))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accountId,
|
accountId,
|
||||||
@ -36,69 +38,93 @@ const mapStateToProps = (state, { params: { username } }) => {
|
|||||||
isAccount: !!state.getIn(['accounts', accountId]),
|
isAccount: !!state.getIn(['accounts', accountId]),
|
||||||
accountIds: state.getIn(['user_lists', 'followers', accountId, 'items']),
|
accountIds: state.getIn(['user_lists', 'followers', accountId, 'items']),
|
||||||
hasMore: !!state.getIn(['user_lists', 'followers', accountId, 'next']),
|
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
|
export default
|
||||||
@connect(mapStateToProps)
|
@connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class Followers extends ImmutablePureComponent {
|
class Followers extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isAccount: PropTypes.bool,
|
isAccount: PropTypes.bool,
|
||||||
unavailable: PropTypes.bool,
|
unavailable: PropTypes.bool,
|
||||||
};
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const { params: { username }, accountId } = this.props;
|
const { params: { username }, accountId } = this.props
|
||||||
|
|
||||||
if (accountId && accountId !== -1) {
|
if (accountId && accountId !== -1) {
|
||||||
this.props.dispatch(fetchAccount(accountId));
|
this.props.dispatch(fetchAccount(accountId))
|
||||||
this.props.dispatch(fetchFollowers(accountId));
|
this.props.dispatch(fetchFollowers(accountId))
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(fetchAccountByUsername(username));
|
this.props.dispatch(fetchAccountByUsername(username))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) {
|
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) {
|
||||||
this.props.dispatch(fetchAccount(nextProps.accountId));
|
this.props.dispatch(fetchAccount(nextProps.accountId))
|
||||||
this.props.dispatch(fetchFollowers(nextProps.accountId));
|
this.props.dispatch(fetchFollowers(nextProps.accountId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoadMore = debounce(() => {
|
handleLoadMore = debounce(() => {
|
||||||
if (this.props.accountId && this.props.accountId !== -1) {
|
if (this.props.accountId && this.props.accountId !== -1) {
|
||||||
this.props.dispatch(expandFollowers(this.props.accountId));
|
this.props.dispatch(expandFollowers(this.props.accountId))
|
||||||
}
|
}
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true })
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { accountIds, hasMore, isAccount, accountId, unavailable } = this.props;
|
const {
|
||||||
|
accountIds,
|
||||||
|
hasMore,
|
||||||
|
isAccount,
|
||||||
|
accountId,
|
||||||
|
unavailable,
|
||||||
|
intl
|
||||||
|
} = this.props
|
||||||
|
|
||||||
if (!isAccount && accountId !== -1) {
|
if (!isAccount && accountId !== -1) {
|
||||||
return (<ColumnIndicator type='missing' />);
|
return <ColumnIndicator type='missing' />
|
||||||
} else if (accountId === -1 || (!accountIds)) {
|
} else if (accountId === -1 || (!accountIds)) {
|
||||||
return (<ColumnIndicator type='loading' />);
|
return <ColumnIndicator type='loading' />
|
||||||
} else if (unavailable) {
|
} else if (unavailable) {
|
||||||
return (<ColumnIndicator type='error' message={<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />} />);
|
return <ColumnIndicator type='error' message={intl.formatMessage(messages.unavailable)} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollableList
|
<Block>
|
||||||
scrollKey='followers'
|
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX, _s.justifyContentCenter, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}>
|
||||||
hasMore={hasMore}
|
<Heading size='h3'>
|
||||||
onLoadMore={this.handleLoadMore}
|
{intl.formatMessage(messages.followers)}
|
||||||
emptyMessage={<FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />}
|
</Heading>
|
||||||
>
|
</div>
|
||||||
{accountIds.map(id =>
|
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>
|
||||||
<AccountContainer key={id} id={id} withNote={false} />
|
<ScrollableList
|
||||||
)}
|
scrollKey='followers'
|
||||||
</ScrollableList>
|
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 +1 @@
|
|||||||
export { default } from './followers';
|
export { default } from './followers'
|
@ -1,34 +1,36 @@
|
|||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash'
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
import {
|
import {
|
||||||
fetchAccount,
|
fetchAccount,
|
||||||
fetchFollowing,
|
fetchFollowing,
|
||||||
expandFollowing,
|
expandFollowing,
|
||||||
fetchAccountByUsername,
|
fetchAccountByUsername,
|
||||||
} from '../../actions/accounts';
|
} from '../../actions/accounts'
|
||||||
import { me } from '../../initial_state';
|
import { me } from '../../initial_state'
|
||||||
import AccountContainer from '../../containers/account_container';
|
import AccountContainer from '../../containers/account_container'
|
||||||
import ColumnIndicator from '../../components/column_indicator';
|
import ColumnIndicator from '../../components/column_indicator'
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list'
|
||||||
|
import Block from '../../components/block'
|
||||||
|
import Heading from '../../components/heading'
|
||||||
|
|
||||||
const mapStateToProps = (state, { params: { username } }) => {
|
const mapStateToProps = (state, { params: { username } }) => {
|
||||||
const accounts = state.getIn(['accounts']);
|
const accounts = state.getIn(['accounts'])
|
||||||
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase());
|
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase())
|
||||||
|
|
||||||
let accountId = -1;
|
let accountId = -1
|
||||||
if (accountFetchError) {
|
if (accountFetchError) {
|
||||||
accountId = null;
|
accountId = null
|
||||||
} else {
|
} else {
|
||||||
let account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase());
|
let account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase())
|
||||||
accountId = account ? account.getIn(['id'], null) : -1;
|
accountId = account ? account.getIn(['id'], null) : -1
|
||||||
}
|
}
|
||||||
|
|
||||||
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false);
|
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false)
|
||||||
const isLocked = state.getIn(['accounts', accountId, 'locked'], false);
|
const isLocked = state.getIn(['accounts', accountId, 'locked'], false)
|
||||||
const isFollowing = state.getIn(['relationships', accountId, 'following'], false);
|
const isFollowing = state.getIn(['relationships', accountId, 'following'], false)
|
||||||
const unavailable = (me == accountId) ? false : (isBlocked || (isLocked && !isFollowing));
|
const unavailable = (me == accountId) ? false : (isBlocked || (isLocked && !isFollowing))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accountId,
|
accountId,
|
||||||
@ -36,71 +38,93 @@ const mapStateToProps = (state, { params: { username } }) => {
|
|||||||
isAccount: !!state.getIn(['accounts', accountId]),
|
isAccount: !!state.getIn(['accounts', accountId]),
|
||||||
accountIds: state.getIn(['user_lists', 'following', accountId, 'items']),
|
accountIds: state.getIn(['user_lists', 'following', accountId, 'items']),
|
||||||
hasMore: !!state.getIn(['user_lists', 'following', accountId, 'next']),
|
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
|
export default
|
||||||
@connect(mapStateToProps)
|
@connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class Following extends ImmutablePureComponent {
|
class Following extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isAccount: PropTypes.bool,
|
isAccount: PropTypes.bool,
|
||||||
unavailable: PropTypes.bool,
|
unavailable: PropTypes.bool,
|
||||||
};
|
}
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount() {
|
||||||
const { params: { username }, accountId } = this.props;
|
const { params: { username }, accountId } = this.props
|
||||||
|
|
||||||
if (accountId && accountId !== -1) {
|
if (accountId && accountId !== -1) {
|
||||||
this.props.dispatch(fetchAccount(accountId));
|
this.props.dispatch(fetchAccount(accountId))
|
||||||
this.props.dispatch(fetchFollowing(accountId));
|
this.props.dispatch(fetchFollowing(accountId))
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(fetchAccountByUsername(username));
|
this.props.dispatch(fetchAccountByUsername(username))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) {
|
if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) {
|
||||||
this.props.dispatch(fetchAccount(nextProps.accountId));
|
this.props.dispatch(fetchAccount(nextProps.accountId))
|
||||||
this.props.dispatch(fetchFollowing(nextProps.accountId));
|
this.props.dispatch(fetchFollowing(nextProps.accountId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoadMore = debounce(() => {
|
handleLoadMore = debounce(() => {
|
||||||
if (this.props.accountId && this.props.accountId !== -1) {
|
if (this.props.accountId && this.props.accountId !== -1) {
|
||||||
this.props.dispatch(expandFollowing(this.props.accountId));
|
this.props.dispatch(expandFollowing(this.props.accountId))
|
||||||
}
|
}
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true })
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { accountIds, hasMore, isAccount, accountId, unavailable } = this.props;
|
const {
|
||||||
|
accountIds,
|
||||||
|
hasMore,
|
||||||
|
isAccount,
|
||||||
|
accountId,
|
||||||
|
unavailable,
|
||||||
|
intl
|
||||||
|
} = this.props
|
||||||
|
|
||||||
if (!isAccount && accountId !== -1) {
|
if (!isAccount && accountId !== -1) {
|
||||||
return ( <ColumnIndicator type='missing' /> );
|
return <ColumnIndicator type='missing' />
|
||||||
} else if (accountId === -1 || (!accountIds)) {
|
} else if (accountId === -1 || (!accountIds)) {
|
||||||
return ( <ColumnIndicator type='loading' /> );
|
return <ColumnIndicator type='loading' />
|
||||||
} else if (unavailable) {
|
} else if (unavailable) {
|
||||||
return (<ColumnIndicator type='error' message={<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />} />);
|
return <ColumnIndicator type='error' message={intl.formatMessage(messages.unavailable)} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollableList
|
<Block>
|
||||||
scrollKey='following'
|
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX, _s.justifyContentCenter, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}>
|
||||||
hasMore={hasMore}
|
<Heading size='h3'>
|
||||||
onLoadMore={this.handleLoadMore}
|
{intl.formatMessage(messages.follows)}
|
||||||
emptyMessage={<FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />}
|
</Heading>
|
||||||
>
|
</div>
|
||||||
{accountIds.map(id =>
|
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>
|
||||||
<AccountContainer key={id} id={id} withNote={false} />
|
<ScrollableList
|
||||||
)}
|
scrollKey='following'
|
||||||
</ScrollableList>
|
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 +1 @@
|
|||||||
export { default } from './following';
|
export { default } from './following'
|
@ -1 +1 @@
|
|||||||
export { default } from './generic_not_found';
|
export { default } from './generic_not_found'
|
@ -1,7 +1,9 @@
|
|||||||
import { changeValue, submit, reset } from '../../../actions/group_editor';
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
import Icon from '../../../components/icon';
|
import { changeValue, submit, reset } from '../../actions/group_editor'
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import Block from '../../components/block'
|
||||||
import classNames from 'classnames';
|
import Button from '../../components/button'
|
||||||
|
import Icon from '../../components/icon'
|
||||||
|
import Input from '../../components/icon'
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
title: { id: 'groups.form.title', defaultMessage: 'Enter a new group title' },
|
title: { id: 'groups.form.title', defaultMessage: 'Enter a new group title' },
|
||||||
@ -9,14 +11,14 @@ const messages = defineMessages({
|
|||||||
coverImage: { id: 'groups.form.coverImage', defaultMessage: 'Upload a banner image' },
|
coverImage: { id: 'groups.form.coverImage', defaultMessage: 'Upload a banner image' },
|
||||||
coverImageChange: { id: 'groups.form.coverImageChange', defaultMessage: 'Banner image selected' },
|
coverImageChange: { id: 'groups.form.coverImageChange', defaultMessage: 'Banner image selected' },
|
||||||
create: { id: 'groups.form.create', defaultMessage: 'Create group' },
|
create: { id: 'groups.form.create', defaultMessage: 'Create group' },
|
||||||
});
|
})
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
title: state.getIn(['group_editor', 'title']),
|
title: state.getIn(['group_editor', 'title']),
|
||||||
description: state.getIn(['group_editor', 'description']),
|
description: state.getIn(['group_editor', 'description']),
|
||||||
coverImage: state.getIn(['group_editor', 'coverImage']),
|
coverImage: state.getIn(['group_editor', 'coverImage']),
|
||||||
disabled: state.getIn(['group_editor', 'isSubmitting']),
|
disabled: state.getIn(['group_editor', 'isSubmitting']),
|
||||||
});
|
})
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
onTitleChange: value => dispatch(changeValue('title', value)),
|
onTitleChange: value => dispatch(changeValue('title', value)),
|
||||||
@ -24,7 +26,7 @@ const mapDispatchToProps = dispatch => ({
|
|||||||
onCoverImageChange: value => dispatch(changeValue('coverImage', value)),
|
onCoverImageChange: value => dispatch(changeValue('coverImage', value)),
|
||||||
onSubmit: routerHistory => dispatch(submit(routerHistory)),
|
onSubmit: routerHistory => dispatch(submit(routerHistory)),
|
||||||
reset: () => dispatch(reset()),
|
reset: () => dispatch(reset()),
|
||||||
});
|
})
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@connect(mapStateToProps, mapDispatchToProps)
|
@connect(mapStateToProps, mapDispatchToProps)
|
||||||
@ -43,69 +45,70 @@ class Create extends PureComponent {
|
|||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
onTitleChange: PropTypes.func.isRequired,
|
onTitleChange: PropTypes.func.isRequired,
|
||||||
onSubmit: PropTypes.func.isRequired,
|
onSubmit: PropTypes.func.isRequired,
|
||||||
};
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.props.reset();
|
this.props.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTitleChange = e => {
|
handleTitleChange = e => {
|
||||||
this.props.onTitleChange(e.target.value);
|
this.props.onTitleChange(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDescriptionChange = e => {
|
handleDescriptionChange = e => {
|
||||||
this.props.onDescriptionChange(e.target.value);
|
this.props.onDescriptionChange(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCoverImageChange = e => {
|
handleCoverImageChange = e => {
|
||||||
this.props.onCoverImageChange(e.target.files[0]);
|
this.props.onCoverImageChange(e.target.files[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit = e => {
|
handleSubmit = e => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
this.props.onSubmit(this.context.router.history);
|
this.props.onSubmit(this.context.router.history)
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { title, description, coverImage, disabled, intl } = this.props;
|
const { title, description, coverImage, disabled, intl } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className='group-form' onSubmit={this.handleSubmit}>
|
<Block>
|
||||||
<div>
|
<form className='group-form' onSubmit={this.handleSubmit}>
|
||||||
<input
|
<div>
|
||||||
className='standard'
|
<Input
|
||||||
type='text'
|
type='text'
|
||||||
value={title}
|
value={title}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={this.handleTitleChange}
|
onChange={this.handleTitleChange}
|
||||||
placeholder={intl.formatMessage(messages.title)}
|
placeholder={intl.formatMessage(messages.title)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<textarea
|
<textarea
|
||||||
className='standard'
|
className='standard'
|
||||||
type='text'
|
type='text'
|
||||||
value={description}
|
value={description}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={this.handleDescriptionChange}
|
onChange={this.handleDescriptionChange}
|
||||||
placeholder={intl.formatMessage(messages.description)}
|
placeholder={intl.formatMessage(messages.description)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor='group_cover_image' className={classNames('group-form__file-label', { 'group-form__file-label--selected': coverImage !== null })}>
|
<label htmlFor='group_cover_image' className='group-form__file-label--selected'>
|
||||||
{intl.formatMessage(coverImage === null ? messages.coverImage : messages.coverImageChange)}
|
{intl.formatMessage(coverImage === null ? messages.coverImage : messages.coverImageChange)}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type='file'
|
type='file'
|
||||||
className='group-form__file'
|
className='group-form__file'
|
||||||
id='group_cover_image'
|
id='group_cover_image'
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={this.handleCoverImageChange}
|
onChange={this.handleCoverImageChange}
|
||||||
/>
|
/>
|
||||||
<button className='button'>{intl.formatMessage(messages.create)}</button>
|
<button className='button'>{intl.formatMessage(messages.create)}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
</Block>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
1
app/javascript/gabsocial/features/group_create/index.js
Normal file
1
app/javascript/gabsocial/features/group_create/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './group_create'
|
@ -0,0 +1,83 @@
|
|||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
import {
|
||||||
|
fetchMembers,
|
||||||
|
expandMembers,
|
||||||
|
updateRole,
|
||||||
|
createRemovedAccount,
|
||||||
|
} from '../../actions/groups';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import AccountContainer from '../../containers/account_container';
|
||||||
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { params: { id } }) => ({
|
||||||
|
group: state.getIn(['groups', id]),
|
||||||
|
relationships: state.getIn(['group_relationships', id]),
|
||||||
|
accountIds: state.getIn(['user_lists', 'groups', id, 'items']),
|
||||||
|
hasMore: !!state.getIn(['user_lists', 'groups', id, 'next']),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default
|
||||||
|
@connect(mapStateToProps)
|
||||||
|
class GroupMembers extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
params: PropTypes.object.isRequired,
|
||||||
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
accountIds: ImmutablePropTypes.list,
|
||||||
|
hasMore: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
const { params: { id } } = this.props;
|
||||||
|
|
||||||
|
this.props.dispatch(fetchMembers(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.params.id !== this.props.params.id) {
|
||||||
|
this.props.dispatch(fetchMembers(nextProps.params.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLoadMore = debounce(() => {
|
||||||
|
this.props.dispatch(expandMembers(this.props.params.id));
|
||||||
|
}, 300, { leading: true });
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { accountIds, hasMore, group, relationships, dispatch } = this.props;
|
||||||
|
|
||||||
|
if (!group || !accountIds || !relationships) {
|
||||||
|
return <LoadingIndicator />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollableList
|
||||||
|
scrollKey='members'
|
||||||
|
hasMore={hasMore}
|
||||||
|
onLoadMore={this.handleLoadMore}
|
||||||
|
emptyMessage={<FormattedMessage id='group.members.empty' defaultMessage='This group does not has any members.' />}
|
||||||
|
>
|
||||||
|
{accountIds.map(id => {
|
||||||
|
let menu = [];
|
||||||
|
|
||||||
|
if (relationships.get('admin')) {
|
||||||
|
menu = [
|
||||||
|
{ text: 'Remove from group', action: () => dispatch(createRemovedAccount(group.get('id'), id)) },
|
||||||
|
{ text: 'Make administrator', action: () => dispatch(updateRole(group.get('id'), id, 'admin')) },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="group-account-wrapper" key={id}>
|
||||||
|
<AccountContainer id={id} withNote={false} actionIcon="none" onActionClick={() => true} />
|
||||||
|
{menu.length > 0 && <DropdownMenuContainer items={menu} icon='ellipsis-h' size={18} direction='right' />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ScrollableList>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1
app/javascript/gabsocial/features/group_members/index.js
Normal file
1
app/javascript/gabsocial/features/group_members/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './group_members'
|
@ -1,15 +1,15 @@
|
|||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import ColumnIndicator from '../../../components/column_indicator';
|
import ColumnIndicator from '../../components/column_indicator';
|
||||||
import {
|
import {
|
||||||
fetchRemovedAccounts,
|
fetchRemovedAccounts,
|
||||||
expandRemovedAccounts,
|
expandRemovedAccounts,
|
||||||
removeRemovedAccount,
|
removeRemovedAccount,
|
||||||
} from '../../../actions/groups';
|
} from '../../actions/groups';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import AccountContainer from '../../../containers/account_container';
|
import AccountContainer from '../../containers/account_container';
|
||||||
import ScrollableList from '../../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
@ -0,0 +1 @@
|
|||||||
|
export { default } from './group_removed_accounts'
|
@ -1,105 +1,98 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import { injectIntl, defineMessages } from 'react-intl'
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom'
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames'
|
||||||
import { fetchGroups } from '../../../actions/groups';
|
import { connectGroupStream } from '../../actions/streaming'
|
||||||
import { openModal } from '../../../actions/modal';
|
import { expandGroupTimeline } from '../../actions/timelines'
|
||||||
import { me } from '../../../initial_state';
|
import StatusListContainer from '../../containers/status_list_container'
|
||||||
import GroupCard from './card';
|
// import ColumnSettingsContainer from './containers/column_settings_container'
|
||||||
import GroupCreate from '../create';
|
import ColumnIndicator from '../../components/column_indicator'
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'column.groups', defaultMessage: 'Groups' },
|
tabLatest: { id: 'group.timeline.tab_latest', defaultMessage: 'Latest' },
|
||||||
create: { id: 'groups.create', defaultMessage: 'Create group' },
|
show: { id: 'group.timeline.show_settings', defaultMessage: 'Show settings' },
|
||||||
tab_featured: { id: 'groups.tab_featured', defaultMessage: 'Featured' },
|
hide: { id: 'group.timeline.hide_settings', defaultMessage: 'Hide settings' },
|
||||||
tab_member: { id: 'groups.tab_member', defaultMessage: 'Member' },
|
empty: { id: 'empty_column.group', defaultMessage: 'There is nothing in this group yet.\nWhen members of this group post new statuses, they will appear here.' },
|
||||||
tab_admin: { id: 'groups.tab_admin', defaultMessage: 'Manage' },
|
})
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { activeTab }) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
groupIds: state.getIn(['group_lists', activeTab]),
|
group: state.getIn(['groups', props.params.id]),
|
||||||
account: state.getIn(['accounts', me]),
|
relationships: state.getIn(['group_relationships', props.params.id]),
|
||||||
});
|
hasUnread: state.getIn(['timelines', `group:${props.params.id}`, 'unread']) > 0,
|
||||||
|
})
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@connect(mapStateToProps)
|
@connect(mapStateToProps)
|
||||||
@injectIntl
|
@injectIntl
|
||||||
class Groups extends ImmutablePureComponent {
|
class GroupTimeline extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
|
||||||
params: PropTypes.object.isRequired,
|
|
||||||
activeTab: PropTypes.string.isRequired,
|
|
||||||
showCreateForm: PropTypes.bool,
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
groups: ImmutablePropTypes.map,
|
|
||||||
groupIds: ImmutablePropTypes.list,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentWillMount () {
|
static contextTypes = {
|
||||||
this.props.dispatch(fetchGroups(this.props.activeTab));
|
router: PropTypes.object,
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(oldProps) {
|
static propTypes = {
|
||||||
if (this.props.activeTab && this.props.activeTab !== oldProps.activeTab) {
|
params: PropTypes.object.isRequired,
|
||||||
this.props.dispatch(fetchGroups(this.props.activeTab));
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
columnId: PropTypes.string,
|
||||||
|
hasUnread: PropTypes.bool,
|
||||||
|
group: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
||||||
|
relationships: ImmutablePropTypes.map,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
collapsed: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { dispatch } = this.props
|
||||||
|
const { id } = this.props.params
|
||||||
|
|
||||||
|
dispatch(expandGroupTimeline(id))
|
||||||
|
|
||||||
|
this.disconnect = dispatch(connectGroupStream(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.disconnect) {
|
||||||
|
this.disconnect()
|
||||||
|
this.disconnect = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOpenProUpgradeModal = () => {
|
handleLoadMore = maxId => {
|
||||||
this.props.dispatch(openModal('PRO_UPGRADE'));
|
const { id } = this.props.params
|
||||||
|
this.props.dispatch(expandGroupTimeline(id, { maxId }))
|
||||||
}
|
}
|
||||||
|
|
||||||
renderHeader() {
|
handleToggleClick = (e) => {
|
||||||
const { intl, activeTab, account, onOpenProUpgradeModal } = this.props;
|
e.stopPropagation()
|
||||||
|
this.setState({ collapsed: !this.state.collapsed })
|
||||||
|
}
|
||||||
|
|
||||||
const isPro = account.get('is_pro');
|
render() {
|
||||||
|
const { columnId, group, relationships, account, intl } = this.props
|
||||||
|
const { collapsed } = this.state
|
||||||
|
const { id } = this.props.params
|
||||||
|
|
||||||
|
if (typeof group === 'undefined' || !relationships) {
|
||||||
|
return <ColumnIndicator type='loading' />
|
||||||
|
} else if (group === false) {
|
||||||
|
return <ColumnIndicator type='missing' />
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group-column-header">
|
<StatusListContainer
|
||||||
<div className="group-column-header__cta">
|
alwaysPrepend
|
||||||
{
|
scrollKey={`group_timeline-${columnId}`}
|
||||||
account && isPro &&
|
timelineId={`group:${id}`}
|
||||||
<Link to="/groups/create" className="button standard-small">{intl.formatMessage(messages.create)}</Link>
|
onLoadMore={this.handleLoadMore}
|
||||||
}
|
group={group}
|
||||||
{
|
withGroupAdmin={relationships && relationships.get('admin')}
|
||||||
account && !isPro &&
|
emptyMessage={intl.formatMessage(messages.empty)}
|
||||||
<button onClick={this.handleOpenProUpgradeModal} className="button standard-small">{intl.formatMessage(messages.create)}</button>
|
/>
|
||||||
}
|
)
|
||||||
</div>
|
|
||||||
<div className="group-column-header__title">{intl.formatMessage(messages.heading)}</div>
|
|
||||||
|
|
||||||
<div className="column-header__wrapper">
|
|
||||||
<h1 className="column-header">
|
|
||||||
<Link to='/groups' className={classNames('btn grouped', {'active': 'featured' === activeTab})}>
|
|
||||||
{intl.formatMessage(messages.tab_featured)}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link to='/groups/browse/member' className={classNames('btn grouped', {'active': 'member' === activeTab})}>
|
|
||||||
{intl.formatMessage(messages.tab_member)}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link to='/groups/browse/admin' className={classNames('btn grouped', {'active': 'admin' === activeTab})}>
|
|
||||||
{intl.formatMessage(messages.tab_admin)}
|
|
||||||
</Link>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
}
|
||||||
const { groupIds, showCreateForm } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{!showCreateForm && this.renderHeader()}
|
|
||||||
{showCreateForm && <GroupCreate /> }
|
|
||||||
|
|
||||||
<div className="group-card-list">
|
|
||||||
{groupIds.map(id => <GroupCard key={id} id={id} />)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './group_timeline';
|
export { default } from './group_timeline'
|
||||||
|
@ -6,27 +6,27 @@ import ColumnIndicator from '../../../components/column_indicator';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
title: { id: 'groups.form.title', defaultMessage: 'Title' },
|
title: { id: 'groups.form.title', defaultMessage: 'Title' },
|
||||||
description: { id: 'groups.form.description', defaultMessage: 'Description' },
|
description: { id: 'groups.form.description', defaultMessage: 'Description' },
|
||||||
coverImage: { id: 'groups.form.coverImage', defaultMessage: 'Upload new banner image (optional)' },
|
coverImage: { id: 'groups.form.coverImage', defaultMessage: 'Upload new banner image (optional)' },
|
||||||
coverImageChange: { id: 'groups.form.coverImageChange', defaultMessage: 'Banner image selected' },
|
coverImageChange: { id: 'groups.form.coverImageChange', defaultMessage: 'Banner image selected' },
|
||||||
update: { id: 'groups.form.update', defaultMessage: 'Update group' },
|
update: { id: 'groups.form.update', defaultMessage: 'Update group' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
group: state.getIn(['groups', props.params.id]),
|
group: state.getIn(['groups', props.params.id]),
|
||||||
title: state.getIn(['group_editor', 'title']),
|
title: state.getIn(['group_editor', 'title']),
|
||||||
description: state.getIn(['group_editor', 'description']),
|
description: state.getIn(['group_editor', 'description']),
|
||||||
coverImage: state.getIn(['group_editor', 'coverImage']),
|
coverImage: state.getIn(['group_editor', 'coverImage']),
|
||||||
disabled: state.getIn(['group_editor', 'isSubmitting']),
|
disabled: state.getIn(['group_editor', 'isSubmitting']),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
onTitleChange: value => dispatch(changeValue('title', value)),
|
onTitleChange: value => dispatch(changeValue('title', value)),
|
||||||
onDescriptionChange: value => dispatch(changeValue('description', value)),
|
onDescriptionChange: value => dispatch(changeValue('description', value)),
|
||||||
onCoverImageChange: value => dispatch(changeValue('coverImage', value)),
|
onCoverImageChange: value => dispatch(changeValue('coverImage', value)),
|
||||||
onSubmit: routerHistory => dispatch(submit(routerHistory)),
|
onSubmit: routerHistory => dispatch(submit(routerHistory)),
|
||||||
setUp: group => dispatch(setUp(group)),
|
setUp: group => dispatch(setUp(group)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@ -35,103 +35,103 @@ export default
|
|||||||
class Edit extends ImmutablePureComponent {
|
class Edit extends ImmutablePureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
router: PropTypes.object,
|
router: PropTypes.object,
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
group: ImmutablePropTypes.map,
|
group: ImmutablePropTypes.map,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
description: PropTypes.string.isRequired,
|
description: PropTypes.string.isRequired,
|
||||||
coverImage: PropTypes.object,
|
coverImage: PropTypes.object,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
onTitleChange: PropTypes.func.isRequired,
|
onTitleChange: PropTypes.func.isRequired,
|
||||||
onSubmit: PropTypes.func.isRequired,
|
onSubmit: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount(nextProps) {
|
componentWillMount(nextProps) {
|
||||||
if (this.props.group) {
|
if (this.props.group) {
|
||||||
this.props.setUp(this.props.group);
|
this.props.setUp(this.props.group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (!this.props.group && nextProps.group) {
|
if (!this.props.group && nextProps.group) {
|
||||||
this.props.setUp(nextProps.group);
|
this.props.setUp(nextProps.group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTitleChange = e => {
|
handleTitleChange = e => {
|
||||||
this.props.onTitleChange(e.target.value);
|
this.props.onTitleChange(e.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDescriptionChange = e => {
|
handleDescriptionChange = e => {
|
||||||
this.props.onDescriptionChange(e.target.value);
|
this.props.onDescriptionChange(e.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCoverImageChange = e => {
|
handleCoverImageChange = e => {
|
||||||
this.props.onCoverImageChange(e.target.files[0]);
|
this.props.onCoverImageChange(e.target.files[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit = e => {
|
handleSubmit = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onSubmit(this.context.router.history);
|
this.props.onSubmit(this.context.router.history);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
this.props.onSubmit(this.context.router.history);
|
this.props.onSubmit(this.context.router.history);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { group, title, description, coverImage, disabled, intl } = this.props;
|
const { group, title, description, coverImage, disabled, intl } = this.props;
|
||||||
|
|
||||||
if (typeof group === 'undefined') {
|
if (typeof group === 'undefined') {
|
||||||
return ( <ColumnIndicator type='loading' /> );
|
return <ColumnIndicator type='loading' />
|
||||||
} else if (group === false) {
|
} else if (group === false) {
|
||||||
return (<ColumnIndicator type='missing' />);
|
return <ColumnIndicator type='missing' />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className='group-form' onSubmit={this.handleSubmit}>
|
<form className='group-form' onSubmit={this.handleSubmit}>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
className='standard'
|
className='standard'
|
||||||
type='text'
|
type='text'
|
||||||
value={title}
|
value={title}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={this.handleTitleChange}
|
onChange={this.handleTitleChange}
|
||||||
placeholder={intl.formatMessage(messages.title)}
|
placeholder={intl.formatMessage(messages.title)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<textarea
|
<textarea
|
||||||
className='standard'
|
className='standard'
|
||||||
type='text'
|
type='text'
|
||||||
value={description}
|
value={description}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={this.handleDescriptionChange}
|
onChange={this.handleDescriptionChange}
|
||||||
placeholder={intl.formatMessage(messages.description)}
|
placeholder={intl.formatMessage(messages.description)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor='group_cover_image' className={classNames('group-form__file-label', { 'group-form__file-label--selected': coverImage !== null })}>
|
<label htmlFor='group_cover_image' className={classNames('group-form__file-label', { 'group-form__file-label--selected': coverImage !== null })}>
|
||||||
{intl.formatMessage(coverImage === null ? messages.coverImage : messages.coverImageChange)}
|
{intl.formatMessage(coverImage === null ? messages.coverImage : messages.coverImageChange)}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type='file'
|
type='file'
|
||||||
className='group-form__file'
|
className='group-form__file'
|
||||||
id='group_cover_image'
|
id='group_cover_image'
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={this.handleCoverImageChange}
|
onChange={this.handleCoverImageChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button>{intl.formatMessage(messages.update)}</button>
|
<button>{intl.formatMessage(messages.update)}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { shortNumberFormat } from '../../../utils/numbers';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
members: { id: 'groups.card.members', defaultMessage: 'Members' },
|
|
||||||
view: { id: 'groups.card.view', defaultMessage: 'View' },
|
|
||||||
join: { id: 'groups.card.join', defaultMessage: 'Join' },
|
|
||||||
role_member: { id: 'groups.card.roles.member', defaultMessage: 'You\'re a member' },
|
|
||||||
role_admin: { id: 'groups.card.roles.admin', defaultMessage: 'You\'re an admin' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { id }) => ({
|
|
||||||
group: state.getIn(['groups', id]),
|
|
||||||
relationships: state.getIn(['group_relationships', id]),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default
|
|
||||||
@connect(mapStateToProps)
|
|
||||||
@injectIntl
|
|
||||||
class GroupCard extends ImmutablePureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
group: ImmutablePropTypes.map,
|
|
||||||
relationships: ImmutablePropTypes.map,
|
|
||||||
}
|
|
||||||
|
|
||||||
getRole() {
|
|
||||||
const { intl, relationships } = this.props;
|
|
||||||
|
|
||||||
if (!relationships) return null;
|
|
||||||
if (relationships.get('admin')) return intl.formatMessage(messages.role_admin);
|
|
||||||
if (relationships.get('member')) return intl.formatMessage(messages.role_member);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { intl, group } = this.props;
|
|
||||||
const coverImageUrl = group.get('cover_image_url');
|
|
||||||
const role = this.getRole();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link to={`/groups/${group.get('id')}`} className="group-card">
|
|
||||||
<div className="group-card__header">{coverImageUrl && <img alt="" src={coverImageUrl} />}</div>
|
|
||||||
<div className="group-card__content">
|
|
||||||
<div className="group-card__title">{group.get('title')}</div>
|
|
||||||
<div className="group-card__meta"><strong>{shortNumberFormat(group.get('member_count'))}</strong> {intl.formatMessage(messages.members)}{role && <span> · {role}</span>}</div>
|
|
||||||
<div className="group-card__description">{group.get('description')}</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { fetchGroups } from '../../../actions/groups';
|
|
||||||
import { openModal } from '../../../actions/modal';
|
|
||||||
import { me } from '../../../initial_state';
|
|
||||||
import GroupCard from './card';
|
|
||||||
import GroupCreate from '../create';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
heading: { id: 'column.groups', defaultMessage: 'Groups' },
|
|
||||||
create: { id: 'groups.create', defaultMessage: 'Create group' },
|
|
||||||
tab_featured: { id: 'groups.tab_featured', defaultMessage: 'Featured' },
|
|
||||||
tab_member: { id: 'groups.tab_member', defaultMessage: 'Member' },
|
|
||||||
tab_admin: { id: 'groups.tab_admin', defaultMessage: 'Manage' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { activeTab }) => ({
|
|
||||||
groupIds: state.getIn(['group_lists', activeTab]),
|
|
||||||
account: state.getIn(['accounts', me]),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default
|
|
||||||
@connect(mapStateToProps)
|
|
||||||
@injectIntl
|
|
||||||
class Groups extends ImmutablePureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
params: PropTypes.object.isRequired,
|
|
||||||
activeTab: PropTypes.string.isRequired,
|
|
||||||
showCreateForm: PropTypes.bool,
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
groups: ImmutablePropTypes.map,
|
|
||||||
groupIds: ImmutablePropTypes.list,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentWillMount () {
|
|
||||||
this.props.dispatch(fetchGroups(this.props.activeTab));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(oldProps) {
|
|
||||||
if (this.props.activeTab && this.props.activeTab !== oldProps.activeTab) {
|
|
||||||
this.props.dispatch(fetchGroups(this.props.activeTab));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOpenProUpgradeModal = () => {
|
|
||||||
this.props.dispatch(openModal('PRO_UPGRADE'));
|
|
||||||
}
|
|
||||||
|
|
||||||
renderHeader() {
|
|
||||||
const { intl, activeTab, account, onOpenProUpgradeModal } = this.props;
|
|
||||||
|
|
||||||
const isPro = account.get('is_pro');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="group-column-header">
|
|
||||||
<div className="group-column-header__cta">
|
|
||||||
{
|
|
||||||
account && isPro &&
|
|
||||||
<Link to="/groups/create" className="button standard-small">{intl.formatMessage(messages.create)}</Link>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
account && !isPro &&
|
|
||||||
<button onClick={this.handleOpenProUpgradeModal} className="button standard-small">{intl.formatMessage(messages.create)}</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className="group-column-header__title">{intl.formatMessage(messages.heading)}</div>
|
|
||||||
|
|
||||||
<div className="column-header__wrapper">
|
|
||||||
<h1 className="column-header">
|
|
||||||
<Link to='/groups' className={classNames('btn grouped', {'active': 'featured' === activeTab})}>
|
|
||||||
{intl.formatMessage(messages.tab_featured)}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link to='/groups/browse/member' className={classNames('btn grouped', {'active': 'member' === activeTab})}>
|
|
||||||
{intl.formatMessage(messages.tab_member)}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link to='/groups/browse/admin' className={classNames('btn grouped', {'active': 'admin' === activeTab})}>
|
|
||||||
{intl.formatMessage(messages.tab_admin)}
|
|
||||||
</Link>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { groupIds, showCreateForm } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{!showCreateForm && this.renderHeader()}
|
|
||||||
{showCreateForm && <GroupCreate /> }
|
|
||||||
|
|
||||||
<div className="group-card-list">
|
|
||||||
{groupIds.map(id => <GroupCard key={id} id={id} />)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import { debounce } from 'lodash';
|
|
||||||
import {
|
|
||||||
fetchMembers,
|
|
||||||
expandMembers,
|
|
||||||
updateRole,
|
|
||||||
createRemovedAccount,
|
|
||||||
} from '../../../actions/groups';
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
import AccountContainer from '../../../containers/account_container';
|
|
||||||
import ScrollableList from '../../../components/scrollable_list';
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { params: { id } }) => ({
|
|
||||||
group: state.getIn(['groups', id]),
|
|
||||||
relationships: state.getIn(['group_relationships', id]),
|
|
||||||
accountIds: state.getIn(['user_lists', 'groups', id, 'items']),
|
|
||||||
hasMore: !!state.getIn(['user_lists', 'groups', id, 'next']),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default
|
|
||||||
@connect(mapStateToProps)
|
|
||||||
class GroupMembers extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
params: PropTypes.object.isRequired,
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
accountIds: ImmutablePropTypes.list,
|
|
||||||
hasMore: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentWillMount () {
|
|
||||||
const { params: { id } } = this.props;
|
|
||||||
|
|
||||||
this.props.dispatch(fetchMembers(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
|
||||||
if (nextProps.params.id !== this.props.params.id) {
|
|
||||||
this.props.dispatch(fetchMembers(nextProps.params.id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleLoadMore = debounce(() => {
|
|
||||||
this.props.dispatch(expandMembers(this.props.params.id));
|
|
||||||
}, 300, { leading: true });
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { accountIds, hasMore, group, relationships, dispatch } = this.props;
|
|
||||||
|
|
||||||
if (!group || !accountIds || !relationships) {
|
|
||||||
return <LoadingIndicator />
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ScrollableList
|
|
||||||
scrollKey='members'
|
|
||||||
hasMore={hasMore}
|
|
||||||
onLoadMore={this.handleLoadMore}
|
|
||||||
emptyMessage={<FormattedMessage id='group.members.empty' defaultMessage='This group does not has any members.' />}
|
|
||||||
>
|
|
||||||
{accountIds.map(id => {
|
|
||||||
let menu = [];
|
|
||||||
|
|
||||||
if (relationships.get('admin')) {
|
|
||||||
menu = [
|
|
||||||
{ text: 'Remove from group', action: () => dispatch(createRemovedAccount(group.get('id'), id)) },
|
|
||||||
{ text: 'Make administrator', action: () => dispatch(updateRole(group.get('id'), id, 'admin')) },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="group-account-wrapper" key={id}>
|
|
||||||
<AccountContainer id={id} withNote={false} actionIcon="none" onActionClick={() => true} />
|
|
||||||
{menu.length > 0 && <DropdownMenuContainer items={menu} icon='ellipsis-h' size={18} direction='right' />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ScrollableList>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
|
||||||
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 ColumnSettingsContainer from './containers/column_settings_container'
|
|
||||||
import Icon from '../../../components/icon'
|
|
||||||
import ColumnIndicator from '../../../components/column_indicator'
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
tabLatest: { id: 'group.timeline.tab_latest', defaultMessage: 'Latest' },
|
|
||||||
show: { id: 'group.timeline.show_settings', defaultMessage: 'Show settings' },
|
|
||||||
hide: { id: 'group.timeline.hide_settings', defaultMessage: 'Hide settings' },
|
|
||||||
empty: { id: 'empty_column.group', defaultMessage: 'There is nothing in this group yet. When members of this group post new statuses, they will appear here.' },
|
|
||||||
})
|
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
|
||||||
group: state.getIn(['groups', props.params.id]),
|
|
||||||
relationships: state.getIn(['group_relationships', props.params.id]),
|
|
||||||
hasUnread: state.getIn(['timelines', `group:${props.params.id}`, 'unread']) > 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
export default
|
|
||||||
@connect(mapStateToProps)
|
|
||||||
@injectIntl
|
|
||||||
class GroupTimeline extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
router: PropTypes.object,
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
params: PropTypes.object.isRequired,
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
columnId: PropTypes.string,
|
|
||||||
hasUnread: PropTypes.bool,
|
|
||||||
group: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
|
||||||
relationships: ImmutablePropTypes.map,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
|
||||||
collapsed: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { dispatch } = this.props
|
|
||||||
const { id } = this.props.params
|
|
||||||
|
|
||||||
dispatch(expandGroupTimeline(id))
|
|
||||||
|
|
||||||
this.disconnect = dispatch(connectGroupStream(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.disconnect) {
|
|
||||||
this.disconnect()
|
|
||||||
this.disconnect = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleLoadMore = maxId => {
|
|
||||||
const { id } = this.props.params
|
|
||||||
this.props.dispatch(expandGroupTimeline(id, { maxId }))
|
|
||||||
}
|
|
||||||
|
|
||||||
handleToggleClick = (e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
this.setState({ collapsed: !this.state.collapsed })
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { columnId, group, relationships, account, intl } = this.props
|
|
||||||
const { collapsed } = this.state
|
|
||||||
const { id } = this.props.params
|
|
||||||
|
|
||||||
if (typeof group === 'undefined' || !relationships) {
|
|
||||||
return (<ColumnIndicator type='loading' />)
|
|
||||||
} else if (group === false) {
|
|
||||||
return (<ColumnIndicator type='missing' />)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StatusListContainer
|
|
||||||
alwaysPrepend
|
|
||||||
scrollKey={`group_timeline-${columnId}`}
|
|
||||||
timelineId={`group:${id}`}
|
|
||||||
onLoadMore={this.handleLoadMore}
|
|
||||||
group={group}
|
|
||||||
withGroupAdmin={relationships && relationships.get('admin')}
|
|
||||||
emptyMessage={intl.formatMessage(messages.empty)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1 +1 @@
|
|||||||
export { default } from './groups_collection';
|
export { default } from './groups_collection'
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './hashtag_timeline';
|
export { default } from './hashtag_timeline'
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user