This commit is contained in:
mgabdev 2020-04-28 01:33:58 -04:00
parent 763694b5ab
commit c3d0d8bde2
87 changed files with 1392 additions and 826 deletions

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::BaseController < ApplicationController class Api::BaseController < ApplicationController
DEFAULT_STATUSES_LIMIT = 20 DEFAULT_STATUSES_LIMIT = 10
DEFAULT_ACCOUNTS_LIMIT = 40 DEFAULT_ACCOUNTS_LIMIT = 40
include RateLimitHeaders include RateLimitHeaders

View File

@ -1,74 +1,83 @@
import api from '../api'; import api from '../api'
import { me } from '../initial_state'; import { me } from '../initial_state'
export const GROUP_CREATE_REQUEST = 'GROUP_CREATE_REQUEST'; export const GROUP_CREATE_REQUEST = 'GROUP_CREATE_REQUEST'
export const GROUP_CREATE_SUCCESS = 'GROUP_CREATE_SUCCESS'; export const GROUP_CREATE_SUCCESS = 'GROUP_CREATE_SUCCESS'
export const GROUP_CREATE_FAIL = 'GROUP_CREATE_FAIL'; export const GROUP_CREATE_FAIL = 'GROUP_CREATE_FAIL'
export const GROUP_UPDATE_REQUEST = 'GROUP_UPDATE_REQUEST'; export const GROUP_UPDATE_REQUEST = 'GROUP_UPDATE_REQUEST'
export const GROUP_UPDATE_SUCCESS = 'GROUP_UPDATE_SUCCESS'; export const GROUP_UPDATE_SUCCESS = 'GROUP_UPDATE_SUCCESS'
export const GROUP_UPDATE_FAIL = 'GROUP_UPDATE_FAIL'; export const GROUP_UPDATE_FAIL = 'GROUP_UPDATE_FAIL'
export const GROUP_EDITOR_VALUE_CHANGE = 'GROUP_EDITOR_VALUE_CHANGE'; export const GROUP_EDITOR_TITLE_CHANGE = 'GROUP_EDITOR_TITLE_CHANGE'
export const GROUP_EDITOR_RESET = 'GROUP_EDITOR_RESET'; export const GROUP_EDITOR_DESCRIPTION_CHANGE = 'GROUP_EDITOR_DESCRIPTION_CHANGE'
export const GROUP_EDITOR_SETUP = 'GROUP_EDITOR_SETUP'; export const GROUP_EDITOR_COVER_IMAGE_CHANGE = 'GROUP_EDITOR_COVER_IMAGE_CHANGE'
export const GROUP_EDITOR_RESET = 'GROUP_EDITOR_RESET'
export const GROUP_EDITOR_SETUP = 'GROUP_EDITOR_SETUP'
export const submit = (routerHistory) => (dispatch, getState) => { export const submit = (routerHistory) => (dispatch, getState) => {
const groupId = getState().getIn(['group_editor', 'groupId']); if (!me) return
const title = getState().getIn(['group_editor', 'title']);
const description = getState().getIn(['group_editor', 'description']); const groupId = getState().getIn(['group_editor', 'groupId'])
const coverImage = getState().getIn(['group_editor', 'coverImage']); const title = getState().getIn(['group_editor', 'title'])
const description = getState().getIn(['group_editor', 'description'])
const coverImage = getState().getIn(['group_editor', 'coverImage'])
if (groupId === null) { if (groupId === null) {
dispatch(create(title, description, coverImage, routerHistory)); dispatch(create(title, description, coverImage, routerHistory))
} else { } else {
dispatch(update(groupId, title, description, coverImage, routerHistory)); dispatch(update(groupId, title, description, coverImage, routerHistory))
} }
}; };
export const create = (title, description, coverImage, routerHistory) => (dispatch, getState) => { const create = (title, description, coverImage, routerHistory) => (dispatch, getState) => {
if (!me) return; if (!me) return
dispatch(createRequest()); dispatch(createRequest())
const formData = new FormData(); const formData = new FormData()
formData.append('title', title); formData.append('title', title)
formData.append('description', description); formData.append('description', description)
if (coverImage !== null) { if (coverImage !== null) {
formData.append('cover_image', coverImage); formData.append('cover_image', coverImage)
} }
api(getState).post('/api/v1/groups', formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(({ data }) => { api(getState).post('/api/v1/groups', formData, {
dispatch(createSuccess(data)); headers: {
routerHistory.push(`/groups/${data.id}`); 'Content-Type': 'multipart/form-data'
}).catch(err => dispatch(createFail(err))); }
}; }).then(({ data }) => {
dispatch(createSuccess(data))
routerHistory.push(`/groups/${data.id}`)
}).catch(err => dispatch(createFail(err)))
};
export const createRequest = id => ({ export const createRequest = (id) => ({
type: GROUP_CREATE_REQUEST, type: GROUP_CREATE_REQUEST,
id, id,
}); })
export const createSuccess = group => ({ export const createSuccess = (group) => ({
type: GROUP_CREATE_SUCCESS, type: GROUP_CREATE_SUCCESS,
group, group,
}); })
export const createFail = error => ({ export const createFail = (error) => ({
type: GROUP_CREATE_FAIL, type: GROUP_CREATE_FAIL,
error, error,
}); })
export const update = (groupId, title, description, coverImage, routerHistory) => (dispatch, getState) => { const update = (groupId, title, description, coverImage, routerHistory) => (dispatch, getState) => {
if (!me) return; if (!me) return
dispatch(updateRequest()); dispatch(updateRequest())
const formData = new FormData(); const formData = new FormData()
formData.append('title', title); formData.append('title', title)
formData.append('description', description); formData.append('description', description);
if (coverImage !== null) { if (coverImage !== null) {
@ -79,35 +88,44 @@ export const update = (groupId, title, description, coverImage, routerHistory) =
dispatch(updateSuccess(data)); dispatch(updateSuccess(data));
routerHistory.push(`/groups/${data.id}`); routerHistory.push(`/groups/${data.id}`);
}).catch(err => dispatch(updateFail(err))); }).catch(err => dispatch(updateFail(err)));
}; };
export const updateRequest = id => ({ export const updateRequest = (id) => ({
type: GROUP_UPDATE_REQUEST, type: GROUP_UPDATE_REQUEST,
id, id,
}); });
export const updateSuccess = group => ({ export const updateSuccess = (group) => ({
type: GROUP_UPDATE_SUCCESS, type: GROUP_UPDATE_SUCCESS,
group, group,
}); });
export const updateFail = error => ({ export const updateFail = (error) => ({
type: GROUP_UPDATE_FAIL, type: GROUP_UPDATE_FAIL,
error, error,
}); })
export const changeValue = (field, value) => ({ export const resetEditor = () => ({
type: GROUP_EDITOR_VALUE_CHANGE,
field,
value,
});
export const reset = () => ({
type: GROUP_EDITOR_RESET type: GROUP_EDITOR_RESET
}); });
export const setUp = (group) => ({ export const setGroup = (group) => ({
type: GROUP_EDITOR_SETUP, type: GROUP_EDITOR_SETUP,
group, group,
}); });
export const changeGroupTitle = (title) => ({
type: GROUP_EDITOR_TITLE_CHANGE,
title,
})
export const changeGroupDescription = (description) => ({
type: GROUP_EDITOR_DESCRIPTION_CHANGE,
description,
})
export const changeGroupCoverImage = (imageData) => ({
type: GROUP_EDITOR_COVER_IMAGE_CHANGE,
value: imageData,
})

View File

@ -30,6 +30,10 @@ export const UNPIN_REQUEST = 'UNPIN_REQUEST';
export const UNPIN_SUCCESS = 'UNPIN_SUCCESS'; export const UNPIN_SUCCESS = 'UNPIN_SUCCESS';
export const UNPIN_FAIL = 'UNPIN_FAIL'; export const UNPIN_FAIL = 'UNPIN_FAIL';
export const LIKES_FETCH_REQUEST = 'LIKES_FETCH_REQUEST';
export const LIKES_FETCH_SUCCESS = 'LIKES_FETCH_SUCCESS';
export const LIKES_FETCH_FAIL = 'LIKES_FETCH_FAIL';
export function repost(status) { export function repost(status) {
return function (dispatch, getState) { return function (dispatch, getState) {
if (!me) return; if (!me) return;
@ -308,3 +312,38 @@ export function unpinFail(status, error) {
skipLoading: true, skipLoading: true,
}; };
}; };
export function fetchLikes(id) {
return (dispatch, getState) => {
dispatch(fetchLikesRequest(id));
api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => {
dispatch(importFetchedAccounts(response.data));
dispatch(fetchLikesSuccess(id, response.data));
}).catch(error => {
dispatch(fetchLikesFail(id, error));
});
};
};
export function fetchLikesRequest(id) {
return {
type: LIKES_FETCH_REQUEST,
id,
};
};
export function fetchLikesSuccess(id, accounts) {
return {
type: LIKES_FETCH_SUCCESS,
id,
accounts,
};
};
export function fetchLikesFail(id, error) {
return {
type: LIKES_FETCH_FAIL,
error,
};
};

View File

@ -98,20 +98,22 @@ export const fetchListsRequest = () => ({
type: LISTS_FETCH_REQUEST, type: LISTS_FETCH_REQUEST,
}); });
export const fetchListsSuccess = lists => ({ export const fetchListsSuccess = (lists) => ({
type: LISTS_FETCH_SUCCESS, type: LISTS_FETCH_SUCCESS,
lists, lists,
}); });
export const fetchListsFail = error => ({ export const fetchListsFail = (error) => ({
type: LISTS_FETCH_FAIL, type: LISTS_FETCH_FAIL,
error, error,
}); });
export const submitListEditor = shouldReset => (dispatch, getState) => { export const submitListEditor = (shouldReset) => (dispatch, getState) => {
const listId = getState().getIn(['listEditor', 'listId']); const listId = getState().getIn(['listEditor', 'listId']);
const title = getState().getIn(['listEditor', 'title']); const title = getState().getIn(['listEditor', 'title']);
console.log("submitListEditor:", title)
if (listId === null) { if (listId === null) {
dispatch(createList(title, shouldReset)); dispatch(createList(title, shouldReset));
} else { } else {
@ -119,7 +121,7 @@ export const submitListEditor = shouldReset => (dispatch, getState) => {
} }
}; };
export const setupListEditor = listId => (dispatch, getState) => { export const setupListEditor = (listId) => (dispatch, getState) => {
dispatch({ dispatch({
type: LIST_EDITOR_SETUP, type: LIST_EDITOR_SETUP,
list: getState().getIn(['lists', listId]), list: getState().getIn(['lists', listId]),
@ -128,7 +130,7 @@ export const setupListEditor = listId => (dispatch, getState) => {
dispatch(fetchListAccounts(listId)); dispatch(fetchListAccounts(listId));
}; };
export const changeListEditorTitle = value => ({ export const changeListEditorTitle = (value) => ({
type: LIST_EDITOR_TITLE_CHANGE, type: LIST_EDITOR_TITLE_CHANGE,
value, value,
}); });
@ -151,12 +153,12 @@ export const createListRequest = () => ({
type: LIST_CREATE_REQUEST, type: LIST_CREATE_REQUEST,
}); });
export const createListSuccess = list => ({ export const createListSuccess = (list) => ({
type: LIST_CREATE_SUCCESS, type: LIST_CREATE_SUCCESS,
list, list,
}); });
export const createListFail = error => ({ export const createListFail = (error) => ({
type: LIST_CREATE_FAIL, type: LIST_CREATE_FAIL,
error, error,
}); });
@ -195,7 +197,7 @@ export const resetListEditor = () => ({
type: LIST_EDITOR_RESET, type: LIST_EDITOR_RESET,
}); });
export const deleteList = id => (dispatch, getState) => { export const deleteList = (id) => (dispatch, getState) => {
if (!me) return; if (!me) return;
dispatch(deleteListRequest(id)); dispatch(deleteListRequest(id));
@ -205,12 +207,12 @@ export const deleteList = id => (dispatch, getState) => {
.catch(err => dispatch(deleteListFail(id, err))); .catch(err => dispatch(deleteListFail(id, err)));
}; };
export const deleteListRequest = id => ({ export const deleteListRequest = (id) => ({
type: LIST_DELETE_REQUEST, type: LIST_DELETE_REQUEST,
id, id,
}); });
export const deleteListSuccess = id => ({ export const deleteListSuccess = (id) => ({
type: LIST_DELETE_SUCCESS, type: LIST_DELETE_SUCCESS,
id, id,
}); });
@ -221,7 +223,7 @@ export const deleteListFail = (id, error) => ({
error, error,
}); });
export const fetchListAccounts = listId => (dispatch, getState) => { export const fetchListAccounts = (listId) => (dispatch, getState) => {
if (!me) return; if (!me) return;
dispatch(fetchListAccountsRequest(listId)); dispatch(fetchListAccountsRequest(listId));
@ -232,7 +234,7 @@ export const fetchListAccounts = listId => (dispatch, getState) => {
}).catch(err => dispatch(fetchListAccountsFail(listId, err))); }).catch(err => dispatch(fetchListAccountsFail(listId, err)));
}; };
export const fetchListAccountsRequest = id => ({ export const fetchListAccountsRequest = (id) => ({
type: LIST_ACCOUNTS_FETCH_REQUEST, type: LIST_ACCOUNTS_FETCH_REQUEST,
id, id,
}); });
@ -250,7 +252,7 @@ export const fetchListAccountsFail = (id, error) => ({
error, error,
}); });
export const fetchListSuggestions = q => (dispatch, getState) => { export const fetchListSuggestions = (q) => (dispatch, getState) => {
if (!me) return; if (!me) return;
const params = { const params = {
@ -277,7 +279,7 @@ export const clearListSuggestions = () => ({
type: LIST_EDITOR_SUGGESTIONS_CLEAR, type: LIST_EDITOR_SUGGESTIONS_CLEAR,
}); });
export const changeListSuggestions = value => ({ export const changeListSuggestions = (value) => ({
type: LIST_EDITOR_SUGGESTIONS_CHANGE, type: LIST_EDITOR_SUGGESTIONS_CHANGE,
value, value,
}); });
@ -361,7 +363,7 @@ export const setupListAdder = accountId => (dispatch, getState) => {
dispatch(fetchAccountLists(accountId)); dispatch(fetchAccountLists(accountId));
}; };
export const fetchAccountLists = accountId => (dispatch, getState) => { export const fetchAccountLists = (accountId) => (dispatch, getState) => {
if (!me) return; if (!me) return;
dispatch(fetchAccountListsRequest(accountId)); dispatch(fetchAccountListsRequest(accountId));
@ -371,7 +373,7 @@ export const fetchAccountLists = accountId => (dispatch, getState) => {
.catch(err => dispatch(fetchAccountListsFail(accountId, err))); .catch(err => dispatch(fetchAccountListsFail(accountId, err)));
}; };
export const fetchAccountListsRequest = id => ({ export const fetchAccountListsRequest = (id) => ({
type:LIST_ADDER_LISTS_FETCH_REQUEST, type:LIST_ADDER_LISTS_FETCH_REQUEST,
id, id,
}); });
@ -388,10 +390,10 @@ export const fetchAccountListsFail = (id, err) => ({
err, err,
}); });
export const addToListAdder = listId => (dispatch, getState) => { export const addToListAdder = (listId) => (dispatch, getState) => {
dispatch(addToList(listId, getState().getIn(['listAdder', 'accountId']))); dispatch(addToList(listId, getState().getIn(['listAdder', 'accountId'])));
}; };
export const removeFromListAdder = listId => (dispatch, getState) => { export const removeFromListAdder = (listId) => (dispatch, getState) => {
dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId']))); dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])));
}; };

View File

@ -1,16 +1,16 @@
export const MODAL_OPEN = 'MODAL_OPEN'; export const MODAL_OPEN = 'MODAL_OPEN'
export const MODAL_CLOSE = 'MODAL_CLOSE'; export const MODAL_CLOSE = 'MODAL_CLOSE'
export function openModal(type, props) { export function openModal(type, props) {
return { return {
type: MODAL_OPEN, type: MODAL_OPEN,
modalType: type, modalType: type,
modalProps: props, modalProps: props,
}; }
}; }
export function closeModal() { export function closeModal() {
return { return {
type: MODAL_CLOSE, type: MODAL_CLOSE,
}; }
}; }

View File

@ -0,0 +1,24 @@
const CogIcon = ({
className = '',
size = '16px',
title = '',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={size}
height={size}
viewBox='0 0 24 24'
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d='M 23.40625 9.769531 L 21.261719 9.511719 C 21.042969 8.695312 20.722656 7.921875 20.3125 7.207031 L 21.640625 5.511719 C 21.847656 5.253906 21.824219 4.851562 21.585938 4.617188 L 19.378906 2.410156 C 19.148438 2.175781 18.746094 2.152344 18.484375 2.355469 L 16.789062 3.6875 C 16.074219 3.273438 15.304688 2.953125 14.488281 2.738281 L 14.230469 0.59375 C 14.191406 0.269531 13.890625 0 13.558594 0 L 10.4375 0 C 10.109375 0 9.804688 0.269531 9.765625 0.59375 L 9.507812 2.738281 C 8.695312 2.953125 7.921875 3.277344 7.207031 3.6875 L 5.511719 2.355469 C 5.253906 2.152344 4.851562 2.175781 4.617188 2.410156 L 2.410156 4.621094 C 2.175781 4.851562 2.152344 5.253906 2.359375 5.515625 L 3.6875 7.210938 C 3.277344 7.925781 2.953125 8.695312 2.738281 9.511719 L 0.597656 9.769531 C 0.269531 9.808594 0 10.109375 0 10.441406 L 0 13.5625 C 0 13.894531 0.269531 14.195312 0.597656 14.234375 L 2.738281 14.492188 C 2.957031 15.304688 3.277344 16.078125 3.6875 16.792969 L 2.359375 18.488281 C 2.15625 18.746094 2.179688 19.148438 2.414062 19.382812 L 4.617188 21.59375 C 4.851562 21.824219 5.253906 21.851562 5.511719 21.648438 L 7.207031 20.3125 C 7.921875 20.726562 8.695312 21.046875 9.511719 21.265625 L 9.769531 23.40625 C 9.808594 23.734375 10.109375 24 10.4375 24 L 13.5625 24 C 13.890625 24 14.195312 23.734375 14.230469 23.40625 L 14.488281 21.265625 C 15.304688 21.046875 16.078125 20.726562 16.792969 20.3125 L 18.488281 21.644531 C 18.746094 21.847656 19.148438 21.824219 19.382812 21.589844 L 21.589844 19.382812 C 21.824219 19.148438 21.847656 18.746094 21.644531 18.484375 L 20.3125 16.792969 C 20.722656 16.078125 21.042969 15.304688 21.261719 14.492188 L 23.402344 14.234375 C 23.730469 14.195312 24 13.894531 24 13.5625 L 24 10.441406 C 24 10.109375 23.734375 9.808594 23.40625 9.769531 Z M 12 18 C 8.6875 18 6 15.3125 6 12 C 6 8.6875 8.6875 6 12 6 C 15.316406 6 18 8.6875 18 12 C 18 15.3125 15.316406 18 12 18 Z M 12 18' />
</g>
</svg>
)
export default CogIcon

View File

@ -1,5 +1,5 @@
const ErrorIcon = ({ const ErrorIcon = ({
className = '', className = _s.fillColorPrimary,
size = '32px', size = '32px',
title = 'Error', title = 'Error',
}) => ( }) => (
@ -16,7 +16,10 @@ const ErrorIcon = ({
aria-label={title} aria-label={title}
> >
<g> <g>
<path d='M 54.67 9.37 C 42.18 -3.12 21.86 -3.12 9.37 9.37 C -3.13 21.86 -3.12 42.18 9.37 54.67 C 21.86 67.16 42.18 67.16 54.67 54.67 C 67.16 42.18 67.16 21.86 54.67 9.37 Z M 51.18 51.18 C 40.61 61.74 23.43 61.74 12.86 51.18 C 2.3 40.61 2.3 23.42 12.86 12.86 C 23.43 2.3 40.61 2.3 51.18 12.86 C 61.74 23.43 61.74 40.61 51.18 51.18 Z M 46.5 44.68 C 46.9 45.6 46.48 46.66 45.56 47 C 44.64 47.46 43.57 47 43.18 46.12 C 41.43 42 37.3 39.47 32.66 39.47 C 27.91 39.47 23.75 42 22 46.11 C 21.79 46.81 21.11 47.23 20.41 47.23 C 20.18 47.23 19.94 47.18 19.71 47 C 18.79 46.71 18.35 45.65 18.73 44.72 C 20.97 39.33 26.44 35.85 32.66 35.85 C 38.75 35.85 44.18 39.31 46.5 44.68 Z M 20 23.35 C 20 21.28 21.75 19.61 23.81 19.61 C 25.88 19.61 27.56 21.28 27.56 23.35 C 27.56 25.42 25.88 27 23.81 27 C 21.75 27 20 25.42 20 23.35 Z M 37 23.35 C 37 21.28 38.72 19.61 40.79 19.61 C 42.86 19.61 44.54 21.28 44.54 23.35 C 44.54 25.42 42.86 27 40.79 27 C 38.72 27 37 25.42 37 23.35 Z M 37 23.35' /> <path d='M 32 0 C 14.355469 0 0 14.355469 0 32 C 0 49.644531 14.355469 64 32 64 C 49.644531 64 64 49.644531 64 32 C 64 14.355469 49.644531 0 32 0 Z M 32 61.332031 C 15.824219 61.332031 2.667969 48.175781 2.667969 32 C 2.667969 15.824219 15.824219 2.667969 32 2.667969 C 48.175781 2.667969 61.332031 15.824219 61.332031 32 C 61.332031 48.175781 48.175781 61.332031 32 61.332031 Z M 32 61.332031' />
<path d='M 24 24 C 24 25.472656 22.804688 26.667969 21.332031 26.667969 C 19.859375 26.667969 18.667969 25.472656 18.667969 24 C 18.667969 22.527344 19.859375 21.332031 21.332031 21.332031 C 22.804688 21.332031 24 22.527344 24 24 Z M 24 24' />
<path d='M 45.332031 24 C 45.332031 25.472656 44.140625 26.667969 42.667969 26.667969 C 41.195312 26.667969 40 25.472656 40 24 C 40 22.527344 41.195312 21.332031 42.667969 21.332031 C 44.140625 21.332031 45.332031 22.527344 45.332031 24 Z M 45.332031 24' />
<path d='M 32 40 L 31.996094 40 C 20.792969 40 16.445312 45.628906 16.265625 45.867188 C 15.828125 46.453125 15.945312 47.28125 16.527344 47.722656 C 17.113281 48.167969 17.949219 48.054688 18.394531 47.472656 C 18.546875 47.277344 22.21875 42.667969 31.996094 42.667969 L 32 42.667969 C 41.777344 42.667969 45.453125 47.277344 45.601562 47.46875 C 45.863281 47.816406 46.261719 48 46.667969 48 C 46.945312 48 47.226562 47.914062 47.46875 47.734375 C 48.054688 47.292969 48.175781 46.457031 47.734375 45.867188 C 47.554688 45.628906 43.203125 40 32 40 Z M 32 40' />
</g> </g>
</svg> </svg>
) )

View File

@ -165,7 +165,7 @@ class Account extends ImmutablePureComponent {
<NavLink <NavLink
title={account.get('acct')} title={account.get('acct')}
to={`/${account.get('acct')}`} to={`/${account.get('acct')}`}
className={[_s.default, _s.alignItemsStart, _s.noUnderline, _s.px10, _s.flexGrow1].join(' ')} className={[_s.default, _s.alignItemsStart, _s.noUnderline, _s.px10, _s.overflowHidden, _s.flexNormal].join(' ')}
> >
<DisplayName account={account} isMultiline={compact} /> <DisplayName account={account} isMultiline={compact} />
{!compact && actionButton} {!compact && actionButton}

View File

@ -164,13 +164,13 @@ export default class Button extends PureComponent {
underline_onHover: underlineOnHover, underline_onHover: underlineOnHover,
backgroundColorSubtle2Dark_onHover: backgroundColor === COLORS.tertiary || backgroundColor === COLORS.secondary, backgroundColorSubtle2Dark_onHover: backgroundColor === COLORS.tertiary || backgroundColor === COLORS.secondary && !isDisabled,
backgroundColorBlackOpaque_onHover: backgroundColor === COLORS.black, backgroundColorBlackOpaque_onHover: backgroundColor === COLORS.black && !isDisabled,
backgroundColorBrandDark_onHover: backgroundColor === COLORS.brand, backgroundColorBrandDark_onHover: backgroundColor === COLORS.brand && !isDisabled,
backgroundColorDangerDark_onHover: backgroundColor === COLORS.danger, backgroundColorDangerDark_onHover: backgroundColor === COLORS.danger && !isDisabled,
backgroundColorBrand_onHover: color === COLORS.brand && isOutline, backgroundColorBrand_onHover: color === COLORS.brand && isOutline && !isDisabled,
colorWhite_onHover: !!children && color === COLORS.brand && isOutline, colorWhite_onHover: !!children && color === COLORS.brand && isOutline && !isDisabled,
fillColorSecondary: !!icon && color === COLORS.secondary, fillColorSecondary: !!icon && color === COLORS.secondary,
fillColorWhite: !!icon && color === COLORS.white, fillColorWhite: !!icon && color === COLORS.white,

View File

@ -27,7 +27,7 @@ class ColumnIndicator extends PureComponent {
render() { render() {
const { type, message, intl } = this.props const { type, message, intl } = this.props
const title = type !== 'error' ? intl.formatMessage(messages[type]) : message const title = type !== 'error' && !message ? intl.formatMessage(messages[type]) : message
return ( return (
<div className={[_s.default, _s.width100PC, _s.justifyContentCenter, _s.alignItemsCenter, _s.py15].join(' ')}> <div className={[_s.default, _s.width100PC, _s.justifyContentCenter, _s.alignItemsCenter, _s.py15].join(' ')}>

View File

@ -60,7 +60,7 @@ class Comment extends ImmutablePureComponent {
<Avatar account={status.get('account')} size={32} /> <Avatar account={status.get('account')} size={32} />
</NavLink> </NavLink>
<div className={[_s.default, _s.flexNormal].join(' ')}> <div className={_s.default}>
<div className={[_s.default, _s.px10, _s.pt5, _s.pb10, _s.radiusSmall, _s.backgroundColorSubtle].join(' ')}> <div className={[_s.default, _s.px10, _s.pt5, _s.pb10, _s.radiusSmall, _s.backgroundColorSubtle].join(' ')}>
<CommentHeader status={status} /> <CommentHeader status={status} />
<StatusContent <StatusContent

View File

@ -161,7 +161,7 @@ class DisplayName extends ImmutablePureComponent {
/> />
{ {
!noRelationship && account.get('locked') && !noRelationship && account.get('locked') &&
<Icon id='lock-filled' size={iconSize} className={_s.ml5} /> <Icon id='lock-filled' size={iconSize} className={[_s.fillColorPrimary, _s.ml5].join(' ')} />
} }
</bdi> </bdi>
{ {

View File

@ -31,8 +31,6 @@ class FloatingActionButton extends PureComponent {
return ( return (
<Button <Button
onClick={onOpenCompose} onClick={onOpenCompose}
color='white'
backgroundColor='brand'
className={[_s.posFixed, _s.z4, _s.py15, _s.mb15, _s.mr15, _s.bottom0, _s.right0].join(' ')} className={[_s.posFixed, _s.z4, _s.py15, _s.mb15, _s.mr15, _s.bottom0, _s.right0].join(' ')}
title={message} title={message}
aria-label={message} aria-label={message}

View File

@ -0,0 +1,37 @@
import Text from './text'
export default class Form extends PureComponent {
static propTypes = {
children: PropTypes.any,
errorMessage: PropTypes.string,
onSubmit: PropTypes.func.isRequired,
}
componentDidUpdate (prevProps) {
}
render() {
const {
children,
errorMessage,
onSubmit,
} = this.props
return (
<form onSubmit={onSubmit} className={_s.default}>
{
!!errorMessage &&
<Text color='danger' className={_s.my10}>
{errorMessage}
</Text>
}
<div className={_s.default}>
{children}
</div>
</form>
)
}
}

View File

@ -4,6 +4,7 @@ import { Fragment } from 'react'
import { NavLink } from 'react-router-dom' import { NavLink } from 'react-router-dom'
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import { PLACEHOLDER_MISSING_HEADER_SRC } from '../constants'
import { shortNumberFormat } from '../utils/numbers' import { shortNumberFormat } from '../utils/numbers'
import Button from './button' import Button from './button'
import DotTextSeperator from './dot_text_seperator' import DotTextSeperator from './dot_text_seperator'
@ -34,10 +35,16 @@ class GroupCollectionItem extends ImmutablePureComponent {
static propTypes = { static propTypes = {
group: ImmutablePropTypes.map, group: ImmutablePropTypes.map,
relationships: ImmutablePropTypes.map, relationships: ImmutablePropTypes.map,
isHidden: PropTypes.bool,
} }
render() { render() {
const { intl, group, relationships } = this.props const {
intl,
group,
relationships,
isHidden,
} = this.props
if (!relationships) return null if (!relationships) return null
@ -53,7 +60,20 @@ class GroupCollectionItem extends ImmutablePureComponent {
const isMember = relationships.get('member') const isMember = relationships.get('member')
const isAdmin = relationships.get('admin') const isAdmin = relationships.get('admin')
const coverSrc = group.get('cover') const coverSrc = group.get('cover_image_url') || ''
const coverMissing = coverSrc.indexOf(PLACEHOLDER_MISSING_HEADER_SRC) > -1 || !coverSrc
if (isHidden) {
return (
<Fragment>
{group.get('title')}
{subtitle}
{isMember && intl.formatMessage(messages.member)}
{isAdmin && intl.formatMessage(messages.admin)}
</Fragment>
)
}
const navLinkClasses = cx({ const navLinkClasses = cx({
default: 1, default: 1,
@ -77,7 +97,7 @@ class GroupCollectionItem extends ImmutablePureComponent {
className={navLinkClasses} className={navLinkClasses}
> >
{ {
!!coverSrc && !!coverSrc && !coverMissing &&
<Image <Image
src={coverSrc} src={coverSrc}
alt={group.get('title')} alt={group.get('title')}
@ -86,7 +106,7 @@ class GroupCollectionItem extends ImmutablePureComponent {
} }
{ {
!coverSrc && (isMember || isAdmin) && (!coverSrc || coverMissing) && (isMember || isAdmin) &&
<div className={[_s.default, _s.height40PX, _s.backgroundColorSubtle, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')} /> <div className={[_s.default, _s.height40PX, _s.backgroundColorSubtle, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')} />
} }

View File

@ -65,7 +65,7 @@ class GroupHeader extends ImmutablePureComponent {
}, },
] ]
const coverSrc = !!group ? group.get('cover') : undefined const coverSrc = !!group ? group.get('cover_image_url') : undefined
const title = !!group ? group.get('title') : undefined const title = !!group ? group.get('title') : undefined
return ( return (

View File

@ -4,6 +4,7 @@ import { Fragment } from 'react'
import { NavLink } from 'react-router-dom' import { NavLink } from 'react-router-dom'
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import { PLACEHOLDER_MISSING_HEADER_SRC } from '../constants'
import { shortNumberFormat } from '../utils/numbers' import { shortNumberFormat } from '../utils/numbers'
import Image from './image' import Image from './image'
import Text from './text' import Text from './text'
@ -31,6 +32,7 @@ class GroupListItem extends ImmutablePureComponent {
relationships: ImmutablePropTypes.map, relationships: ImmutablePropTypes.map,
slim: PropTypes.bool, slim: PropTypes.bool,
isLast: PropTypes.bool, isLast: PropTypes.bool,
isHidden: PropTypes.bool,
} }
static defaultProps = { static defaultProps = {
@ -39,9 +41,16 @@ class GroupListItem extends ImmutablePureComponent {
} }
render() { render() {
const { intl, group, relationships, slim, isLast } = this.props const {
intl,
group,
relationships,
slim,
isLast,
isHidden,
} = this.props
if (!relationships) return null if (!relationships || !group) return null
const unreadCount = relationships.get('unread_count') const unreadCount = relationships.get('unread_count')
@ -53,6 +62,15 @@ class GroupListItem extends ImmutablePureComponent {
</Fragment> </Fragment>
) : intl.formatMessage(messages.no_recent_activity) ) : intl.formatMessage(messages.no_recent_activity)
if (isHidden) {
return (
<Fragment>
{group.get('title')}
{subtitle}
</Fragment>
)
}
const containerClasses = cx({ const containerClasses = cx({
default: 1, default: 1,
noUnderline: 1, noUnderline: 1,
@ -80,10 +98,12 @@ class GroupListItem extends ImmutablePureComponent {
default: 1, default: 1,
px10: 1, px10: 1,
mt5: 1, mt5: 1,
flexShrink1: slim,
mb10: !slim, mb10: !slim,
}) })
const coverSrc = group.get('cover') const coverSrc = group.get('cover_image_url') || ''
const coverMissing = coverSrc.indexOf(PLACEHOLDER_MISSING_HEADER_SRC) > -1 || !coverSrc
return ( return (
<NavLink <NavLink
@ -92,7 +112,7 @@ class GroupListItem extends ImmutablePureComponent {
> >
{ {
(!!coverSrc || slim) && (!!coverSrc || slim) && !coverMissing &&
<Image <Image
src={coverSrc} src={coverSrc}
alt={group.get('title')} alt={group.get('title')}

View File

@ -11,6 +11,7 @@ import ChatIcon from '../assets/chat_icon'
import CircleIcon from '../assets/circle_icon' import CircleIcon from '../assets/circle_icon'
import CloseIcon from '../assets/close_icon' import CloseIcon from '../assets/close_icon'
import CodeIcon from '../assets/code_icon' import CodeIcon from '../assets/code_icon'
import CogIcon from '../assets/cog_icon'
import CommentIcon from '../assets/comment_icon' import CommentIcon from '../assets/comment_icon'
import CopyIcon from '../assets/copy_icon' import CopyIcon from '../assets/copy_icon'
import DissenterIcon from '../assets/dissenter_icon' import DissenterIcon from '../assets/dissenter_icon'
@ -80,6 +81,7 @@ const ICONS = {
'chat': ChatIcon, 'chat': ChatIcon,
'close': CloseIcon, 'close': CloseIcon,
'code': CodeIcon, 'code': CodeIcon,
'cog': CogIcon,
'comment': CommentIcon, 'comment': CommentIcon,
'copy': CopyIcon, 'copy': CopyIcon,
'dissenter': DissenterIcon, 'dissenter': DissenterIcon,

View File

@ -7,6 +7,7 @@ import Text from './text'
const cx = classNames.bind(_s) const cx = classNames.bind(_s)
export default class Input extends PureComponent { export default class Input extends PureComponent {
static propTypes = { static propTypes = {
placeholder: PropTypes.string, placeholder: PropTypes.string,
prependIcon: PropTypes.string, prependIcon: PropTypes.string,
@ -25,6 +26,10 @@ export default class Input extends PureComponent {
hideLabel: PropTypes.bool, hideLabel: PropTypes.bool,
} }
handleOnChange = (e) => {
this.props.onChange(e.target.value)
}
render() { render() {
const { const {
placeholder, placeholder,
@ -55,6 +60,7 @@ export default class Input extends PureComponent {
py5: small, py5: small,
backgroundTransparent: !readOnly, backgroundTransparent: !readOnly,
backgroundColorSubtle2: readOnly, backgroundColorSubtle2: readOnly,
colorPrimary: !readOnly,
colorSecondary: readOnly, colorSecondary: readOnly,
fontSize15PX: !small, fontSize15PX: !small,
fontSize13PX: small, fontSize13PX: small,
@ -101,7 +107,7 @@ export default class Input extends PureComponent {
placeholder={placeholder} placeholder={placeholder}
ref={inputRef} ref={inputRef}
value={value} value={value}
onChange={onChange} onChange={this.handleOnChange}
onKeyUp={onKeyUp} onKeyUp={onKeyUp}
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur} onBlur={onBlur}

View File

@ -1,3 +1,4 @@
import { Fragment } from 'react'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import Button from './button' import Button from './button'
import Icon from './icon' import Icon from './icon'
@ -9,6 +10,7 @@ export default class ListItem extends PureComponent {
static propTypes = { static propTypes = {
icon: PropTypes.string, icon: PropTypes.string,
isLast: PropTypes.bool, isLast: PropTypes.bool,
isHidden: PropTypes.bool,
to: PropTypes.string, to: PropTypes.string,
href: PropTypes.string, href: PropTypes.string,
title: PropTypes.string, title: PropTypes.string,
@ -29,8 +31,17 @@ export default class ListItem extends PureComponent {
size, size,
icon, icon,
hideArrow, hideArrow,
isHidden,
} = this.props } = this.props
if (isHidden) {
return (
<Fragment>
{title}
</Fragment>
)
}
const small = size === 'small' const small = size === 'small'
const large = size === 'large' const large = size === 'large'
@ -77,7 +88,7 @@ export default class ListItem extends PureComponent {
/> />
} }
<Text color='primary' size={textSize}> <Text color='primary' size={textSize} className={[_s.overflowHidden, _s.flexNormal, _s.pr5, _s.textOverflowEllipsis].join(' ')}>
{title} {title}
</Text> </Text>
@ -86,7 +97,7 @@ export default class ListItem extends PureComponent {
<Icon <Icon
id='angle-right' id='angle-right'
size='10px' size='10px'
className={[_s.mlAuto, _s.fillColorSecondary].join(' ')} className={[_s.mlAuto, _s.fillColorSecondary, _s.flexShrink1].join(' ')}
/> />
} }
</Button> </Button>

View File

@ -1,6 +1,5 @@
import { injectIntl, defineMessages } from 'react-intl' import { injectIntl, defineMessages } from 'react-intl'
import Button from './button' import Button from './button'
import Icon from './icon'
import Text from './text' import Text from './text'
const messages = defineMessages({ const messages = defineMessages({
@ -15,8 +14,6 @@ class LoadMore extends PureComponent {
onClick: PropTypes.func, onClick: PropTypes.func,
disabled: PropTypes.bool, disabled: PropTypes.bool,
visible: PropTypes.bool, visible: PropTypes.bool,
maxId: PropTypes.string,
gap: PropTypes.bool,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
} }
@ -24,13 +21,16 @@ class LoadMore extends PureComponent {
visible: true, visible: true,
} }
handleClick = () => { handleClick = (e) => {
const { gap, maxId } = this.props this.props.onClick()
this.props.onClick(gap ? maxId : undefined)
} }
render() { render() {
const { disabled, visible, gap, intl } = this.props const {
disabled,
visible,
intl,
} = this.props
return ( return (
<div className={[_s.default, _s.py10, _s.px10].join(' ')}> <div className={[_s.default, _s.py10, _s.px10].join(' ')}>
@ -40,22 +40,15 @@ class LoadMore extends PureComponent {
backgroundColor='tertiary' backgroundColor='tertiary'
color='primary' 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 &&
<Text color='inherit' align='center'> <Text color='inherit' align='center'>
{intl.formatMessage(messages.load_more)} {intl.formatMessage(messages.load_more)}
</Text> </Text>
}
{
gap &&
<Text align='center'>
<Icon id='ellipsis' size='14px' />
</Text>
}
</Button> </Button>
</div> </div>
) )

View File

@ -13,13 +13,14 @@ const cx = classNames.bind(_s)
export default class MediaItem extends ImmutablePureComponent { export default class MediaItem extends ImmutablePureComponent {
static propTypes = { static propTypes = {
account: ImmutablePropTypes.map.isRequired,
attachment: ImmutablePropTypes.map.isRequired, attachment: ImmutablePropTypes.map.isRequired,
small: PropTypes.bool isSmall: PropTypes.bool,
} }
state = { state = {
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
loaded: false, loaded: false,
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
} }
componentDidMount() { componentDidMount() {
@ -59,7 +60,11 @@ export default class MediaItem extends ImmutablePureComponent {
} }
render() { render() {
const { attachment, small } = this.props const {
account,
attachment,
isSmall,
} = this.props
const { visible, loaded } = this.state const { visible, loaded } = this.state
const status = attachment.get('status') const status = attachment.get('status')
@ -81,8 +86,8 @@ export default class MediaItem extends ImmutablePureComponent {
top0: 1, top0: 1,
height100PC: 1, height100PC: 1,
width100PC: 1, width100PC: 1,
py5: !small, py2: !isSmall,
px5: !small, px2: !isSmall,
}) })
const linkClasses = cx({ const linkClasses = cx({
@ -91,15 +96,16 @@ export default class MediaItem extends ImmutablePureComponent {
height100PC: 1, height100PC: 1,
overflowHidden: 1, overflowHidden: 1,
border1PX: 1, border1PX: 1,
borderColorPrimary: !small, borderColorPrimary: 1,
borderColorPrimary: small,
}) })
const statusUrl = `/${account.getIn(['acct'])}/posts/${status.get('id')}`;
return ( return (
<div className={[_s.default, _s.width25PC, _s.pt25PC].join(' ')}> <div className={[_s.default, _s.width25PC, _s.pt25PC].join(' ')}>
<div className={containerClasses}> <div className={containerClasses}>
<NavLink <NavLink
to={status.get('url')} /* : todo : */ to={statusUrl}
title={title} title={title}
className={linkClasses} className={linkClasses}
> >

View File

@ -51,7 +51,7 @@ class CommunityTimelineSettingsModal extends ImmutablePureComponent {
return ( return (
<ModalLayout <ModalLayout
width='320' width={320}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
> >

View File

@ -0,0 +1,58 @@
import { injectIntl, defineMessages } from 'react-intl'
import { muteAccount } from '../../actions/accounts'
const messages = defineMessages({
muteMessage: { id: 'confirmations.mute.message', defaultMessage: 'Are you sure you want to mute {name}?' },
cancel: { id: 'confirmation_modal.cancel', defaultMessage: 'Cancel' },
title: { id: 'display_options', defaultMessage: 'Display Options' },
})
const mapStateToProps = (state) => ({
})
const mapDispatchToProps = (dispatch) => ({
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class DisplayOptionsModal extends PureComponent {
static propTypes = {
isSubmitting: PropTypes.bool.isRequired,
account: PropTypes.object.isRequired,
onConfirm: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
}
componentDidMount() {
this.button.focus()
}
handleClick = () => {
this.props.onClose()
this.props.onConfirm(this.props.account, this.props.notifications)
}
handleCancel = () => {
this.props.onClose()
}
// document.documentElement.style.setProperty("--color-surface", "black");
render() {
const { account, intl } = this.props
return (
<ModalLayout
width={320}
title={intl.formatMessage(messages.title)}
>
</ModalLayout>
)
}
}

View File

@ -28,10 +28,11 @@ class EditProfileModal extends ImmutablePureComponent {
static propTypes = { static propTypes = {
account: ImmutablePropTypes.map, account: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
} }
render() { render() {
const { account, intl } = this.props const { account, intl, onClose } = this.props
const headerSrc = !!account ? account.get('header') : '' const headerSrc = !!account ? account.get('header') : ''
@ -40,6 +41,7 @@ class EditProfileModal extends ImmutablePureComponent {
title={intl.formatMessage(messages.edit_profile)} title={intl.formatMessage(messages.edit_profile)}
noPadding noPadding
width={460} width={460}
onClose={onClose}
> >
<div className={[_s.default, _s.py5, _s.px5, _s.width100PC, _s.overflowHidden].join(' ')}> <div className={[_s.default, _s.py5, _s.px5, _s.width100PC, _s.overflowHidden].join(' ')}>
<Image <Image

View File

@ -4,7 +4,7 @@ import ModalLayout from './modal_layout'
import GroupCreate from '../../features/group_create' import GroupCreate from '../../features/group_create'
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'create_group', defaultMessage: 'Create Group' }, title: { id: 'create_group', defaultMessage: 'Create group' },
}) })
export default export default
@ -22,7 +22,7 @@ class GroupCreateModal extends ImmutablePureComponent {
return ( return (
<ModalLayout <ModalLayout
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
width='440' width={440}
onClose={onClose} onClose={onClose}
> >
<GroupCreate onCloseModal={onClose} /> <GroupCreate onCloseModal={onClose} />

View File

@ -54,7 +54,7 @@ class HashtagTimelineSettingsModal extends ImmutablePureComponent {
return ( return (
<ModalLayout <ModalLayout
width='320' width={320}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onClose={onClose} onClose={onClose}
> >

View File

@ -55,7 +55,7 @@ class HomeTimelineSettingsModal extends ImmutablePureComponent {
return ( return (
<ModalLayout <ModalLayout
width='320' width={320}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onClose={onClose} onClose={onClose}
> >

View File

@ -22,10 +22,10 @@ class ListCreateModal extends ImmutablePureComponent {
return ( return (
<ModalLayout <ModalLayout
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
width='500' width={500}
onClose={onClose} onClose={onClose}
> >
<ListCreate /> <ListCreate isModal />
</ModalLayout> </ModalLayout>
) )
} }

View File

@ -1,6 +1,5 @@
import { injectIntl, defineMessages } from 'react-intl' import { injectIntl, defineMessages } from 'react-intl'
import { makeGetAccount } from '../../selectors' import { deleteList } from '../../actions/lists'
import { blockAccount } from '../../actions/accounts'
import ConfirmationModal from './confirmation_modal' import ConfirmationModal from './confirmation_modal'
const messages = defineMessages({ const messages = defineMessages({
@ -10,14 +9,14 @@ const messages = defineMessages({
}) })
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
onConfirm(account) { onConfirm(listId) {
// dispatch(blockAccount(account.get('id'))) dispatch(deleteList(listId))
}, },
}) })
export default export default
@connect(null, mapDispatchToProps)
@injectIntl @injectIntl
@connect(null, mapDispatchToProps)
class ListDeleteModal extends PureComponent { class ListDeleteModal extends PureComponent {
static propTypes = { static propTypes = {
@ -27,17 +26,17 @@ class ListDeleteModal extends PureComponent {
} }
handleClick = () => { handleClick = () => {
this.props.onConfirm(this.props.account) this.props.onConfirm(this.props.list.get('id'))
} }
render() { render() {
const { list, intl, onClose } = this.props const { list, intl, onClose } = this.props
const title = intl.formatMessage(messages.title, { const title = intl.formatMessage(messages.title, {
list: !!list ? account.get('title') : '', list: !!list ? list.get('title') : '',
}) })
const message = intl.formatMessage(messages.listMessage, { const message = intl.formatMessage(messages.listMessage, {
list: !!list ? account.get('title') : '', list: !!list ? list.get('title') : '',
}) })
return ( return (

View File

@ -1,68 +1,33 @@
import { injectIntl, defineMessages } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import { muteAccount } from '../../actions/accounts' import ImmutablePureComponent from 'react-immutable-pure-component'
import ModalLayout from './modal_layout'
import ListEdit from '../../features/list_edit'
const messages = defineMessages({ const messages = defineMessages({
muteMessage: { id: 'confirmations.mute.message', defaultMessage: 'Are you sure you want to mute {name}?' }, title: { id: 'lists.edit', defaultMessage: 'Edit list' },
cancel: { id: 'confirmation_modal.cancel', defaultMessage: 'Cancel' },
confirm: { id: 'confirmations.mute.confirm', defaultMessage: 'Mute' },
})
const mapStateToProps = (state) => ({
isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
account: state.getIn(['mutes', 'new', 'account']),
})
const mapDispatchToProps = (dispatch) => ({
onConfirm(account, notifications) {
dispatch(muteAccount(account.get('id'), notifications))
},
}) })
export default export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl @injectIntl
class UnfollowModal extends PureComponent { class ListEditorModal extends ImmutablePureComponent {
static propTypes = { static propTypes = {
isSubmitting: PropTypes.bool.isRequired,
account: PropTypes.object.isRequired,
onConfirm: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
} onClose: PropTypes.func.isRequired,
listId: PropTypes.string.isRequired,
componentDidMount() {
this.button.focus()
}
handleClick = () => {
this.props.onClose()
this.props.onConfirm(this.props.account, this.props.notifications)
}
handleCancel = () => {
this.props.onClose()
} }
render() { render() {
const { account, intl } = this.props const { intl, onClose, listId } = this.props
// , {
// message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
// confirm: intl.formatMessage(messages.unfollowConfirm),
// onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
// }));
return ( return (
<ConfirmationModal <ModalLayout
title={`Mute @${account.get('acct')}`} title={intl.formatMessage(messages.title)}
message={<FormattedMessage id='confirmations.mute.message' defaultMessage='Are you sure you want to mute @{name}?' values={{ name: account.get('acct') }} />} width={500}
confirm={<FormattedMessage id='mute' defaultMessage='Mute' />} onClose={onClose}
onConfirm={() => { >
// dispatch(blockDomain(domain)) <ListEdit listId={listId} />
// dispatch(blockDomain(domain)) </ModalLayout>
}}
/>
) )
} }
} }

View File

@ -55,7 +55,7 @@ class ListTimelineSettingsModal extends ImmutablePureComponent {
return ( return (
<ModalLayout <ModalLayout
width='320' width={320}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onClose={onClose} onClose={onClose}
> >

View File

@ -17,7 +17,6 @@ class ModalLayout extends PureComponent {
title: PropTypes.string, title: PropTypes.string,
children: PropTypes.node, children: PropTypes.node,
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
handleCloseModal: PropTypes.func.isRequired,
width: PropTypes.number, width: PropTypes.number,
hideClose: PropTypes.bool, hideClose: PropTypes.bool,
noPadding: PropTypes.bool, noPadding: PropTypes.bool,

View File

@ -2,73 +2,101 @@ import { closeModal } from '../../actions/modal'
import { cancelReplyCompose } from '../../actions/compose' import { cancelReplyCompose } from '../../actions/compose'
import Bundle from '../../features/ui/util/bundle' import Bundle from '../../features/ui/util/bundle'
import { import {
MuteModal, MODAL_ACTIONS,
ReportModal, MODAL_BLOCK_ACCOUNT,
MODAL_BLOCK_DOMAIN,
MODAL_BOOST,
MODAL_COMMUNITY_TIMELINE_SETTINGS,
MODAL_COMPOSE,
MODAL_CONFIRM,
MODAL_DISPLAY_OPTIONS,
MODAL_EDIT_PROFILE,
MODAL_EMBED,
MODAL_GIF_PICKER,
MODAL_GROUP_CREATE,
MODAL_GROUP_DELETE,
MODAL_GROUP_EDITOR,
MODAL_HASHTAG_TIMELINE_SETTINGS,
MODAL_HOME_TIMELINE_SETTINGS,
MODAL_HOTKEYS,
MODAL_LIST_CREATE,
MODAL_LIST_DELETE,
MODAL_LIST_EDITOR,
MODAL_LIST_TIMELINE_SETTINGS,
MODAL_MEDIA,
MODAL_MUTE,
MODAL_PRO_UPGRADE,
MODAL_REPORT,
MODAL_STATUS_REVISIONS,
MODAL_UNAUTHORIZED,
MODAL_UNFOLLOW,
MODAL_VIDEO,
} from '../../constants'
import {
ActionsModal,
BlockAccountModal,
BlockDomainModal,
BoostModal,
CommunityTimelineSettingsModal,
ComposeModal,
ConfirmationModal,
DisplayOptionsModal,
EditProfileModal,
EmbedModal, EmbedModal,
// ListEditor, GifPickerModal,
// ListAdder, GroupCreateModal,
GroupDeleteModal,
GroupEditorModal,
HashtagTimelineSettingsModal,
HomeTimelineSettingsModal,
HotkeysModal,
ListCreateModal,
ListDeleteModal,
ListEditorModal,
ListTimelineSettingsModal,
MediaModal,
MuteModal,
ProUpgradeModal,
ReportModal,
StatusRevisionsModal, StatusRevisionsModal,
UnauthorizedModal,
UnfollowModal,
VideoModal,
} from '../../features/ui/util/async_components' } from '../../features/ui/util/async_components'
import ModalBase from './modal_base' import ModalBase from './modal_base'
import BundleModalError from '../bundle_modal_error' import BundleModalError from '../bundle_modal_error'
import ActionsModal from './actions_modal' const MODAL_COMPONENTS = {}
import BlockAccountModal from './block_account_modal' MODAL_COMPONENTS[MODAL_ACTIONS] = ActionsModal
import BlockDomainModal from './block_domain_modal' MODAL_COMPONENTS[MODAL_BLOCK_ACCOUNT] = BlockAccountModal
import BoostModal from './boost_modal' MODAL_COMPONENTS[MODAL_BLOCK_DOMAIN] = BlockDomainModal
import CommunityTimelineSettingsModal from './community_timeline_settings_modal' MODAL_COMPONENTS[MODAL_BOOST] = BoostModal
import ComposeModal from './compose_modal' MODAL_COMPONENTS[MODAL_COMMUNITY_TIMELINE_SETTINGS] = CommunityTimelineSettingsModal
import ConfirmationModal from './confirmation_modal' MODAL_COMPONENTS[MODAL_COMPOSE] = ComposeModal
import EditProfileModal from './edit_profile_modal' MODAL_COMPONENTS[MODAL_CONFIRM] = ConfirmationModal
import GifPickerModal from './gif_picker_modal' MODAL_COMPONENTS[MODAL_DISPLAY_OPTIONS] = DisplayOptionsModal
import GroupCreateModal from './group_create_modal' MODAL_COMPONENTS[MODAL_EDIT_PROFILE] = EditProfileModal
import GroupDeleteModal from './group_delete_modal' MODAL_COMPONENTS[MODAL_EMBED] = EmbedModal
import GroupEditorModal from './group_editor_modal' MODAL_COMPONENTS[MODAL_GIF_PICKER] = GifPickerModal
import HashtagTimelineSettingsModal from './hashtag_timeline_settings_modal' MODAL_COMPONENTS[MODAL_GROUP_CREATE] = GroupCreateModal
import HomeTimelineSettingsModal from './home_timeline_settings_modal' MODAL_COMPONENTS[MODAL_GROUP_DELETE] = GroupDeleteModal
import HotkeysModal from './hotkeys_modal' MODAL_COMPONENTS[MODAL_GROUP_EDITOR] = GroupEditorModal
import ListCreateModal from './list_create_modal' MODAL_COMPONENTS[MODAL_HASHTAG_TIMELINE_SETTINGS] = HashtagTimelineSettingsModal
import ListDeleteModal from './list_delete_modal' MODAL_COMPONENTS[MODAL_HOME_TIMELINE_SETTINGS] = HomeTimelineSettingsModal
import ListEditorModal from './list_editor_modal' MODAL_COMPONENTS[MODAL_HOTKEYS] = HotkeysModal
import ListTimelineSettingsModal from './list_timeline_settings_modal' MODAL_COMPONENTS[MODAL_LIST_CREATE] = ListCreateModal
import MediaModal from './media_modal' MODAL_COMPONENTS[MODAL_LIST_DELETE] = ListDeleteModal
import ModalLoading from './modal_loading' MODAL_COMPONENTS[MODAL_LIST_EDITOR] = ListEditorModal
import ProUpgradeModal from './pro_upgrade_modal' MODAL_COMPONENTS[MODAL_LIST_TIMELINE_SETTINGS] = ListTimelineSettingsModal
import VideoModal from './video_modal' MODAL_COMPONENTS[MODAL_MEDIA] = MediaModal
import UnauthorizedModal from './unauthorized_modal' MODAL_COMPONENTS[MODAL_MUTE] = MuteModal
import UnfollowModal from './unfollow_modal' MODAL_COMPONENTS[MODAL_PRO_UPGRADE] = ProUpgradeModal
MODAL_COMPONENTS[MODAL_REPORT] = ReportModal
const MODAL_COMPONENTS = { MODAL_COMPONENTS[MODAL_STATUS_REVISIONS] = StatusRevisionsModal
ACTIONS: () => Promise.resolve({ default: ActionsModal }), MODAL_COMPONENTS[MODAL_UNAUTHORIZED] = UnauthorizedModal
BLOCK_ACCOUNT: () => Promise.resolve({ default: BlockAccountModal }), MODAL_COMPONENTS[MODAL_UNFOLLOW] = UnfollowModal
BLOCK_DOMAIN: () => Promise.resolve({ default: BlockDomainModal }), MODAL_COMPONENTS[MODAL_VIDEO] = VideoModal
BOOST: () => Promise.resolve({ default: BoostModal }),
COMMUNITY_TIMELINE_SETTINGS: () => Promise.resolve({ default: CommunityTimelineSettingsModal }),
COMPOSE: () => Promise.resolve({ default: ComposeModal }),
CONFIRM: () => Promise.resolve({ default: ConfirmationModal }),
EDIT_PROFILE: () => Promise.resolve({ default: EditProfileModal }),
EMBED: () => Promise.resolve({ default: EmbedModal }),
GIF_PICKER: () => Promise.resolve({ default: GifPickerModal }),
GROUP_CREATE: () => Promise.resolve({ default: GroupCreateModal }),
GROUP_DELETE: () => Promise.resolve({ default: GroupDeleteModal }),
GROUP_EDITOR: () => Promise.resolve({ default: GroupEditorModal }),
HASHTAG_TIMELINE_SETTINGS: () => Promise.resolve({ default: HashtagTimelineSettingsModal }),
HOME_TIMELINE_SETTINGS: () => Promise.resolve({ default: HomeTimelineSettingsModal }),
HOTKEYS: () => Promise.resolve({ default: HotkeysModal }),
LIST_CREATE: () => Promise.resolve({ default: ListCreateModal }),
LIST_DELETE: () => Promise.resolve({ default: ListDeleteModal }),
LIST_EDITOR: () => Promise.resolve({ default: ListEditorModal }),
LIST_TIMELINE_SETTINGS: () => Promise.resolve({ default: ListTimelineSettingsModal }),
MEDIA: () => Promise.resolve({ default: MediaModal }),
MUTE: MuteModal,
PRO_UPGRADE: () => Promise.resolve({ default: ProUpgradeModal }),
REPORT: ReportModal,
STATUS_REVISIONS: StatusRevisionsModal,
UNAUTHORIZED: () => Promise.resolve({ default: UnauthorizedModal }),
UNFOLLOW: () => Promise.resolve({ default: UnfollowModal }),
VIDEO: () => Promise.resolve({ default: VideoModal }),
}
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
type: state.getIn(['modal', 'modalType']), type: state.getIn(['modal', 'modalType']),

View File

@ -25,7 +25,7 @@ class ProUpgradeModal extends ImmutablePureComponent {
return ( return (
<ModalLayout <ModalLayout
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
width='460' width={460}
onClose={onClose} onClose={onClose}
> >
<Text> <Text>

View File

@ -5,7 +5,6 @@ import classNames from 'classnames/bind'
import { loadStatusRevisions } from '../../actions/status_revisions' import { loadStatusRevisions } from '../../actions/status_revisions'
import ModalLayout from './modal_layout' import ModalLayout from './modal_layout'
import RelativeTimestamp from '../relative_timestamp' import RelativeTimestamp from '../relative_timestamp'
import ScrollableList from '../scrollable_list'
import Text from '../text' import Text from '../text'
const cx = classNames.bind(_s) const cx = classNames.bind(_s)
@ -56,11 +55,10 @@ class StatusRevisionsModal extends ImmutablePureComponent {
return ( return (
<ModalLayout <ModalLayout
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
width='480' width={480}
onClose={onClose} onClose={onClose}
> >
<div className={[_s.default]}> <div className={[_s.default]}>
<ScrollableList>
{ {
revisions.map((revision, i) => { revisions.map((revision, i) => {
const isFirst = i === 0 const isFirst = i === 0
@ -91,7 +89,6 @@ class StatusRevisionsModal extends ImmutablePureComponent {
) )
}) })
} }
</ScrollableList>
</div> </div>
</ModalLayout> </ModalLayout>
) )

View File

@ -43,10 +43,18 @@ class Notification extends ImmutablePureComponent {
createdAt: PropTypes.string, createdAt: PropTypes.string,
statusId: PropTypes.string, statusId: PropTypes.string,
type: PropTypes.string.isRequired, type: PropTypes.string.isRequired,
isHidden: PropTypes.bool,
} }
render() { render() {
const { intl, accounts, createdAt, type, statusId } = this.props const {
intl,
accounts,
createdAt,
type,
statusId,
isHidden,
} = this.props
const count = !!accounts ? accounts.size : 0 const count = !!accounts ? accounts.size : 0
@ -83,6 +91,10 @@ class Notification extends ImmutablePureComponent {
return null return null
} }
if (isHidden) {
// : todo :
}
return ( return (
<div className={[_s.default, _s.px10, _s.cursorPointer, _s.backgroundColorSubtle_onHover].join(' ')}> <div className={[_s.default, _s.px10, _s.cursorPointer, _s.backgroundColorSubtle_onHover].join(' ')}>
<div className={[_s.default, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}> <div className={[_s.default, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>

View File

@ -1,5 +1,5 @@
import { Fragment } from 'react' import { Fragment } from 'react'
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl, FormattedMessage } 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 { shortNumberFormat } from '../../utils/numbers' import { shortNumberFormat } from '../../utils/numbers'
@ -9,6 +9,7 @@ import Divider from '../divider'
import Heading from '../heading' import Heading from '../heading'
import Icon from '../icon' import Icon from '../icon'
import Text from '../text' import Text from '../text'
import RelativeTimestamp from '../relative_timestamp'
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'about', defaultMessage: 'About' }, title: { id: 'about', defaultMessage: 'About' },
@ -27,15 +28,19 @@ class GroupInfoPanel extends ImmutablePureComponent {
render() { render() {
const { intl, group } = this.props const { intl, group } = this.props
console.log("group:", group)
return ( return (
<PanelLayout title={intl.formatMessage(messages.title)}> <PanelLayout title={intl.formatMessage(messages.title)}>
{ {
!!group && !!group &&
<Fragment> <Fragment>
<Heading size='h2'> <div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Text weight='medium'>
{group.get('title')} {group.get('title')}
</Heading> </Text>
</div>
<Divider isSmall /> <Divider isSmall />
@ -63,6 +68,23 @@ class GroupInfoPanel extends ImmutablePureComponent {
<Divider isSmall /> <Divider isSmall />
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='calendar' size='12px' className={_s.fillColorSecondary} />
<Text
size='small'
color='secondary'
className={_s.ml5}
>
{
<FormattedMessage id='lists.panel_created' defaultMessage='Created: {date}' values={{
date: <RelativeTimestamp timestamp={group.get('created_at')} />,
}} />
}
</Text>
</div>
<Divider isSmall />
<Text> <Text>
{group.get('description')} {group.get('description')}
</Text> </Text>

View File

@ -1,9 +1,10 @@
import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import { fetchGroups } from '../../actions/groups'
import PanelLayout from './panel_layout' import PanelLayout from './panel_layout'
import GroupListItem from '../group_list_item' import GroupListItem from '../group_list_item'
import Button from '../button' import ScrollableList from '../scrollable_list'
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'groups.sidebar-panel.title', defaultMessage: 'Groups you\'re in' }, title: { id: 'groups.sidebar-panel.title', defaultMessage: 'Groups you\'re in' },
@ -15,13 +16,41 @@ const mapStateToProps = (state) => ({
groupIds: state.getIn(['group_lists', 'member']), groupIds: state.getIn(['group_lists', 'member']),
}) })
const mapDispatchToProps = (dispatch) => ({
onFetchGroups: (type) => {
dispatch(fetchGroups(type))
}
})
export default export default
@connect(mapStateToProps) @connect(mapStateToProps, mapDispatchToProps)
@injectIntl @injectIntl
class GroupSidebarPanel extends ImmutablePureComponent { class GroupSidebarPanel extends ImmutablePureComponent {
static propTypes = { static propTypes = {
groupIds: ImmutablePropTypes.list, groupIds: ImmutablePropTypes.list,
slim: PropTypes.bool, isLazy: PropTypes.bool,
isSlim: PropTypes.bool,
onFetchGroups: PropTypes.func.isRequired,
}
state = {
fetched: false,
}
static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.isHidden && nextProps.isIntersecting && !prevState.fetched) {
return {
fetched: true
}
}
return null
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (!prevState.fetched && this.state.fetched && this.props.isLazy) {
this.props.onFetchGroups('member')
}
} }
render() { render() {
@ -42,6 +71,9 @@ class GroupSidebarPanel extends ImmutablePureComponent {
noPadding={slim} noPadding={slim}
> >
<div className={_s.default}> <div className={_s.default}>
<ScrollableList
scrollKey='groups-panel'
>
{ {
groupIds.slice(0, maxCount).map((groupId, i) => ( groupIds.slice(0, maxCount).map((groupId, i) => (
<GroupListItem <GroupListItem
@ -52,6 +84,7 @@ class GroupSidebarPanel extends ImmutablePureComponent {
/> />
)) ))
} }
</ScrollableList>
</div> </div>
</PanelLayout> </PanelLayout>
) )

View File

@ -1,57 +1,48 @@
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
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 PanelLayout from './panel_layout' import PanelLayout from './panel_layout'
import Avatar from '../avatar'
import Divider from '../divider' import Divider from '../divider'
import Icon from '../icon' import Icon from '../icon'
import Heading from '../heading' import RelativeTimestamp from '../relative_timestamp'
import Text from '../text' import Text from '../text'
const messages = defineMessages({ const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
memberCount: { id: 'lists.panel_members', defaultMessage: 'Members: {count}' },
createdAt: { id: 'lists.panel_created', defaultMessage: 'Created: {date}' },
title: { id: 'lists_information', defaultMessage: 'List Information' }, title: { id: 'lists_information', defaultMessage: 'List Information' },
edit: { id: 'edit', defaultMessage: 'Edit' }, edit: { id: 'edit', defaultMessage: 'Edit' },
}) })
const mapStateToProps = (state) => ({
// accountIds: state.getIn(['listEditor', 'accounts', 'items']),
})
const mapDispatchToProps = (dispatch) => ({
})
export default export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl @injectIntl
class ListDetailsPanel extends ImmutablePureComponent { class ListDetailsPanel extends ImmutablePureComponent {
static propTypes = { static propTypes = {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
onEdit: PropTypes.func.isRequired,
list: ImmutablePropTypes.map,
} }
handleShowAllLists() { handleOnEdit = () => {
this.props.onEdit()
} }
render() { render() {
const { intl } = this.props const { intl, list } = this.props
const title = !!list ? list.get('title') : ''
const createdAt = !!list ? list.get('created_at') : ''
return ( return (
<PanelLayout <PanelLayout
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
headerButtonTitle={intl.formatMessage(messages.edit)} headerButtonTitle={intl.formatMessage(messages.edit)}
headerButtonAction={this.handleShowAllLists} headerButtonAction={this.handleOnEdit}
> >
<div className={_s.default}> <div className={_s.default}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter,].join(' ')}> <div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Text weight='medium'> <Text weight='medium'>
Some List Title {title}
</Text> </Text>
</div> </div>
@ -65,42 +56,13 @@ class ListDetailsPanel extends ImmutablePureComponent {
className={_s.ml5} className={_s.ml5}
> >
{ {
intl.formatMessage(messages.createdAt, { <FormattedMessage id='lists.panel_created' defaultMessage='Created: {date}' values={{
date: '12-25-2019' date: <RelativeTimestamp timestamp={createdAt} />,
}) }} />
} }
</Text> </Text>
</div> </div>
<Divider isSmall />
<div className={[_s.default].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='group' size='12px' className={_s.fillColorSecondary} />
<Text
size='small'
color='secondary'
className={_s.ml5}
>
{
intl.formatMessage(messages.memberCount, {
count: 10
})
}
</Text>
</div>
<div className={[_s.default, _s.flexRow, _s.flexWrap, _s.pt10].join(' ')}>
{
[1, 2, 3, 4, 5, 6, 7, 8, 9].map(item => (
<div className={[_s.default, _s.mr5].join(' ')}>
<Avatar size={26} />
</div>
))
}
</div>
</div>
</div> </div>
</PanelLayout> </PanelLayout>
) )

View File

@ -32,9 +32,29 @@ class ListsPanel extends ImmutablePureComponent {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
} }
componentWillMount() { state = {
fetched: false,
}
updateOnProps = [
'lists',
]
static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.isHidden && nextProps.isIntersecting && !prevState.fetched) {
return {
fetched: true,
}
}
return null
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (!prevState.fetched && this.state.fetched) {
this.props.onFetchLists() this.props.onFetchLists()
} }
}
render() { render() {
const { intl, lists } = this.props const { intl, lists } = this.props

View File

@ -66,11 +66,12 @@ class MediaGalleryPanel extends ImmutablePureComponent {
> >
<div className={[_s.default, _s.flexRow, _s.flexWrap, _s.px10, _s.py10].join(' ')}> <div className={[_s.default, _s.flexRow, _s.flexWrap, _s.px10, _s.py10].join(' ')}>
{ {
attachments.slice(0, 16).map((attachment) => ( attachments.slice(0, 16).map((attachment, i) => (
<MediaItem <MediaItem
small isSmall
key={attachment.get('id')} key={attachment.get('id')}
attachment={attachment} attachment={attachment}
account={account}
/> />
)) ))
} }

View File

@ -9,7 +9,7 @@ import SettingSwitch from '../setting_switch'
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'notification_filters', defaultMessage: 'Notification Filters' }, title: { id: 'notification_filters', defaultMessage: 'Notification Filters' },
onlyVerified: { id: 'notification_only_verified', defaultMessage: 'Only Verified Users' }, onlyVerified: { id: 'notification_only_verified', defaultMessage: 'Only Verified Users' },
onlyFollowing: { id: 'notification_only_following', defaultMessage: 'Only People I Follow' }, // onlyFollowing: { id: 'notification_only_following', defaultMessage: 'Only People I Follow' },
}) })
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
@ -55,13 +55,14 @@ class NotificationFilterPanel extends ImmutablePureComponent {
label={intl.formatMessage(messages.onlyVerified)} label={intl.formatMessage(messages.onlyVerified)}
/> />
{ /* : todo :
<SettingSwitch <SettingSwitch
prefix='notification' prefix='notification'
settings={settings} settings={settings}
settingPath={'onlyFollowing'} settingPath={'onlyFollowing'}
onChange={onChange} onChange={onChange}
label={intl.formatMessage(messages.onlyFollowing)} label={intl.formatMessage(messages.onlyFollowing)}
/> /> */ }
</PanelLayout> </PanelLayout>
) )
} }

View File

@ -4,6 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import { fetchGabTrends } from '../../actions/gab_trends' import { fetchGabTrends } from '../../actions/gab_trends'
import PanelLayout from './panel_layout' import PanelLayout from './panel_layout'
import ColumnIndicator from '../column_indicator' import ColumnIndicator from '../column_indicator'
import ScrollableList from '../scrollable_list'
import TrendingItem from '../trends_item' import TrendingItem from '../trends_item'
const messages = defineMessages({ const messages = defineMessages({
@ -29,7 +30,7 @@ class TrendsPanel extends ImmutablePureComponent {
onFetchGabTrends: PropTypes.func.isRequired, onFetchGabTrends: PropTypes.func.isRequired,
} }
componentWillMount() { componentDidMount() {
this.props.onFetchGabTrends() this.props.onFetchGabTrends()
} }
@ -47,7 +48,12 @@ class TrendsPanel extends ImmutablePureComponent {
<ColumnIndicator type='loading' /> <ColumnIndicator type='loading' />
} }
{ {
gabtrends && gabtrends.slice(0, 8).map((trend, i) => ( !gabtrends.isEmpty() &&
<ScrollableList
scrollKey='trending-items'
>
{
gabtrends.slice(0, 8).map((trend, i) => (
<TrendingItem <TrendingItem
key={`gab-trend-${i}`} key={`gab-trend-${i}`}
index={i + 1} index={i + 1}
@ -61,6 +67,8 @@ class TrendsPanel extends ImmutablePureComponent {
/> />
)) ))
} }
</ScrollableList>
}
</div> </div>
</PanelLayout> </PanelLayout>
) )

View File

@ -36,19 +36,34 @@ class WhoToFollowPanel extends ImmutablePureComponent {
'suggestions', 'suggestions',
] ]
componentDidMount () { state = {
fetched: false,
}
static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.isHidden && nextProps.isIntersecting && !prevState.fetched) {
return {
fetched: true
}
}
return null
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (!prevState.fetched && this.state.fetched) {
this.props.fetchSuggestions() this.props.fetchSuggestions()
} }
}
render() { render() {
const { intl, suggestions, dismissSuggestion } = this.props const { intl, suggestions, dismissSuggestion } = this.props
if (suggestions.isEmpty()) { if (suggestions.isEmpty()) return null
return null
}
return ( return (
<PanelLayout <PanelLayout
noPadding
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
footerButtonTitle={intl.formatMessage(messages.show_more)} footerButtonTitle={intl.formatMessage(messages.show_more)}
footerButtonTo='/explore' footerButtonTo='/explore'
@ -57,6 +72,7 @@ class WhoToFollowPanel extends ImmutablePureComponent {
{ {
suggestions.map(accountId => ( suggestions.map(accountId => (
<Account <Account
compact
showDismiss showDismiss
key={accountId} key={accountId}
id={accountId} id={accountId}

View File

@ -0,0 +1,12 @@
import PopoverLayout from './popover_layout'
import Text from '../text'
export default class UserInfoPopover extends PureComponent {
render() {
return (
<PopoverLayout>
<Text>testing</Text>
</PopoverLayout>
)
}
}

View File

@ -2,8 +2,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import { Manager, Reference, Popper } from 'react-popper' import { Manager, Reference, Popper } from 'react-popper'
import classnames from 'classnames/bind' import classnames from 'classnames/bind'
import spring from 'react-motion/lib/spring'
import Motion from '../../features/ui/util/optional_motion'
import { openPopover, closePopover } from '../../actions/popover' import { openPopover, closePopover } from '../../actions/popover'
import { openModal, closeModal } from '../../actions/modal' import { openModal, closeModal } from '../../actions/modal'
import { isUserTouching } from '../../utils/is_mobile' import { isUserTouching } from '../../utils/is_mobile'
@ -117,7 +115,6 @@ class PopoverBase extends ImmutablePureComponent {
targetRef, targetRef,
innerRef, innerRef,
} = this.props } = this.props
const open = this.state.id === openPopoverType
const containerClasses = cx({ const containerClasses = cx({
default: 1, default: 1,
@ -125,8 +122,6 @@ class PopoverBase extends ImmutablePureComponent {
displayNone: !visible, displayNone: !visible,
}) })
console.log('targetRef:', targetRef)
return ( return (
<Manager> <Manager>
<Popper <Popper

View File

@ -1,37 +1,51 @@
import detectPassiveEvents from 'detect-passive-events' import detectPassiveEvents from 'detect-passive-events'
import { closePopover } from '../../actions/popover' import { closePopover } from '../../actions/popover'
import {
POPOVER_CONTENT_WARNING,
POPOVER_DATE_PICKER,
POPOVER_EMOJI_PICKER,
POPOVER_GROUP_INFO,
POPOVER_PROFILE_OPTIONS,
POPOVER_REPOST_OPTIONS,
POPOVER_SEARCH,
POPOVER_SIDEBAR_MORE,
POPOVER_STATUS_OPTIONS,
POPOVER_STATUS_SHARE,
POPOVER_STATUS_VISIBILITY,
POPOVER_USER_INFO,
} from '../../constants'
import {
ContentWarningPopover,
DatePickerPopover,
EmojiPickerPopover,
GroupInfoPopover,
ProfileOptionsPopover,
RepostOptionsPopover,
SearchPopover,
SidebarMorePopover,
StatusOptionsPopover,
StatusSharePopover,
StatusVisibilityPopover,
UserInfoPopover,
} from '../../features/ui/util/async_components'
import Bundle from '../../features/ui/util/bundle' import Bundle from '../../features/ui/util/bundle'
import BundleModalError from '../bundle_modal_error'
import PopoverBase from './popover_base' import PopoverBase from './popover_base'
import ContentWarningPopover from './content_warning_popover'
import DatePickerPopover from './date_picker_popover'
import EmojiPickerPopover from './emoji_picker_popover'
import GroupInfoPopover from './group_info_popover'
import ProfileOptionsPopover from './profile_options_popover'
import RepostOptionsPopover from './repost_options_popover'
import SearchPopover from './search_popover'
import SidebarMorePopover from './sidebar_more_popover'
import StatusOptionsPopover from './status_options_popover'
import StatusSharePopover from './status_share_popover'
import StatusVisibilityPopover from './status_visibility_popover'
import UserInfoPopover from './user_info_popover'
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
const POPOVER_COMPONENTS = { const POPOVER_COMPONENTS = {}
CONTENT_WARNING: () => Promise.resolve({ default: ContentWarningPopover }), POPOVER_COMPONENTS[POPOVER_CONTENT_WARNING] = ContentWarningPopover
DATE_PICKER: () => Promise.resolve({ default: DatePickerPopover }), POPOVER_COMPONENTS[POPOVER_DATE_PICKER] = DatePickerPopover
EMOJI_PICKER: () => Promise.resolve({ default: EmojiPickerPopover }), POPOVER_COMPONENTS[POPOVER_EMOJI_PICKER] = EmojiPickerPopover
GROUP_INFO: () => GroupInfoPopover, POPOVER_COMPONENTS[POPOVER_GROUP_INFO] = GroupInfoPopover
PROFILE_OPTIONS: () => Promise.resolve({ default: ProfileOptionsPopover }), POPOVER_COMPONENTS[POPOVER_PROFILE_OPTIONS] = ProfileOptionsPopover
REPOST_OPTIONS: () => Promise.resolve({ default: RepostOptionsPopover }), POPOVER_COMPONENTS[POPOVER_REPOST_OPTIONS] = RepostOptionsPopover
SEARCH: () => Promise.resolve({ default: SearchPopover }), POPOVER_COMPONENTS[POPOVER_SEARCH] = SearchPopover
SIDEBAR_MORE: () => Promise.resolve({ default: SidebarMorePopover }), POPOVER_COMPONENTS[POPOVER_SIDEBAR_MORE] = SidebarMorePopover
STATUS_OPTIONS: () => Promise.resolve({ default: StatusOptionsPopover }), POPOVER_COMPONENTS[POPOVER_STATUS_OPTIONS] = StatusOptionsPopover
STATUS_SHARE: () => Promise.resolve({ default: StatusSharePopover }), POPOVER_COMPONENTS[POPOVER_STATUS_SHARE] = StatusSharePopover
STATUS_VISIBILITY: () => Promise.resolve({ default: StatusVisibilityPopover }), POPOVER_COMPONENTS[POPOVER_STATUS_VISIBILITY] = StatusVisibilityPopover
USER_INFO: () => Promise.resolve({ default: UserInfoPopover }), POPOVER_COMPONENTS[POPOVER_USER_INFO] = UserInfoPopover
}
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
type: state.getIn(['popover', 'popoverType']), type: state.getIn(['popover', 'popoverType']),
@ -84,14 +98,10 @@ class PopoverRoot extends PureComponent {
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions) document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions)
} }
setRef = c => { setRef = (c) => {
this.node = c this.node = c
} }
setFocusRef = c => {
this.focusedItem = c
}
handleKeyDown = e => { handleKeyDown = e => {
const items = Array.from(this.node.getElementsByTagName('a')) const items = Array.from(this.node.getElementsByTagName('a'))
const index = items.indexOf(document.activeElement) const index = items.indexOf(document.activeElement)
@ -150,8 +160,6 @@ class PopoverRoot extends PureComponent {
const { type, props } = this.props const { type, props } = this.props
const visible = !!type const visible = !!type
console.log("POPOVER_COMPONENTS[type]:", type, POPOVER_COMPONENTS[type]);
return ( return (
<PopoverBase <PopoverBase
visible={visible} visible={visible}
@ -162,12 +170,12 @@ class PopoverRoot extends PureComponent {
visible && visible &&
<Bundle <Bundle
fetchComponent={POPOVER_COMPONENTS[type]} fetchComponent={POPOVER_COMPONENTS[type]}
loading={this.renderLoading(type)} loading={this.renderLoading()}
error={this.renderError} error={this.renderError}
renderDelay={200} renderDelay={200}
> >
{ {
(SpecificComponent) => <SpecificComponent {...props} /> (Component) => <Component {...props} />
} }
</Bundle> </Bundle>
} }

View File

@ -1,8 +1,39 @@
import { defineMessages, injectIntl } from 'react-intl'
import { MODAL_DISPLAY_OPTIONS } from '../../constants'
import { openModal } from '../../actions/modal'
import PopoverLayout from './popover_layout' import PopoverLayout from './popover_layout'
import List from '../list' import List from '../list'
export default class SidebarMorePopover extends PureComponent { const messages = defineMessages({
display: { id: 'display_options', defaultMessage: 'Display Options' },
help: { id: 'getting_started.help', defaultMessage: 'Help' },
settings: { id: 'settings', defaultMessage: 'Settings' },
logout: { 'id': 'confirmations.logout.confirm', 'defaultMessage': 'Log out' },
})
const mapDispatchToProps = (dispatch) => ({
onOpenDisplayModal: () => {
dispatch(openModal(MODAL_DISPLAY_OPTIONS))
},
})
export default
@injectIntl
@connect(null, mapDispatchToProps)
class SidebarMorePopover extends PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
onOpenDisplayModal: PropTypes.func.isRequired,
}
handleOnOpenDisplayModal = () => {
this.props.onOpenDisplayModal()
}
render() { render() {
const { intl } = this.props
return ( return (
<PopoverLayout className={_s.width240PX}> <PopoverLayout className={_s.width240PX}>
<List <List
@ -10,15 +41,19 @@ export default class SidebarMorePopover extends PureComponent {
scrollKey='profile_options' scrollKey='profile_options'
items={[ items={[
{ {
title: 'Help', title: intl.formatMessage(messages.help),
href: 'https://help.gab.com', href: 'https://help.gab.com',
}, },
{ {
title: 'Settings', title: intl.formatMessage(messages.display),
onClick: this.handleOnOpenDisplayModal,
},
{
title: intl.formatMessage(messages.settings),
href: '/settings', href: '/settings',
}, },
{ {
title: 'Log Out', title: intl.formatMessage(messages.logout),
href: '/auth/log_out', href: '/auth/log_out',
}, },
]} ]}

View File

@ -47,7 +47,7 @@ class StatusVisibilityDropdown extends PureComponent {
this.props.onChange(value) this.props.onChange(value)
} }
componentWillMount () { componentDidMount () {
const { intl } = this.props const { intl } = this.props
this.options = [ this.options = [

View File

@ -6,7 +6,9 @@ import {
CX, CX,
POPOVER_PROFILE_OPTIONS, POPOVER_PROFILE_OPTIONS,
PLACEHOLDER_MISSING_HEADER_SRC, PLACEHOLDER_MISSING_HEADER_SRC,
MODAL_EDIT_PROFILE,
} from '../constants' } from '../constants'
import { openModal } from '../actions/modal'
import { openPopover } from '../actions/popover' import { openPopover } from '../actions/popover'
import { me } from '../initial_state' import { me } from '../initial_state'
import AccountActionButton from './account_action_button' import AccountActionButton from './account_action_button'
@ -27,6 +29,7 @@ const messages = defineMessages({
comments: { id: 'comments', defaultMessage: 'Comments' }, comments: { id: 'comments', defaultMessage: 'Comments' },
media: { id: 'media', defaultMessage: 'Media' }, media: { id: 'media', defaultMessage: 'Media' },
accountFollowsYou: { id: 'account.follows_you', defaultMessage: 'Follows you' }, accountFollowsYou: { id: 'account.follows_you', defaultMessage: 'Follows you' },
editProfile: { id: "account.edit_profile", defaultMessage: "Edit profile" },
}) })
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
@ -35,6 +38,10 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(openPopover(POPOVER_PROFILE_OPTIONS, props)) dispatch(openPopover(POPOVER_PROFILE_OPTIONS, props))
}, },
onEditProfile() {
dispatch(openModal(MODAL_EDIT_PROFILE))
},
}); });
export default export default
@ -45,6 +52,7 @@ class ProfileHeader extends ImmutablePureComponent {
static propTypes = { static propTypes = {
account: ImmutablePropTypes.map, account: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
onEditProfile: PropTypes.func.isRequired,
openProfileOptionsPopover: PropTypes.func.isRequired, openProfileOptionsPopover: PropTypes.func.isRequired,
} }
@ -52,6 +60,10 @@ class ProfileHeader extends ImmutablePureComponent {
stickied: false, stickied: false,
} }
handleOnEditProfile = () => {
this.props.onEditProfile()
}
handleOpenMore = () => { handleOpenMore = () => {
const { openProfileOptionsPopover, account } = this.props const { openProfileOptionsPopover, account } = this.props
@ -164,15 +176,10 @@ class ProfileHeader extends ImmutablePureComponent {
backgroundColor='none' backgroundColor='none'
color='brand' color='brand'
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')} className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
href='' onClick={this.handleOnEditProfile}
> >
<Text <Text color='inherit' weight='bold' size='medium' className={_s.px15}>
color='inherit' {intl.formatMessage(messages.editProfile)}
weight='bold'
size='medium'
className={[_s.px15].join(' ')}
>
Edit Profile
</Text> </Text>
</Button> </Button>
</div> </div>

View File

@ -196,7 +196,7 @@ export default class ScrollableList extends PureComponent {
return firstChild && firstChild.key; return firstChild && firstChild.key;
} }
handleLoadMore = e => { handleLoadMore = (e) => {
e.preventDefault(); e.preventDefault();
this.props.onLoadMore(); this.props.onLoadMore();
} }
@ -213,9 +213,7 @@ export default class ScrollableList extends PureComponent {
} = this.props } = this.props
const childrenCount = React.Children.count(children); const childrenCount = React.Children.count(children);
const trackScroll = true; //placeholder const loadMore = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null
const loadMore = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
if (showLoading) { if (showLoading) {
return <ColumnIndicator type='loading' /> return <ColumnIndicator type='loading' />
@ -232,7 +230,7 @@ export default class ScrollableList extends PureComponent {
index={index} index={index}
listLength={childrenCount} listLength={childrenCount}
intersectionObserverWrapper={this.intersectionObserverWrapper} intersectionObserverWrapper={this.intersectionObserverWrapper}
saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null} saveHeightKey={`${this.context.router.route.location.key}:${scrollKey}`}
> >
{ {
React.cloneElement(child, { React.cloneElement(child, {

View File

@ -57,8 +57,8 @@ class Search extends PureComponent {
} }
} }
handleChange = (e) => { handleChange = (value) => {
this.props.onChange(e.target.value) this.props.onChange(value)
} }
handleFocus = () => { handleFocus = () => {

View File

@ -43,8 +43,13 @@ const mapStateToProps = (state, { timelineId }) => {
const getStatusIds = makeGetStatusIds(); const getStatusIds = makeGetStatusIds();
const promotion = promotions.length > 0 && sample(promotions.filter(p => p.timeline_id === timelineId)); const promotion = promotions.length > 0 && sample(promotions.filter(p => p.timeline_id === timelineId));
const statusIds = getStatusIds(state, {
type: timelineId.substring(0,5) === 'group' ? 'group' : timelineId,
id: timelineId
})
return { return {
statusIds: getStatusIds(state, { type: timelineId.substring(0,5) === 'group' ? 'group' : timelineId, id: timelineId }), statusIds,
isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true), isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false), isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
hasMore: state.getIn(['timelines', timelineId, 'hasMore']), hasMore: state.getIn(['timelines', timelineId, 'hasMore']),

View File

@ -28,7 +28,7 @@ export default class TabBar extends PureComponent {
onClick={tab.onClick} onClick={tab.onClick}
icon={tab.icon} icon={tab.icon}
to={tab.to} to={tab.to}
active={tab.active} isActive={tab.active}
isLarge={isLarge} isLarge={isLarge}
/> />
)) ))

View File

@ -13,22 +13,23 @@ export default class Textarea extends PureComponent {
onKeyUp: PropTypes.func, onKeyUp: PropTypes.func,
onFocus: PropTypes.func, onFocus: PropTypes.func,
onBlur: PropTypes.func, onBlur: PropTypes.func,
onClear: PropTypes.func,
title: PropTypes.string, title: PropTypes.string,
} }
handleOnChange = (e) => {
this.props.onChange(e.target.value)
}
render() { render() {
const { const {
placeholder, placeholder,
prependIcon, prependIcon,
value, value,
hasClear, hasClear,
onChange,
onKeyUp, onKeyUp,
onFocus, onFocus,
onBlur, onBlur,
onClear, title,
title
} = this.props } = this.props
const inputClasses = cx({ const inputClasses = cx({
@ -64,7 +65,7 @@ export default class Textarea extends PureComponent {
type='text' type='text'
placeholder={placeholder} placeholder={placeholder}
value={value} value={value}
onChange={onChange} onChange={this.handleOnChange}
onKeyUp={onKeyUp} onKeyUp={onKeyUp}
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur} onBlur={onBlur}

View File

@ -1,3 +1,4 @@
import { Fragment } from 'react'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import { urlRegex } from '../features/compose/util/url_regex' import { urlRegex } from '../features/compose/util/url_regex'
import Button from './button' import Button from './button'
@ -19,6 +20,7 @@ export default class TrendingItem extends PureComponent {
author: PropTypes.string, author: PropTypes.string,
publishDate: PropTypes.string, publishDate: PropTypes.string,
isLast: PropTypes.bool, isLast: PropTypes.bool,
isHidden: PropTypes.bool,
wide: PropTypes.bool, wide: PropTypes.bool,
} }
@ -51,9 +53,24 @@ export default class TrendingItem extends PureComponent {
publishDate, publishDate,
isLast, isLast,
wide, wide,
isHidden,
} = this.props } = this.props
const { hovering } = this.state const { hovering } = this.state
const correctedAuthor = author.replace('www.', '')
const correctedDescription = description.length >= 120 ? `${description.substring(0, 120).trim()}...` : description
const descriptionHasLink = correctedDescription.match(urlRegex)
if (isHidden) {
return (
<Fragment>
{title}
{!descriptionHasLink && correctedDescription}
{correctedAuthor}
</Fragment>
)
}
const containerClasses = cx({ const containerClasses = cx({
default: 1, default: 1,
noUnderline: 1, noUnderline: 1,
@ -70,10 +87,6 @@ export default class TrendingItem extends PureComponent {
underline: hovering, underline: hovering,
}) })
const correctedAuthor = author.replace('www.', '')
const correctedDescription = description.length >= 120 ? `${description.substring(0, 120).trim()}...` : description
const descriptionHasLink = correctedDescription.match(urlRegex)
const image = ( const image = (
<Image <Image
nullable nullable

View File

@ -12,7 +12,47 @@ export const BREAKPOINT_EXTRA_SMALL = 992
export const ALLOWED_AROUND_SHORT_CODE = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d' export const ALLOWED_AROUND_SHORT_CODE = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d'
export const MAX_POST_CHARACTER_COUNT = 3000 export const MAX_POST_CHARACTER_COUNT = 3000
export const PLACEHOLDER_MISSING_HEADER_SRC = '/headers/original/missing.png' export const PLACEHOLDER_MISSING_HEADER_SRC = '/original/missing.png'
export const POPOVER_CONTENT_WARNING = 'CONTENT_WARNING'
export const POPOVER_DATE_PICKER = 'DATE_PICKER'
export const POPOVER_EMOJI_PICKER = 'EMOJI_PICKER'
export const POPOVER_GROUP_INFO = 'GROUP_INFO'
export const POPOVER_PROFILE_OPTIONS = 'PROFILE_OPTIONS' export const POPOVER_PROFILE_OPTIONS = 'PROFILE_OPTIONS'
export const POPOVER_REPOST_OPTIONS = 'REPOST_OPTIONS'
export const POPOVER_SEARCH = 'SEARCH'
export const POPOVER_SIDEBAR_MORE = 'SIDEBAR_MORE'
export const POPOVER_STATUS_OPTIONS = 'STATUS_OPTIONS'
export const POPOVER_STATUS_SHARE = 'STATUS_SHARE'
export const POPOVER_STATUS_VISIBILITY = 'STATUS_VISIBILITY'
export const POPOVER_USER_INFO = 'USER_INFO' export const POPOVER_USER_INFO = 'USER_INFO'
export const MODAL_ACTIONS = 'ACTIONS'
export const MODAL_BLOCK_ACCOUNT = 'BLOCK_ACCOUNT'
export const MODAL_BLOCK_DOMAIN = 'BLOCK_DOMAIN'
export const MODAL_BOOST = 'BOOST'
export const MODAL_COMMUNITY_TIMELINE_SETTINGS = 'COMMUNITY_TIMELINE_SETTINGS'
export const MODAL_COMPOSE = 'COMPOSE'
export const MODAL_CONFIRM = 'CONFIRM'
export const MODAL_DISPLAY_OPTIONS = 'DISPLAY_OPTIONS'
export const MODAL_EDIT_PROFILE = 'EDIT_PROFILE'
export const MODAL_EMBED = 'EMBED'
export const MODAL_GIF_PICKER = 'GIF_PICKER'
export const MODAL_GROUP_CREATE = 'GROUP_CREATE'
export const MODAL_GROUP_DELETE = 'GROUP_DELETE'
export const MODAL_GROUP_EDITOR = 'GROUP_EDITOR'
export const MODAL_HASHTAG_TIMELINE_SETTINGS = 'HASHTAG_TIMELINE_SETTINGS'
export const MODAL_HOME_TIMELINE_SETTINGS = 'HOME_TIMELINE_SETTINGS'
export const MODAL_HOTKEYS = 'HOTKEYS'
export const MODAL_LIST_CREATE = 'LIST_CREATE'
export const MODAL_LIST_DELETE = 'LIST_DELETE'
export const MODAL_LIST_EDITOR = 'LIST_EDITOR'
export const MODAL_LIST_TIMELINE_SETTINGS = 'LIST_TIMELINE_SETTINGS'
export const MODAL_MEDIA = 'MEDIA'
export const MODAL_MUTE = 'MUTE'
export const MODAL_PRO_UPGRADE = 'PRO_UPGRADE'
export const MODAL_REPORT = 'REPORT'
export const MODAL_STATUS_REVISIONS = 'STATUS_REVISIONS'
export const MODAL_UNAUTHORIZED = 'UNAUTHORIZED'
export const MODAL_UNFOLLOW = 'UNFOLLOW'
export const MODAL_VIDEO = 'VIDEO'

View File

@ -98,8 +98,12 @@ class AccountGallery extends ImmutablePureComponent {
> >
{ {
attachments.map((attachment) => ( attachments.map((attachment, i) => (
<MediaItem key={attachment.get('id')} attachment={attachment} /> <MediaItem
key={attachment.get('id')}
attachment={attachment}
account={account}
/>
)) ))
} }
@ -108,16 +112,16 @@ class AccountGallery extends ImmutablePureComponent {
<ColumnIndicator type='loading' /> <ColumnIndicator type='loading' />
} }
{ /* {
attachments.size === 0 && !isLoading && attachments.size === 0 &&
<ColumnIndicator type='empty' message={intl.formatMessage(messages.none)} /> <ColumnIndicator type='error' message={intl.formatMessage(messages.none)} />
*/ } }
</div>
{ {
hasMore && !(isLoading && attachments.size === 0) && hasMore && !(isLoading && attachments.size === 0) &&
<LoadMore visible={!isLoading} onClick={this.handleLoadOlder} /> <LoadMore visible={!isLoading} onClick={this.handleLoadOlder} />
} }
</div>
</Block> </Block>
) )
} }

View File

@ -1,70 +0,0 @@
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { FormattedMessage } from 'react-intl';
import { fetchReposts } from '../actions/interactions';
import { fetchStatus } from '../actions/statuses';
import { makeGetStatus } from '../selectors';
import Account from '../components/account';
import ColumnIndicator from '../components/column_indicator';
import ScrollableList from '../components/scrollable_list';
const mapStateToProps = (state, props) => {
const getStatus = makeGetStatus();
const status = getStatus(state, {
id: props.params.statusId,
username: props.params.username,
});
return {
status,
accountIds: state.getIn(['user_lists', 'favorites', props.params.statusId]),
};
};
export default
@connect(mapStateToProps)
class Favorites extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
accountIds: ImmutablePropTypes.list,
status: ImmutablePropTypes.map,
};
componentWillMount() {
this.props.dispatch(fetchReposts(this.props.params.statusId));
this.props.dispatch(fetchStatus(this.props.params.statusId));
}
componentWillReceiveProps(nextProps) {
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
this.props.dispatch(fetchReposts(nextProps.params.statusId));
this.props.dispatch(fetchStatus(nextProps.params.statusId));
}
}
render() {
const { accountIds, status } = this.props;
if (!accountIds) {
return <ColumnIndicator type='loading' />
} else if (!status) {
return <ColumnIndicator type='missing' />
}
return (
<ScrollableList
scrollKey='reposts'
emptyMessage={<FormattedMessage id='status.reposts.empty' defaultMessage='No one has reposted this gab yet. When someone does, they will show up here.' />}
>
{
accountIds.map(id =>
<Account key={id} id={id} />
)
}
</ScrollableList>
);
}
}

View File

@ -41,7 +41,7 @@ class Following extends ImmutablePureComponent {
hasMore: PropTypes.bool, hasMore: PropTypes.bool,
} }
componentWillMount() { componentDidMount() {
const { accountId } = this.props const { accountId } = this.props
if (!!accountId && accountId !== -1) { if (!!accountId && accountId !== -1) {
@ -67,7 +67,7 @@ class Following extends ImmutablePureComponent {
account, account,
accountIds, accountIds,
hasMore, hasMore,
intl intl,
} = this.props } = this.props
if (!account) return null if (!account) return null

View File

@ -2,12 +2,20 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import isObject from 'lodash.isobject' import isObject from 'lodash.isobject'
import { changeValue, submit, setUp, reset } from '../actions/group_editor' import {
import ColumnIndicator from '../components/column_indicator'; changeGroupTitle,
changeGroupDescription,
changeGroupCoverImage,
submit,
setGroup,
resetEditor,
} from '../actions/group_editor'
import ColumnIndicator from '../components/column_indicator'
import Button from '../components/button' import Button from '../components/button'
import Divider from '../components/divider' import Divider from '../components/divider'
import Input from '../components/input' import Input from '../components/input'
import Text from '../components/text' import Text from '../components/text'
import Form from '../components/form'
import Textarea from '../components/textarea' import Textarea from '../components/textarea'
import FileInput from '../components/file_input' import FileInput from '../components/file_input'
@ -18,6 +26,8 @@ const messages = defineMessages({
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' },
update: { id: 'groups.form.update', defaultMessage: 'Update group' }, update: { id: 'groups.form.update', defaultMessage: 'Update group' },
titlePlaceholder: { id: 'groups.form.title_placeholder', defaultMessage: 'New group title...' },
descriptionPlaceholder: { id: 'groups.form.description_placeholder', defaultMessage: 'Some group description...' },
}) })
const mapStateToProps = (state, { params }) => { const mapStateToProps = (state, { params }) => {
@ -27,26 +37,37 @@ const mapStateToProps = (state, { params }) => {
return { return {
group, group,
error: groupId && !group, error: groupId && !group,
title: state.getIn(['group_editor', 'title']), titleValue: state.getIn(['group_editor', 'title']),
description: state.getIn(['group_editor', 'description']), descriptionValue: state.getIn(['group_editor', 'description']),
coverImage: state.getIn(['group_editor', 'coverImage']), coverImage: state.getIn(['group_editor', 'coverImage']),
disabled: state.getIn(['group_editor', 'isSubmitting']), isSubmitting: state.getIn(['group_editor', 'isSubmitting']),
} }
} }
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
onTitleChange: value => dispatch(changeValue('title', value)), onTitleChange: (value) => {
onDescriptionChange: value => dispatch(changeValue('description', value)), dispatch(changeGroupTitle(value))
onCoverImageChange: value => dispatch(changeValue('coverImage', value)), },
onSubmit: routerHistory => dispatch(submit(routerHistory)), onDescriptionChange: (value) => {
reset: () => dispatch(reset()), dispatch(changeGroupDescription(value))
setUp: group => dispatch(setUp(group)), },
onCoverImageChange: (imageData) => {
console.log("imageData:", imageData)
dispatch(changeGroupCoverImage(imageData))
},
onResetEditor: () => {
dispatch(resetEditor())
},
onSetGroup: (group) => {
dispatch(setGroup(group))
},
onSubmit: (routerHistory) => dispatch(submit(routerHistory)),
}) })
export default export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl @injectIntl
class Create extends ImmutablePureComponent { @connect(mapStateToProps, mapDispatchToProps)
class GroupCreate extends ImmutablePureComponent {
static contextTypes = { static contextTypes = {
router: PropTypes.object router: PropTypes.object
@ -54,43 +75,45 @@ class Create extends ImmutablePureComponent {
static propTypes = { static propTypes = {
group: ImmutablePropTypes.map, group: ImmutablePropTypes.map,
title: PropTypes.string.isRequired, titleValue: PropTypes.string.isRequired,
description: PropTypes.string.isRequired, descriptionValue: PropTypes.string.isRequired,
coverImage: PropTypes.object, coverImage: PropTypes.object,
disabled: PropTypes.bool,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
onTitleChange: PropTypes.func.isRequired, onTitleChange: PropTypes.func.isRequired,
onDescriptionChange: PropTypes.func.isRequired,
onResetEditor: PropTypes.func.isRequired,
onSetGroup: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,
onCloseModal: PropTypes.func, isSubmitting: PropTypes.bool,
} }
componentWillMount() { updateOnProps = [
'group',
'titleValue',
'descriptionValue',
'coverImage',
'isSubmitting',
]
componentDidMount() {
if (!this.props.group) { if (!this.props.group) {
this.props.reset() this.props.onResetEditor()
} else { } else {
this.props.setUp(this.props.group) this.props.onSetGroup(this.props.group)
} }
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (this.props.group !== nextProps.group && !!nextProps.group) { if (this.props.group !== nextProps.group && !!nextProps.group) {
this.props.setUp(nextProps.group) this.props.onSetGroup(nextProps.group)
} }
} }
handleTitleChange = e => { handleCoverImageChange = (e) => {
this.props.onTitleChange(e.target.value)
}
handleDescriptionChange = e => {
this.props.onDescriptionChange(e.target.value)
}
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)
} }
@ -99,11 +122,14 @@ class Create extends ImmutablePureComponent {
const { const {
group, group,
error, error,
title, titleValue,
description, descriptionValue,
coverImage, coverImage,
disabled, intl,
intl onTitleChange,
onDescriptionChange,
isSubmitting,
onSubmit,
} = this.props } = this.props
if (!group && error) { if (!group && error) {
@ -111,30 +137,30 @@ class Create extends ImmutablePureComponent {
} }
return ( return (
<form onSubmit={this.handleSubmit}> <Form onSubmit={onSubmit}>
<Input <Input
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
value={title} value={titleValue}
disabled={disabled} onChange={onTitleChange}
onChange={this.handleTitleChange} disabled={isSubmitting}
placeholder={'New group title...'} placeholder={intl.formatMessage(messages.titlePlaceholder)}
/> />
<Divider isInvisible /> <Divider isInvisible />
<Textarea <Textarea
title={intl.formatMessage(messages.description)} title={intl.formatMessage(messages.description)}
value={description} value={descriptionValue}
disabled={disabled} onChange={onDescriptionChange}
onChange={this.handleDescriptionChange} placeholder={intl.formatMessage(messages.descriptionPlaceholder)}
placeholder={'Some group description...'} disabled={isSubmitting}
/> />
<Divider isInvisible /> <Divider isInvisible />
<FileInput <FileInput
disabled={isSubmitting}
title={intl.formatMessage(coverImage === null ? messages.coverImage : messages.coverImageChange)} title={intl.formatMessage(coverImage === null ? messages.coverImage : messages.coverImageChange)}
disabled={disabled}
onChange={this.handleCoverImageChange} onChange={this.handleCoverImageChange}
width='340px' width='340px'
height='145px' height='145px'
@ -142,13 +168,16 @@ class Create extends ImmutablePureComponent {
<Divider isInvisible /> <Divider isInvisible />
<Button className={_s.ml10}> <Button
<Text color='white'> isDisabled={!titleValue || !descriptionValue && !isSubmitting}
onClick={this.handleSubmit}
>
<Text color='inherit' align='center'>
{intl.formatMessage(!!group ? messages.update : messages.create)} {intl.formatMessage(!!group ? messages.update : messages.create)}
</Text> </Text>
</Button> </Button>
</form> </Form>
) )
} }

View File

@ -85,7 +85,7 @@ class GroupTimeline extends ImmutablePureComponent {
} }
return ( return (
<StatusListContainer <StatusList
alwaysPrepend alwaysPrepend
scrollKey={`group_timeline-${columnId}`} scrollKey={`group_timeline-${columnId}`}
timelineId={`group:${id}`} timelineId={`group:${id}`}

View File

@ -1,12 +1,17 @@
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import { changeListEditorTitle, submitListEditor } from '../actions/lists' import { changeListEditorTitle, submitListEditor } from '../actions/lists'
import { closeModal } from '../actions/modal'
import { MODAL_LIST_CREATE } from '../constants'
import Button from '../components/button' import Button from '../components/button'
import Input from '../components/input' import Input from '../components/input'
import Form from '../components/form'
import Text from '../components/text' import Text from '../components/text'
const messages = defineMessages({ const messages = defineMessages({
label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' }, label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' },
create: { id: 'lists.new.create_title', defaultMessage: 'Create' }, create: { id: 'lists.new.create_title', defaultMessage: 'Create list' },
list_description: { id: 'list.description', defaultMessage: 'Lists are private and only you can see who is on a list.\nNo one else can view your lists. No one knows that they are on your list.' },
new_list_placeholder: { id: 'list.title_placeholder', defaultMessage: 'My new list...', },
}) })
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
@ -14,9 +19,12 @@ const mapStateToProps = (state) => ({
disabled: state.getIn(['listEditor', 'isSubmitting']), disabled: state.getIn(['listEditor', 'isSubmitting']),
}) })
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch, { isModal }) => ({
onChange: value => dispatch(changeListEditorTitle(value)), onChange: (value) => dispatch(changeListEditorTitle(value)),
onSubmit: () => dispatch(submitListEditor(true)), onSubmit: () => {
if (isModal) dispatch(closeModal(MODAL_LIST_CREATE))
dispatch(submitListEditor(true))
},
}) })
export default export default
@ -25,49 +33,47 @@ export default
class ListCreate extends PureComponent { class ListCreate extends PureComponent {
static propTypes = { static propTypes = {
value: PropTypes.string.isRequired, value: PropTypes.string,
disabled: PropTypes.bool,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,
isModal: PropTypes.bool,
} }
render() { render() {
const { const {
value, value,
disabled,
intl, intl,
onSubmit, onSubmit,
onChange onChange,
} = this.props } = this.props
const isDisabled = !value
return ( return (
<form onSubmit={onSubmit}> <Form>
<Input <Input
title={intl.formatMessage(messages.label)} title={intl.formatMessage(messages.label)}
placeholder='My new list...' placeholder={intl.formatMessage(messages.new_list_placeholder)}
value={value} value={value}
onChange={onChange} onChange={onChange}
onSubmit={onSubmit}
disabled={disabled}
/> />
<div className={[_s.default, _s.my10, _s.py5, _s.ml10].join(' ')}> <div className={[_s.default, _s.my10, _s.py5, _s.ml10].join(' ')}>
<Text color='secondary' size='small'> <Text color='secondary' size='small'>
Lists are private and only you can see who is on a list.<br/> {intl.formatMessage(messages.list_description)}
No one else can view your lists. No one knows that they are on your list.
</Text> </Text>
</div> </div>
<Button <Button
className={_s.ml10} isDisabled={isDisabled}
type='submit' onClick={onSubmit}
> >
<Text color='white'> <Text color='inherit' align='center'>
{intl.formatMessage(messages.create)} {intl.formatMessage(messages.create)}
</Text> </Text>
</Button> </Button>
</form> </Form>
) )
} }

View File

@ -12,9 +12,9 @@ const mapStateToProps = (state) => ({
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
onSubmit: value => dispatch(fetchListSuggestions(value)), onSubmit: (value) => dispatch(fetchListSuggestions(value)),
onClear: () => dispatch(clearListSuggestions()), onClear: () => dispatch(clearListSuggestions()),
onChange: value => dispatch(changeListSuggestions(value)), onChange: (value) => dispatch(changeListSuggestions(value)),
}); });
export default export default

View File

@ -7,6 +7,7 @@ import Account from './components/account'
import ListEditorSearch from './components/list_editor_search' import ListEditorSearch from './components/list_editor_search'
import EditListForm from './components/edit_list_form/edit_list_form' import EditListForm from './components/edit_list_form/edit_list_form'
import Button from '../../components/button' import Button from '../../components/button'
import Form from '../../components/form'
import Input from '../../components/input' import Input from '../../components/input'
const mapStateToProps = (state, { params }) => { const mapStateToProps = (state, { params }) => {
@ -24,7 +25,7 @@ const mapStateToProps = (state, { params }) => {
} }
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
onInitialize: listId => dispatch(setupListEditor(listId)), onInitialize: (listId) => dispatch(setupListEditor(listId)),
onReset: () => dispatch(resetListEditor()), onReset: () => dispatch(resetListEditor()),
}) })
@ -80,7 +81,7 @@ class ListEdit extends ImmutablePureComponent {
console.log("title:", title) console.log("title:", title)
return ( return (
<div> <Form>
<Input <Input
title={intl.formatMessage(messages.editListTitle)} title={intl.formatMessage(messages.editListTitle)}
placeholder='My new list title...' placeholder='My new list title...'
@ -90,8 +91,6 @@ class ListEdit extends ImmutablePureComponent {
// disabled={disabled} // disabled={disabled}
/> />
{
/*
<div className='compose-modal__header'> <div className='compose-modal__header'>
<h3 className='compose-modal__header__title'> <h3 className='compose-modal__header__title'>
{intl.formatMessage(messages.editList)} {intl.formatMessage(messages.editList)}
@ -119,9 +118,8 @@ class ListEdit extends ImmutablePureComponent {
</div> </div>
</div> </div>
</div> </div>
*/ }
</div> </Form>
) )
} }

View File

@ -1,26 +1,22 @@
import ImmutablePropTypes from 'react-immutable-proptypes'; import { Fragment } from 'react'
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePropTypes from 'react-immutable-proptypes'
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'
import { connectListStream } from '../actions/streaming'; import { FormattedMessage } from 'react-intl'
import { expandListTimeline } from '../actions/timelines'; import { connectListStream } from '../actions/streaming'
import { fetchList, deleteList } from '../actions/lists'; import { expandListTimeline } from '../actions/timelines'
import { openModal } from '../actions/modal'; import { fetchList, deleteList } from '../actions/lists'
import StatusList from '../components/status_list'; import { openModal } from '../actions/modal'
import ColumnIndicator from '../components/column_indicator'; import StatusList from '../components/status_list'
import Button from '../components/button'; import ColumnIndicator from '../components/column_indicator'
import Button from '../components/button'
const messages = defineMessages({ import Text from '../components/text'
deleteMessage: { id: 'confirmations.delete_list.message', defaultMessage: 'Are you sure you want to permanently delete this list?' },
deleteConfirm: { id: 'confirmations.delete_list.confirm', defaultMessage: 'Delete' },
});
const mapStateToProps = (state, props) => ({ const mapStateToProps = (state, props) => ({
list: state.getIn(['lists', props.params.id]), list: state.getIn(['lists', props.params.id]),
}); })
export default export default
@connect(mapStateToProps) @connect(mapStateToProps)
@injectIntl
class ListTimeline extends ImmutablePureComponent { class ListTimeline extends ImmutablePureComponent {
static contextTypes = { static contextTypes = {
@ -35,84 +31,76 @@ class ListTimeline extends ImmutablePureComponent {
PropTypes.bool, PropTypes.bool,
]), ]),
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; }
componentDidMount() { componentDidMount() {
this.handleConnect(this.props.params.id); this.handleConnect(this.props.params.id)
} }
componentWillUnmount() { componentWillUnmount() {
this.handleDisconnect(); this.handleDisconnect()
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (nextProps.params.id !== this.props.params.id) { if (nextProps.params.id !== this.props.params.id) {
this.handleDisconnect(); this.handleDisconnect()
this.handleConnect(nextProps.params.id); this.handleConnect(nextProps.params.id)
} }
} }
handleConnect(id) { handleConnect(id) {
const { dispatch } = this.props; const { dispatch } = this.props
dispatch(fetchList(id)); dispatch(fetchList(id))
dispatch(expandListTimeline(id)); dispatch(expandListTimeline(id))
this.disconnect = dispatch(connectListStream(id)); this.disconnect = dispatch(connectListStream(id))
} }
handleDisconnect() { handleDisconnect() {
if (this.disconnect) { if (this.disconnect) {
this.disconnect(); this.disconnect()
this.disconnect = null; this.disconnect = null
} }
} }
handleLoadMore = maxId => { handleLoadMore = (maxId) => {
const { id } = this.props.params; const { id } = this.props.params
this.props.dispatch(expandListTimeline(id, { maxId })); this.props.dispatch(expandListTimeline(id, { maxId }))
} }
handleEditClick = () => { handleEditClick = () => {
this.props.dispatch(openModal('LIST_EDITOR', { listId: this.props.params.id })); this.props.dispatch(openModal('LIST_EDITOR', { listId: this.props.params.id }))
}
handleDeleteClick = () => {
const { dispatch, intl } = this.props;
const { id } = this.props.params;
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.deleteMessage),
confirm: intl.formatMessage(messages.deleteConfirm),
onConfirm: () => {
dispatch(deleteList(id));
this.context.router.history.push('/lists');
},
}));
} }
render() { render() {
const { list } = this.props; const { list } = this.props
const { id } = this.props.params; const { id } = this.props.params
const title = list ? list.get('title') : id; const title = list ? list.get('title') : id
if (typeof list === 'undefined') { if (typeof list === 'undefined') {
return (<ColumnIndicator type='loading' />); return <ColumnIndicator type='loading' />
} else if (list === false) { } else if (list === false) {
return (<ColumnIndicator type='missing' />); return <ColumnIndicator type='missing' />
} }
const emptyMessage = ( const emptyMessage = (
<div> <div className={[_s.default].join(' ')}>
<FormattedMessage <FormattedMessage
id='empty_column.list' id='empty_column.list'
defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' /> defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.'
<br/><br/> />
<Button onClick={this.handleEditClick}>
<Button
onClick={this.handleEditClick}
className={[_s.mt10]}
>
<Text color='inherit'>
<FormattedMessage id='list.click_to_add' defaultMessage='Click here to add people' /> <FormattedMessage id='list.click_to_add' defaultMessage='Click here to add people' />
</Text>
</Button> </Button>
</div> </div>
); )
return ( return (
<StatusList <StatusList
@ -121,7 +109,7 @@ class ListTimeline extends ImmutablePureComponent {
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
emptyMessage={emptyMessage} emptyMessage={emptyMessage}
/> />
); )
} }
} }

View File

@ -53,6 +53,7 @@ class Notifications extends ImmutablePureComponent {
} }
handleLoadGap = (maxId) => { handleLoadGap = (maxId) => {
// maxId={index > 0 ? notifications.getIn([index - 1, 'id']) : null}
this.props.dispatch(expandNotifications({ maxId })) this.props.dispatch(expandNotifications({ maxId }))
} }
@ -122,13 +123,7 @@ class Notifications extends ImmutablePureComponent {
scrollableContent = this.scrollableContent scrollableContent = this.scrollableContent
} else if (notifications.size > 0 || hasMore) { } else if (notifications.size > 0 || hasMore) {
scrollableContent = notifications.map((item, index) => item === null ? ( scrollableContent = notifications.map((item, index) => item === null ? (
<LoadMore <LoadMore disabled={isLoading} onClick={this.handleLoadGap} />
gap
key={'gap:' + notifications.getIn([index + 1, 'id'])}
disabled={isLoading}
maxId={index > 0 ? notifications.getIn([index - 1, 'id']) : null}
onClick={this.handleLoadGap}
/>
) : ( ) : (
<NotificationContainer <NotificationContainer
key={`notification-${index}`} key={`notification-${index}`}

View File

@ -0,0 +1,72 @@
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ColumnIndicator from '../components/column_indicator'
import { fetchLikes } from '../actions/interactions'
import Account from '../components/account'
import ScrollableList from '../components/scrollable_list'
const messages = defineMessages({
refresh: { id: 'refresh', defaultMessage: 'Refresh' },
});
const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
});
export default
@injectIntl
@connect(mapStateToProps)
class StatusLikes extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
multiColumn: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
componentWillMount () {
if (!this.props.accountIds) {
this.props.dispatch(fetchLikes(this.props.params.statusId));
}
}
componentWillReceiveProps (nextProps) {
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
this.props.dispatch(fetchLikes(nextProps.params.statusId));
}
}
handleRefresh = () => {
this.props.dispatch(fetchLikes(this.props.params.statusId));
}
render () {
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
if (!accountIds) {
return <ColumnIndicator type='loading' />
}
const emptyMessage = <FormattedMessage id='empty_column.favourites' defaultMessage='No one has favourited this toot yet. When someone does, they will show up here.' />;
return (
<div>
<ScrollableList
scrollKey='favourites'
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
>
{accountIds.map(id =>
<Account key={id} id={id} withNote={false} />,
)}
</ScrollableList>
</div>
);
}
}

View File

@ -23,7 +23,7 @@ const mapStateToProps = (state, props) => {
export default export default
@connect(mapStateToProps) @connect(mapStateToProps)
class Reposts extends ImmutablePureComponent { class StatusReposts extends ImmutablePureComponent {
static propTypes = { static propTypes = {
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,

View File

@ -6,6 +6,7 @@ import { Switch, Redirect, withRouter } from 'react-router-dom'
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import { uploadCompose, resetCompose } from '../../actions/compose' import { uploadCompose, resetCompose } from '../../actions/compose'
import { expandHomeTimeline } from '../../actions/timelines' import { expandHomeTimeline } from '../../actions/timelines'
import { fetchGroups } from '../../actions/groups'
import { import {
initializeNotifications, initializeNotifications,
expandNotifications, expandNotifications,
@ -40,7 +41,6 @@ import {
BlockedAccounts, BlockedAccounts,
BlockedDomains, BlockedDomains,
CommunityTimeline, CommunityTimeline,
// Favorites,
// Filters, // Filters,
Followers, Followers,
Following, Following,
@ -61,17 +61,17 @@ import {
ListTimeline, ListTimeline,
Mutes, Mutes,
Notifications, Notifications,
Reposts,
Search, Search,
// Shortcuts, // Shortcuts,
Status, Status,
StatusLikes,
StatusReposts,
} from './util/async_components' } from './util/async_components'
import { me, meUsername } from '../../initial_state' import { me, meUsername } from '../../initial_state'
// Dummy import, to make sure that <Status /> ends up in the application bundle. // Dummy import, to make sure that <Status /> ends up in the application bundle.
// Without this it ends up in ~8 very commonly used bundles. // Without this it ends up in ~8 very commonly used bundles.
import '../../components/status' import '../../components/status'
import { fetchGroups } from '../../actions/groups'
const messages = defineMessages({ const messages = defineMessages({
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Gab Social.' }, beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Gab Social.' },
@ -116,7 +116,7 @@ class SwitchingArea extends PureComponent {
onLayoutChange: PropTypes.func.isRequired, onLayoutChange: PropTypes.func.isRequired,
} }
componentWillMount() { componentDidMount() {
window.addEventListener('resize', this.handleResize, { window.addEventListener('resize', this.handleResize, {
passive: true passive: true
}) })
@ -209,10 +209,10 @@ class SwitchingArea extends PureComponent {
<WrappedRoute path='/:username/posts/:statusId' publicRoute exact page={BasicPage} component={Status} content={children} componentParams={{ title: 'Status' }} /> <WrappedRoute path='/:username/posts/:statusId' publicRoute exact page={BasicPage} component={Status} content={children} componentParams={{ title: 'Status' }} />
<Redirect from='/@:username/posts/:statusId/reposts' to='/:username/posts/:statusId/reposts' /> <Redirect from='/@:username/posts/:statusId/reposts' to='/:username/posts/:statusId/reposts' />
<WrappedRoute path='/:username/posts/:statusId/reposts' page={BasicPage} component={Reposts} content={children} componentParams={{ title: 'Reposts' }} /> <WrappedRoute path='/:username/posts/:statusId/reposts' page={BasicPage} component={StatusReposts} content={children} componentParams={{ title: 'Reposts' }} />
{ /* <Redirect from='/@:username/posts/:statusId/favorites' to='/:username/posts/:statusId/favorites' /> <Redirect from='/@:username/posts/:statusId/likes' to='/:username/posts/:statusId/likes' />
<WrappedRoute path='/:username/posts/:statusId/favorites' page={BasicPage} component={Favorites} content={children} componentParams={{ title: 'Favorites' }} /> */ } <WrappedRoute path='/:username/posts/:statusId/likes' page={BasicPage} component={StatusLikes} content={children} componentParams={{ title: 'Likes' }} />
<WrappedRoute page={ErrorPage} component={GenericNotFound} content={children} /> <WrappedRoute page={ErrorPage} component={GenericNotFound} content={children} />
</Switch> </Switch>
@ -349,7 +349,13 @@ class UI extends PureComponent {
} }
} }
componentWillMount() { // componentWillMount() {
// }
componentDidMount() {
// if (!me) return
window.addEventListener('beforeunload', this.handleBeforeUnload, false) window.addEventListener('beforeunload', this.handleBeforeUnload, false)
document.addEventListener('dragenter', this.handleDragEnter, false) document.addEventListener('dragenter', this.handleDragEnter, false)
@ -370,15 +376,9 @@ class UI extends PureComponent {
this.props.dispatch(expandHomeTimeline()) this.props.dispatch(expandHomeTimeline())
this.props.dispatch(expandNotifications()) this.props.dispatch(expandNotifications())
this.props.dispatch(initializeNotifications()) this.props.dispatch(initializeNotifications())
this.props.dispatch(fetchGroups('member'))
setTimeout(() => this.props.dispatch(fetchFilters()), 500) setTimeout(() => this.props.dispatch(fetchFilters()), 500)
} }
}
componentDidMount() {
if (!me) return
// this.hotkeys.__mousetrap__.stopCallback = (e, element) => { // this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
// return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName) // return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName)
// } // }
@ -435,7 +435,7 @@ class UI extends PureComponent {
} }
handleHotkeyToggleHelp = () => { handleHotkeyToggleHelp = () => {
this.props.dispatch(openModal("HOTKEYS")) this.props.dispatch(openModal('HOTKEYS'))
} }
handleHotkeyGoToHome = () => { handleHotkeyGoToHome = () => {
@ -471,7 +471,7 @@ class UI extends PureComponent {
} }
handleOpenComposeModal = () => { handleOpenComposeModal = () => {
this.props.dispatch(openModal("COMPOSE")) this.props.dispatch(openModal('COMPOSE'))
} }
render() { render() {

View File

@ -1,34 +1,74 @@
export function AccountTimeline() { return import(/* webpackChunkName: "features/account_timeline" */'../../account_timeline') } export function AccountTimeline() { return import(/* webpackChunkName: "features/account_timeline" */'../../account_timeline') }
export function AccountGallery() { return import(/* webpackChunkName: "features/account_gallery" */'../../account_gallery') } export function AccountGallery() { return import(/* webpackChunkName: "features/account_gallery" */'../../account_gallery') }
export function ActionsModal() { return import(/* webpackChunkName: "components/actions_modal" */'../../../components/modal/actions_modal') }
export function BlockAccountModal() { return import(/* webpackChunkName: "components/block_account_modal" */'../../../components/modal/block_account_modal') }
export function BlockDomainModal() { return import(/* webpackChunkName: "components/block_domain_modal" */'../../../components/modal/block_domain_modal') }
export function BlockedAccounts() { return import(/* webpackChunkName: "features/blocked_accounts" */'../../blocked_accounts') } export function BlockedAccounts() { return import(/* webpackChunkName: "features/blocked_accounts" */'../../blocked_accounts') }
export function BlockedDomains() { return import(/* webpackChunkName: "features/blocked_domains" */'../../blocked_domains') } export function BlockedDomains() { return import(/* webpackChunkName: "features/blocked_domains" */'../../blocked_domains') }
export function BoostModal() { return import(/* webpackChunkName: "components/boost_modal" */'../../../components/modal/boost_modal') }
export function CommunityTimeline() { return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline') } export function CommunityTimeline() { return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline') }
export function CommunityTimelineSettingsModal() { return import(/* webpackChunkName: "components/community_timeline_settings_modal" */'../../../components/modal/community_timeline_settings_modal') }
export function Compose() { return import(/* webpackChunkName: "features/compose" */'../../compose') } export function Compose() { return import(/* webpackChunkName: "features/compose" */'../../compose') }
export function ComposeForm() { return import(/* webpackChunkName: "components/compose_form" */'../../compose/components/compose_form') }
export function ComposeModal() { return import(/* webpackChunkName: "components/compose_modal" */'../../../components/modal/compose_modal') }
export function ConfirmationModal() { return import(/* webpackChunkName: "components/confirmation_modal" */'../../../components/modal/confirmation_modal') }
export function ContentWarningPopover() { return import(/* webpackChunkName: "components/content_warning_popover" */'../../../components/popover/content_warning_popover') }
export function DatePickerPopover() { return import(/* webpackChunkName: "components/date_picker_popover" */'../../../components/popover/date_picker_popover') }
export function DisplayOptionsModal() { return import(/* webpackChunkName: "components/display_options_modal" */'../../../components/modal/display_options_modal') }
export function EditProfileModal() { return import(/* webpackChunkName: "components/edit_profile_modal" */'../../../components/modal/edit_profile_modal') }
export function EmbedModal() { return import(/* webpackChunkName: "modals/embed_modal" */'../../../components/modal/embed_modal') } export function EmbedModal() { return import(/* webpackChunkName: "modals/embed_modal" */'../../../components/modal/embed_modal') }
export function EmojiPicker() { return import(/* webpackChunkName: "emoji_picker" */'../../../components/emoji/emoji_picker') } export function EmojiPicker() { return import(/* webpackChunkName: "emoji_picker" */'../../../components/emoji/emoji_picker') }
export function EmojiPickerPopover() { return import(/* webpackChunkName: "components/emoji_picker_popover" */'../../../components/popover/emoji_picker_popover') }
export function Followers() { return import(/* webpackChunkName: "features/followers" */'../../followers') } export function Followers() { return import(/* webpackChunkName: "features/followers" */'../../followers') }
export function Following() { return import(/* webpackChunkName: "features/following" */'../../following') } export function Following() { return import(/* webpackChunkName: "features/following" */'../../following') }
export function FollowRequests() { return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests') } export function FollowRequests() { return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests') }
export function LikedStatuses() { return import(/* webpackChunkName: "features/liked_statuses" */'../../liked_statuses') } export function LikedStatuses() { return import(/* webpackChunkName: "features/liked_statuses" */'../../liked_statuses') }
export function GenericNotFound() { return import(/* webpackChunkName: "features/generic_not_found" */'../../generic_not_found') } export function GenericNotFound() { return import(/* webpackChunkName: "features/generic_not_found" */'../../generic_not_found') }
export function GifPickerModal() { return import(/* webpackChunkName: "components/gif_picker_modal" */'../../../components/modal/gif_picker_modal') }
export function GroupsCollection() { return import(/* webpackChunkName: "features/groups_collection" */'../../groups_collection') } export function GroupsCollection() { return import(/* webpackChunkName: "features/groups_collection" */'../../groups_collection') }
export function GroupCreate() { return import(/* webpackChunkName: "features/group_create" */'../../group_create') } export function GroupCreate() { return import(/* webpackChunkName: "features/group_create" */'../../group_create') }
export function GroupCreateModal() { return import(/* webpackChunkName: "components/group_create_modal" */'../../../components/modal/group_create_modal') }
export function GroupDeleteModal() { return import(/* webpackChunkName: "components/group_delete_modal" */'../../../components/modal/group_delete_modal') }
export function GroupEditorModal() { return import(/* webpackChunkName: "components/group_editor_modal" */'../../../components/modal/group_editor_modal') }
export function GroupInfoPopover() { return import(/* webpackChunkName: "components/group_info_popover" */'../../../components/popover/group_info_popover') }
export function GroupMembers() { return import(/* webpackChunkName: "features/group_members" */'../../group_members') } export function GroupMembers() { return import(/* webpackChunkName: "features/group_members" */'../../group_members') }
export function GroupRemovedAccounts() { return import(/* webpackChunkName: "features/group_removed_accounts" */'../../group_removed_accounts') } export function GroupRemovedAccounts() { return import(/* webpackChunkName: "features/group_removed_accounts" */'../../group_removed_accounts') }
export function GroupTimeline() { return import(/* webpackChunkName: "features/group_timeline" */'../../group_timeline') } export function GroupTimeline() { return import(/* webpackChunkName: "features/group_timeline" */'../../group_timeline') }
export function HashtagTimeline() { return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline') } export function HashtagTimeline() { return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline') }
export function HashtagTimelineSettingsModal() { return import(/* webpackChunkName: "components/hashtag_timeline_settings_modal" */'../../../components/modal/hashtag_timeline_settings_modal') }
export function HomeTimeline() { return import(/* webpackChunkName: "features/home_timeline" */'../../home_timeline') } export function HomeTimeline() { return import(/* webpackChunkName: "features/home_timeline" */'../../home_timeline') }
export function HomeTimelineSettingsModal() { return import(/* webpackChunkName: "components/home_timeline_settings_modal" */'../../../components/modal/home_timeline_settings_modal') }
export function HotkeysModal() { return import(/* webpackChunkName: "components/hotkeys_modal" */'../../../components/modal/hotkeys_modal') }
export function ListCreate() { return import(/* webpackChunkName: "features/list_create" */'../../list_create') } export function ListCreate() { return import(/* webpackChunkName: "features/list_create" */'../../list_create') }
export function ListCreateModal() { return import(/* webpackChunkName: "components/list_create_modal" */'../../../components/modal/list_create_modal') }
export function ListDeleteModal() { return import(/* webpackChunkName: "components/list_delete_modal" */'../../../components/modal/list_delete_modal') }
export function ListsDirectory() { return import(/* webpackChunkName: "features/lists_directory" */'../../lists_directory') } export function ListsDirectory() { return import(/* webpackChunkName: "features/lists_directory" */'../../lists_directory') }
export function ListEdit() { return import(/* webpackChunkName: "features/list_editor" */'../../list_edit') } export function ListEdit() { return import(/* webpackChunkName: "features/list_editor" */'../../list_edit') }
export function ListEditorModal() { return import(/* webpackChunkName: "components/list_editor_modal" */'../../../components/modal/list_editor_modal') }
export function ListTimeline() { return import(/* webpackChunkName: "features/list_timeline" */'../../list_timeline') } export function ListTimeline() { return import(/* webpackChunkName: "features/list_timeline" */'../../list_timeline') }
export function ListTimelineSettingsModal() { return import(/* webpackChunkName: "components/list_timeline_settings_modal" */'../../../components/modal/list_timeline_settings_modal') }
export function MediaGallery() { return import(/* webpackChunkName: "components/media_gallery" */'../../../components/media_gallery') } export function MediaGallery() { return import(/* webpackChunkName: "components/media_gallery" */'../../../components/media_gallery') }
export function MediaModal() { return import(/* webpackChunkName: "components/media_modal" */'../../../components/modal/media_modal') }
export function ModalLoading() { return import(/* webpackChunkName: "components/modal_loading" */'../../../components/modal/modal_loading') }
export function Mutes() { return import(/* webpackChunkName: "features/mutes" */'../../mutes') } export function Mutes() { return import(/* webpackChunkName: "features/mutes" */'../../mutes') }
export function MuteModal() { return import(/* webpackChunkName: "modals/mute_modal" */'../../../components/modal/mute_modal') } export function MuteModal() { return import(/* webpackChunkName: "modals/mute_modal" */'../../../components/modal/mute_modal') }
export function Notifications() { return import(/* webpackChunkName: "features/notifications" */'../../notifications') } export function Notifications() { return import(/* webpackChunkName: "features/notifications" */'../../notifications') }
export function Reposts() { return import(/* webpackChunkName: "features/reposts" */'../../reposts') } export function ProfileOptionsPopover() { return import(/* webpackChunkName: "components/profile_options_popover" */'../../../components/popover/profile_options_popover') }
export function ProUpgradeModal() { return import(/* webpackChunkName: "components/pro_upgrade_modal" */'../../../components/modal/pro_upgrade_modal') }
export function ReportModal() { return import(/* webpackChunkName: "modals/report_modal" */'../../../components/modal/report_modal') } export function ReportModal() { return import(/* webpackChunkName: "modals/report_modal" */'../../../components/modal/report_modal') }
export function RepostOptionsPopover() { return import(/* webpackChunkName: "components/repost_options_popover" */'../../../components/popover/repost_options_popover') }
export function Search() { return import(/*webpackChunkName: "features/search" */'../../search') } export function Search() { return import(/*webpackChunkName: "features/search" */'../../search') }
export function Status() { return import(/* webpackChunkName: "components/status" */'../../../components/status') } export function Status() { return import(/* webpackChunkName: "components/status" */'../../../components/status') }
export function SearchPopover() { return import(/* webpackChunkName: "components/search_popover" */'../../../components/popover/search_popover') }
export function SidebarMorePopover() { return import(/* webpackChunkName: "components/sidebar_more_popover" */'../../../components/popover/sidebar_more_popover') }
export function StatusLikes() { return import(/* webpackChunkName: "features/status_likes" */'../../status_likes') }
export function StatusOptionsPopover() { return import(/* webpackChunkName: "components/status_options_popover" */'../../../components/popover/status_options_popover') }
export function StatusReposts() { return import(/* webpackChunkName: "features/status_reposts" */'../../status_reposts') }
export function StatusRevisionsModal() { return import(/* webpackChunkName: "modals/status_revisions_modal" */'../../../components/modal/status_revisions_modal') } export function StatusRevisionsModal() { return import(/* webpackChunkName: "modals/status_revisions_modal" */'../../../components/modal/status_revisions_modal') }
export function StatusSharePopover() { return import(/* webpackChunkName: "components/status_share_popover" */'../../../components/popover/status_share_popover') }
export function StatusVisibilityPopover() { return import(/* webpackChunkName: "components/status_visibility_popover" */'../../../components/popover/status_visibility_popover') }
export function UnauthorizedModal() { return import(/* webpackChunkName: "components/unauthorized_modal" */'../../../components/modal/unauthorized_modal') }
export function UnfollowModal() { return import(/* webpackChunkName: "components/unfollow_modal" */'../../../components/modal/unfollow_modal') }
export function UserInfoPopover() { return import(/* webpackChunkName: "components/user_info_popover" */'../../../components/popover/user_info_popover') }
export function Video() { return import(/* webpackChunkName: "components/video" */'../../../components/video') } export function Video() { return import(/* webpackChunkName: "components/video" */'../../../components/video') }
export function VideoModal() { return import(/* webpackChunkName: "components/video_modal" */'../../../components/modal/video_modal') }

View File

@ -1,7 +1,7 @@
import { import {
fetchBundleRequest, fetchBundleRequest,
fetchBundleSuccess, fetchBundleSuccess,
fetchBundleFail fetchBundleFail,
} from '../../../actions/bundles' } from '../../../actions/bundles'
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({

View File

@ -1,3 +1,4 @@
import api from '../../../api'
import { Route } from 'react-router-dom' import { Route } from 'react-router-dom'
import BundleColumnError from '../../../components/bundle_column_error' import BundleColumnError from '../../../components/bundle_column_error'
import Bundle from './bundle' import Bundle from './bundle'
@ -51,6 +52,15 @@ export default class WrappedRoute extends PureComponent {
...rest ...rest
} = this.props } = this.props
// : todo :
// api().get('/api/v1/accounts/verify_credentials')
// .then((res) => {
// console.log("res:", res)
// })
// .catch((err) => {
// console.log("err:", err)
// })
if (!publicRoute && !me) { if (!publicRoute && !me) {
const actualUrl = encodeURIComponent(this.props.computedMatch.url) const actualUrl = encodeURIComponent(this.props.computedMatch.url)
return <Route path={this.props.path} component={() => { return <Route path={this.props.path} component={() => {

View File

@ -32,7 +32,7 @@ export default class GroupLayout extends ImmutablePureComponent {
actions={actions} actions={actions}
showBackBtn={showBackBtn} showBackBtn={showBackBtn}
> >
<div className={[_s.default, _s.width1015PX, _s.pl15, _s.py15].join(' ')}> <div className={[_s.default, _s.width100PC].join(' ')}>
<GroupHeader group={group} relationships={relationships} /> <GroupHeader group={group} relationships={relationships} />

View File

@ -7,7 +7,6 @@ import LinkFooter from '../components/link_footer'
import WhoToFollowPanel from '../components/panel/who_to_follow_panel' import WhoToFollowPanel from '../components/panel/who_to_follow_panel'
import ProgressPanel from '../components/panel/progress_panel' import ProgressPanel from '../components/panel/progress_panel'
import TrendsPanel from '../components/panel/trends_panel' import TrendsPanel from '../components/panel/trends_panel'
import HashtagsPanel from '../components/panel/hashtags_panel'
import DefaultLayout from '../layouts/default_layout' import DefaultLayout from '../layouts/default_layout'
import TimelineComposeBlock from '../components/timeline_compose_block' import TimelineComposeBlock from '../components/timeline_compose_block'
import Divider from '../components/divider' import Divider from '../components/divider'
@ -51,7 +50,6 @@ class CommunityPage extends PureComponent {
<Fragment> <Fragment>
<ProgressPanel /> <ProgressPanel />
<TrendsPanel /> <TrendsPanel />
<HashtagsPanel />
<WhoToFollowPanel /> <WhoToFollowPanel />
<GroupSidebarPanel /> <GroupSidebarPanel />
<LinkFooter /> <LinkFooter />

View File

@ -92,7 +92,7 @@ class GroupPage extends ImmutablePureComponent {
<Fragment> <Fragment>
<GroupInfoPanel group={group} /> <GroupInfoPanel group={group} />
<WhoToFollowPanel /> <WhoToFollowPanel />
<GroupSidebarPanel slim /> <GroupSidebarPanel isSlim />
<LinkFooter /> <LinkFooter />
</Fragment> </Fragment>
)} )}

View File

@ -2,6 +2,7 @@ import { Fragment } from 'react'
import { me } from '../initial_state' import { me } from '../initial_state'
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import { openModal } from '../actions/modal' import { openModal } from '../actions/modal'
import { MODAL_GROUP_CREATE } from '../constants'
import PageTitle from '../features/ui/util/page_title' import PageTitle from '../features/ui/util/page_title'
import LinkFooter from '../components/link_footer' import LinkFooter from '../components/link_footer'
import GroupsPanel from '../components/panel/groups_panel' import GroupsPanel from '../components/panel/groups_panel'
@ -22,7 +23,7 @@ const mapStateToProps = (state) => ({
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
onOpenGroupCreateModal() { onOpenGroupCreateModal() {
dispatch(openModal('GROUP_CREATE')) dispatch(openModal(MODAL_GROUP_CREATE))
}, },
}) })
@ -81,6 +82,7 @@ class GroupsPage extends PureComponent {
showBackBtn showBackBtn
title={title} title={title}
actions={actions} actions={actions}
tabs={tabs}
layout={( layout={(
<Fragment> <Fragment>
<WhoToFollowPanel /> <WhoToFollowPanel />
@ -88,7 +90,6 @@ class GroupsPage extends PureComponent {
<LinkFooter /> <LinkFooter />
</Fragment> </Fragment>
)} )}
tabs={tabs}
> >
<PageTitle path={title} /> <PageTitle path={title} />
{ children } { children }

View File

@ -6,7 +6,6 @@ import LinkFooter from '../components/link_footer'
import WhoToFollowPanel from '../components/panel/who_to_follow_panel' import WhoToFollowPanel from '../components/panel/who_to_follow_panel'
import ProgressPanel from '../components/panel/progress_panel' import ProgressPanel from '../components/panel/progress_panel'
import TrendsPanel from '../components/panel/trends_panel' import TrendsPanel from '../components/panel/trends_panel'
import HashtagsPanel from '../components/panel/hashtags_panel'
import DefaultLayout from '../layouts/default_layout' import DefaultLayout from '../layouts/default_layout'
const messages = defineMessages({ const messages = defineMessages({
@ -53,7 +52,6 @@ class HashtagPage extends PureComponent {
<Fragment> <Fragment>
<ProgressPanel /> <ProgressPanel />
<TrendsPanel /> <TrendsPanel />
<HashtagsPanel />
<WhoToFollowPanel /> <WhoToFollowPanel />
<LinkFooter /> <LinkFooter />
</Fragment> </Fragment>

View File

@ -1,6 +1,8 @@
import { Fragment } from 'react' import { Fragment } from 'react'
import { openModal } from '../actions/modal' import { openModal } from '../actions/modal'
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import IntersectionObserverArticle from '../components/intersection_observer_article'
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper'
import PageTitle from '../features/ui/util/page_title' import PageTitle from '../features/ui/util/page_title'
import GroupsPanel from '../components/panel/groups_panel' import GroupsPanel from '../components/panel/groups_panel'
import ListsPanel from '../components/panel/lists_panel' import ListsPanel from '../components/panel/lists_panel'
@ -9,7 +11,6 @@ import WhoToFollowPanel from '../components/panel/who_to_follow_panel'
import ProgressPanel from '../components/panel/progress_panel' import ProgressPanel from '../components/panel/progress_panel'
import UserPanel from '../components/panel/user_panel' import UserPanel from '../components/panel/user_panel'
import TrendsPanel from '../components/panel/trends_panel' import TrendsPanel from '../components/panel/trends_panel'
import HashtagsPanel from '../components/panel/hashtags_panel'
import DefaultLayout from '../layouts/default_layout' import DefaultLayout from '../layouts/default_layout'
import TimelineComposeBlock from '../components/timeline_compose_block' import TimelineComposeBlock from '../components/timeline_compose_block'
import Divider from '../components/divider' import Divider from '../components/divider'
@ -40,6 +41,25 @@ class HomePage extends PureComponent {
onOpenHomePageSettingsModal: PropTypes.func.isRequired, onOpenHomePageSettingsModal: PropTypes.func.isRequired,
} }
intersectionObserverWrapper = new IntersectionObserverWrapper()
componentDidMount() {
this.attachIntersectionObserver()
}
componentWillUnmount() {
this.detachIntersectionObserver()
}
attachIntersectionObserver() {
this.intersectionObserverWrapper.connect()
}
detachIntersectionObserver() {
this.intersectionObserverWrapper.disconnect()
}
render() { render() {
const { const {
intl, intl,
@ -62,10 +82,30 @@ class HomePage extends PureComponent {
<UserPanel /> <UserPanel />
<ProgressPanel /> <ProgressPanel />
<TrendsPanel /> <TrendsPanel />
<IntersectionObserverArticle
id={'home-sidebar-lists-panel'}
listLength={7}
index={4}
intersectionObserverWrapper={this.intersectionObserverWrapper}
>
<ListsPanel /> <ListsPanel />
<HashtagsPanel /> </IntersectionObserverArticle>
<IntersectionObserverArticle
id={'home-sidebar-wtf-panel'}
listLength={7}
index={5}
intersectionObserverWrapper={this.intersectionObserverWrapper}
>
<WhoToFollowPanel /> <WhoToFollowPanel />
<GroupsPanel /> </IntersectionObserverArticle>
<IntersectionObserverArticle
id={'home-sidebar-groups-panel'}
listLength={7}
index={6}
intersectionObserverWrapper={this.intersectionObserverWrapper}
>
<GroupsPanel isLazy />
</IntersectionObserverArticle>
<LinkFooter /> <LinkFooter />
</Fragment> </Fragment>
)} )}

View File

@ -3,6 +3,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import { openModal } from '../actions/modal' import { openModal } from '../actions/modal'
import {
MODAL_LIST_EDITOR,
// MODAL_LIST_TIMELINE_SETTINGS,
} from '../constants'
import PageTitle from '../features/ui/util/page_title' import PageTitle from '../features/ui/util/page_title'
import LinkFooter from '../components/link_footer' import LinkFooter from '../components/link_footer'
import DefaultLayout from '../layouts/default_layout' import DefaultLayout from '../layouts/default_layout'
@ -18,15 +22,16 @@ const mapStateToProps = (state, props) => ({
list: state.getIn(['lists', props.params.id]), list: state.getIn(['lists', props.params.id]),
}) })
const mapDispatchToProps = (dispatch, { list }) => ({ const mapDispatchToProps = (dispatch) => ({
onOpenListEditModal() { onOpenListEditModal(list) {
dispatch(openModal('LIST_EDIT', { if (!list) return
list, const listId = list.get('id')
})) dispatch(openModal(MODAL_LIST_EDITOR, { listId }))
},
onOpenListTimelineSettingsModal() {
dispatch(openModal('LIST_TIMELINE_SETTINGS'))
}, },
// : todo :
// onOpenListTimelineSettingsModal() {
// dispatch(openModal(MODAL_LIST_TIMELINE_SETTINGS))
// },
}) })
export default export default
@ -39,7 +44,11 @@ class ListPage extends ImmutablePureComponent {
list: ImmutablePropTypes.map, list: ImmutablePropTypes.map,
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
onOpenListEditModal: PropTypes.func.isRequired, onOpenListEditModal: PropTypes.func.isRequired,
onOpenListTimelineSettingsModal: PropTypes.func.isRequired, // onOpenListTimelineSettingsModal: PropTypes.func.isRequired,
}
handleOnOpenListEditModal = () => {
this.props.onOpenListEditModal(this.props.list)
} }
render() { render() {
@ -48,33 +57,33 @@ class ListPage extends ImmutablePureComponent {
children, children,
list, list,
onOpenListEditModal, onOpenListEditModal,
onOpenListTimelineSettingsModal // onOpenListTimelineSettingsModal
} = this.props } = this.props
const title = !!list ? list.get('title') : '' const title = !!list ? list.get('title') : ''
return ( return (
<DefaultLayout <DefaultLayout
showBackBtn
title={title} title={title}
actions={[ actions={[
{ {
icon: 'list-edit', icon: 'cog',
onClick: onOpenListEditModal, onClick: this.handleOnOpenListEditModal,
},
{
icon: 'ellipsis',
onClick: onOpenListTimelineSettingsModal,
}, },
// {
// icon: 'ellipsis',
// onClick: onOpenListTimelineSettingsModal,
// },
]} ]}
layout={( layout={(
<Fragment> <Fragment>
<ListDetailsPanel /> <ListDetailsPanel list={list} onEdit={this.handleOnOpenListEditModal} />
<TrendsPanel /> <TrendsPanel />
<WhoToFollowPanel /> <WhoToFollowPanel />
<LinkFooter /> <LinkFooter />
</Fragment> </Fragment>
)} )}
showBackBtn
> >
<PageTitle path={[title, intl.formatMessage(messages.list)]} /> <PageTitle path={[title, intl.formatMessage(messages.list)]} />
{ children } { children }

View File

@ -93,9 +93,11 @@ class NotificationsPage extends PureComponent {
const tabs = filters.map((filter) => ({ const tabs = filters.map((filter) => ({
title: intl.formatMessage(messages[filter]), title: intl.formatMessage(messages[filter]),
onClick: () => this.onChangeActiveFilter(filter), onClick: () => this.onChangeActiveFilter(filter),
active: selectedFilter === filter, active: selectedFilter.toLowerCase() === filter.toLowerCase(),
})) }))
console.log("selectedFilter, filter: ", tabs, selectedFilter)
return ( return (
<DefaultLayout <DefaultLayout
title={intl.formatMessage(messages.notifications)} title={intl.formatMessage(messages.notifications)}
@ -117,4 +119,4 @@ class NotificationsPage extends PureComponent {
</DefaultLayout> </DefaultLayout>
) )
} }
} }{}

View File

@ -6,7 +6,6 @@ import WhoToFollowPanel from '../components/panel/who_to_follow_panel'
import ProgressPanel from '../components/panel/progress_panel' import ProgressPanel from '../components/panel/progress_panel'
import UserPanel from '../components/panel/user_panel' import UserPanel from '../components/panel/user_panel'
import TrendsPanel from '../components/panel/trends_panel' import TrendsPanel from '../components/panel/trends_panel'
import HashtagsPanel from '../components/panel/hashtags_panel'
import DefaultLayout from '../layouts/default_layout' import DefaultLayout from '../layouts/default_layout'
export default export default
@ -24,7 +23,6 @@ class ShortcutsPage extends PureComponent {
<UserPanel /> <UserPanel />
<ProgressPanel /> <ProgressPanel />
<TrendsPanel /> <TrendsPanel />
<HashtagsPanel />
<WhoToFollowPanel /> <WhoToFollowPanel />
<GroupSidebarPanel /> <GroupSidebarPanel />
<LinkFooter /> <LinkFooter />

View File

@ -1,4 +1,4 @@
import { Map as ImmutableMap } from 'immutable'; import { Map as ImmutableMap } from 'immutable'
import { import {
GROUP_CREATE_REQUEST, GROUP_CREATE_REQUEST,
GROUP_CREATE_FAIL, GROUP_CREATE_FAIL,
@ -8,8 +8,10 @@ import {
GROUP_UPDATE_SUCCESS, GROUP_UPDATE_SUCCESS,
GROUP_EDITOR_RESET, GROUP_EDITOR_RESET,
GROUP_EDITOR_SETUP, GROUP_EDITOR_SETUP,
GROUP_EDITOR_VALUE_CHANGE, GROUP_EDITOR_TITLE_CHANGE,
} from '../actions/group_editor'; GROUP_EDITOR_DESCRIPTION_CHANGE,
GROUP_EDITOR_COVER_IMAGE_CHANGE,
} from '../actions/group_editor'
const initialState = ImmutableMap({ const initialState = ImmutableMap({
groupId: null, groupId: null,
@ -18,40 +20,50 @@ const initialState = ImmutableMap({
title: '', title: '',
description: '', description: '',
coverImage: null, coverImage: null,
}); })
export default function groupEditorReducer(state = initialState, action) { export default function groupEditorReducer(state = initialState, action) {
switch(action.type) { switch(action.type) {
case GROUP_EDITOR_RESET: case GROUP_EDITOR_RESET:
return initialState; return initialState
case GROUP_EDITOR_SETUP: case GROUP_EDITOR_SETUP:
return state.withMutations(map => { return state.withMutations(map => {
map.set('groupId', action.group.get('id')); map.set('groupId', action.group.get('id'))
map.set('title', action.group.get('title')); map.set('title', action.group.get('title'))
map.set('description', action.group.get('description')); map.set('description', action.group.get('description'))
map.set('isSubmitting', false); map.set('isSubmitting', false)
}); })
case GROUP_EDITOR_VALUE_CHANGE: case GROUP_EDITOR_TITLE_CHANGE:
return state.withMutations(map => { return state.withMutations(map => {
map.set(action.field, action.value); map.set('title', action.title)
map.set('isChanged', true); map.set('isChanged', true)
}); })
case GROUP_EDITOR_DESCRIPTION_CHANGE:
return state.withMutations(map => {
map.set('description', action.description)
map.set('isChanged', true)
})
case GROUP_EDITOR_COVER_IMAGE_CHANGE:
return state.withMutations(map => {
map.set('coverImage', action.value)
map.set('isChanged', true)
})
case GROUP_CREATE_REQUEST: case GROUP_CREATE_REQUEST:
case GROUP_UPDATE_REQUEST: case GROUP_UPDATE_REQUEST:
return state.withMutations(map => { return state.withMutations(map => {
map.set('isSubmitting', true); map.set('isSubmitting', true)
map.set('isChanged', false); map.set('isChanged', false)
}); })
case GROUP_CREATE_FAIL: case GROUP_CREATE_FAIL:
case GROUP_UPDATE_FAIL: case GROUP_UPDATE_FAIL:
return state.set('isSubmitting', false); return state.set('isSubmitting', false)
case GROUP_CREATE_SUCCESS: case GROUP_CREATE_SUCCESS:
case GROUP_UPDATE_SUCCESS: case GROUP_UPDATE_SUCCESS:
return state.withMutations(map => { return state.withMutations(map => {
map.set('isSubmitting', false); map.set('isSubmitting', false)
map.set('groupId', action.group.id); map.set('groupId', action.group.id)
}); })
default: default:
return state; return state
} }
}; }

View File

@ -1,5 +1,16 @@
:root { :root {
--radius: 8px; --radius-small: 8px;
--radius-circle: 9999px;
/* Default Font Sizes */
--font_size_extra_small: 12px;
--font_size_small: 13px;
--font_size_normal: 14px;
--font_size_medium: 15px;
--font_size_large: 16px;
--font_size_extra_large: 19px;
--font_size_extra_extra_large: 24px;
--color_brand-dark: #38a16b; --color_brand-dark: #38a16b;
--color_brand-light: #36e991; --color_brand-light: #36e991;
@ -9,13 +20,13 @@
--color_white: #fff; --color_white: #fff;
--color_black: #2d3436; --color_black: #2d3436;
--color_black-opaque: rgba(0, 0, 0, .8); --color_black-opaque: rgba(0, 0, 0, .8);
--color_black-opaquer: rgba(0, 0, 0, .4); --color_black-opaquer: rgba(0, 0, 0, .5);
--color_gold: #ffd700; --color_gold: #ffd700;
--color_red: #de2960; --color_red: #de2960;
--color_red-dark: #c72c5b; --color_red-dark: #c72c5b;
/* LIGHT THEME */ /* LIGHT THEME */
/* --solid_color_primary: #fff; --solid_color_primary: #fff;
--solid_color_primary-opaque:rgba(255, 255, 255,.6); --solid_color_primary-opaque:rgba(255, 255, 255,.6);
--solid_color_secondary: #e2e8ec; --solid_color_secondary: #e2e8ec;
--solid_color_secondary-dark: #d9e0e5; --solid_color_secondary-dark: #d9e0e5;
@ -26,7 +37,7 @@
--text_color_secondary: #4b4f55; --text_color_secondary: #4b4f55;
--text_color_tertiary: #777; --text_color_tertiary: #777;
--border_color_secondary: #ececed; */ --border_color_secondary: #ececed;
/* MUTED THEME */ /* MUTED THEME */
/* --solid_color_primary: #222; /* --solid_color_primary: #222;
@ -43,7 +54,7 @@
--border_color_secondary: #424141;*/ --border_color_secondary: #424141;*/
/* BLACK THEME */ /* BLACK THEME */
--solid_color_primary: #13171b; /* --solid_color_primary: #13171b;
--solid_color_primary-opaque:rgba(19, 23, 27, .6); --solid_color_primary-opaque:rgba(19, 23, 27, .6);
--solid_color_secondary: #4f5050; --solid_color_secondary: #4f5050;
--solid_color_secondary-dark: #424343; --solid_color_secondary-dark: #424343;
@ -54,7 +65,7 @@
--text_color_secondary: #61686E; --text_color_secondary: #61686E;
--text_color_tertiary: #656565; --text_color_tertiary: #656565;
--border_color_secondary: #212020; --border_color_secondary: #212020; */
} }
html, html,
@ -62,6 +73,7 @@ body {
height: 100%; height: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: var(--solid_color_tertiary);
} }
body { body {
@ -78,7 +90,7 @@ body {
.statusContent * { .statusContent * {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
font-size: 15px; font-size: var(--font_size_medium);
overflow-wrap: break-word; overflow-wrap: break-word;
color: var(--text_color_primary); color: var(--text_color_primary);
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
@ -102,7 +114,7 @@ body {
} }
.statusContent h1 { .statusContent h1 {
font-size: 1.5rem; font-size: var(--font_size_extra_large);
font-weight: 700; font-weight: 700;
margin-top: 0.5rem; margin-top: 0.5rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
@ -119,7 +131,7 @@ body {
.dangerousContent * { .dangerousContent * {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
font-size: 14px; font-size: var(--font_size_normal);
overflow-wrap: break-word; overflow-wrap: break-word;
color: var(--text_color_primary); color: var(--text_color_primary);
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
@ -224,8 +236,12 @@ body {
.resizeNone { resize: none; } .resizeNone { resize: none; }
.circle { border-radius: 9999px; } .circle { border-radius: var(--radius-circle); }
.radiusSmall { border-radius: var(--radius); } .radiusSmall { border-radius: var(--radius-small); }
.topLeftRadiusSmall { border-top-left-radius: var(--radius-small); }
.topRightRadiusSmall { border-top-right-radius: var(--radius-small); }
.bottomRightRadiusSmall { border-bottom-right-radius: var(--radius-small); }
.bottomLeftRadiusSmall { border-bottom-left-radius: var(--radius-small); }
.borderColorPrimary { border-color: var(--solid_color_primary); } .borderColorPrimary { border-color: var(--solid_color_primary); }
.borderColorSecondary { border-color: var(--border_color_secondary); } .borderColorSecondary { border-color: var(--border_color_secondary); }
@ -509,13 +525,13 @@ body {
.textAlignLeft { text-align: left; } .textAlignLeft { text-align: left; }
.textAlignCenter { text-align: center; } .textAlignCenter { text-align: center; }
.fontSize24PX { font-size: 24px; } .fontSize24PX { font-size: var(--font_size_extra_extra_large); }
.fontSize19PX { font-size: 19px; } .fontSize19PX { font-size: var(--font_size_extra_large); }
.fontSize16PX { font-size: 16px; } .fontSize16PX { font-size: var(--font_size_large); }
.fontSize15PX { font-size: 15px; } .fontSize15PX { font-size: var(--font_size_medium); }
.fontSize14PX { font-size: 14px; } .fontSize14PX { font-size: var(--font_size_normal); }
.fontSize13PX { font-size: 13px; } .fontSize13PX { font-size: var(--font_size_small); }
.fontSize12PX { font-size: 12px; } .fontSize12PX { font-size: var(--font_size_extra_small); }
.fontSize0 { font-size: 0; } .fontSize0 { font-size: 0; }
.fontWeightNormal { font-weight: 400; } .fontWeightNormal { font-weight: 400; }
@ -588,6 +604,7 @@ body {
.pl0 { padding-left: 0; } .pl0 { padding-left: 0; }
.pr15 { padding-right: 15px; } .pr15 { padding-right: 15px; }
.pr5 { padding-right: 5px; }
.pr0 { padding-right: 0; } .pr0 { padding-right: 0; }
.px15 { .px15 {
@ -677,7 +694,7 @@ body {
.select { .select {
height: 42px; height: 42px;
line-height: 42px; line-height: 42px;
font-size: 18px; font-size: var(--font_size_extra_large);
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
appearance: none; appearance: none;
@ -708,7 +725,7 @@ body {
/* :global(.public-DraftEditorPlaceholder-inner) { /* :global(.public-DraftEditorPlaceholder-inner) {
font-weight: 400; font-weight: 400;
font-size: 16px; font-size: var(--font_size_large);
} */ } */
:global(.RichEditor-blockquote) { :global(.RichEditor-blockquote) {
@ -722,14 +739,14 @@ body {
:global(.public-DraftStyleDefault-pre) { :global(.public-DraftStyleDefault-pre) {
background-color: rgba(0,0,0,.05); background-color: rgba(0,0,0,.05);
font-family: 'Inconsolata', 'Menlo', 'Consolas', monospace; font-family: 'Inconsolata', 'Menlo', 'Consolas', monospace;
font-size: 16px; font-size: var(--font_size_large);
padding: 10px 20px; padding: 10px 20px;
} }
/* */ /* */
:global(.emoji-mart-search input) { :global(.emoji-mart-search input) {
border-radius: 9999px !important; border-radius: var(--radius-circle) !important;
} }
/* */ /* */

View File

@ -3,7 +3,7 @@
class REST::GroupSerializer < ActiveModel::Serializer class REST::GroupSerializer < ActiveModel::Serializer
include RoutingHelper include RoutingHelper
attributes :id, :title, :description, :cover_image_url, :is_archived, :member_count attributes :id, :title, :description, :cover_image_url, :is_archived, :member_count, :created_at
def id def id
object.id.to_s object.id.to_s

View File

@ -24,6 +24,9 @@
= render 'stream_entries/meta', stream_entry: @stream_entry, account: @account = render 'stream_entries/meta', stream_entry: @stream_entry, account: @account
- elsif @account && @account.local? - elsif @account && @account.local?
= render 'accounts/meta', account: @account, older_url: nil, newer_url: nil = render 'accounts/meta', account: @account, older_url: nil, newer_url: nil
- else
- description = strip_tags(t('about.about_gabsocial_html'))
%meta{ name: 'description', content: description }/
%title= content_for?(:page_title) ? safe_join([yield(:page_title).chomp.html_safe, title], ' - ') : title %title= content_for?(:page_title) ? safe_join([yield(:page_title).chomp.html_safe, title], ' - ') : title