From d69f045681e4b48ff55077eefda388904d06b158 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Mon, 20 Aug 2018 12:59:36 +0200 Subject: Fix ColumnLink keys in getting_started_misc --- .../glitch/features/getting_started_misc/index.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/features/getting_started_misc/index.js b/app/javascript/flavours/glitch/features/getting_started_misc/index.js index b67e6f97f..cbd4b8fe2 100644 --- a/app/javascript/flavours/glitch/features/getting_started_misc/index.js +++ b/app/javascript/flavours/glitch/features/getting_started_misc/index.js @@ -40,20 +40,22 @@ export default class gettingStartedMisc extends ImmutablePureComponent { render () { const { intl } = this.props; + let i = 1; + return (
- - - - - - - - + + + + + + + +
); -- cgit From 9fbaaefe59c56e4cd6874be87fb961df39ead3d0 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Mon, 20 Aug 2018 16:34:17 +0200 Subject: Split list editor into components and containers --- .../features/list_editor/components/account.js | 23 +-------------------- .../features/list_editor/components/search.js | 16 +-------------- .../list_editor/containers/account_container.js | 24 ++++++++++++++++++++++ .../list_editor/containers/search_container.js | 17 +++++++++++++++ .../flavours/glitch/features/list_editor/index.js | 10 ++++----- 5 files changed, 48 insertions(+), 42 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/list_editor/containers/account_container.js create mode 100644 app/javascript/flavours/glitch/features/list_editor/containers/search_container.js (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/features/list_editor/components/account.js b/app/javascript/flavours/glitch/features/list_editor/components/account.js index f48df759d..71a8b7673 100644 --- a/app/javascript/flavours/glitch/features/list_editor/components/account.js +++ b/app/javascript/flavours/glitch/features/list_editor/components/account.js @@ -1,38 +1,17 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { makeGetAccount } from 'flavours/glitch/selectors'; import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Avatar from 'flavours/glitch/components/avatar'; import DisplayName from 'flavours/glitch/components/display_name'; import IconButton from 'flavours/glitch/components/icon_button'; -import { defineMessages, injectIntl } from 'react-intl'; -import { removeFromListEditor, addToListEditor } from 'flavours/glitch/actions/lists'; +import { defineMessages } from 'react-intl'; const messages = defineMessages({ remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' }, add: { id: 'lists.account.add', defaultMessage: 'Add to list' }, }); -const makeMapStateToProps = () => { - const getAccount = makeGetAccount(); - - const mapStateToProps = (state, { accountId, added }) => ({ - account: getAccount(state, accountId), - added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added, - }); - - return mapStateToProps; -}; - -const mapDispatchToProps = (dispatch, { accountId }) => ({ - onRemove: () => dispatch(removeFromListEditor(accountId)), - onAdd: () => dispatch(addToListEditor(accountId)), -}); - -@connect(makeMapStateToProps, mapDispatchToProps) -@injectIntl export default class Account extends ImmutablePureComponent { static propTypes = { diff --git a/app/javascript/flavours/glitch/features/list_editor/components/search.js b/app/javascript/flavours/glitch/features/list_editor/components/search.js index 45c4d0f2e..280632652 100644 --- a/app/javascript/flavours/glitch/features/list_editor/components/search.js +++ b/app/javascript/flavours/glitch/features/list_editor/components/search.js @@ -1,26 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { defineMessages, injectIntl } from 'react-intl'; -import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; +import { defineMessages } from 'react-intl'; import classNames from 'classnames'; const messages = defineMessages({ search: { id: 'lists.search', defaultMessage: 'Search among people you follow' }, }); -const mapStateToProps = state => ({ - value: state.getIn(['listEditor', 'suggestions', 'value']), -}); - -const mapDispatchToProps = dispatch => ({ - onSubmit: value => dispatch(fetchListSuggestions(value)), - onClear: () => dispatch(clearListSuggestions()), - onChange: value => dispatch(changeListSuggestions(value)), -}); - -@connect(mapStateToProps, mapDispatchToProps) -@injectIntl export default class Search extends React.PureComponent { static propTypes = { diff --git a/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js b/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js new file mode 100644 index 000000000..782eb42f3 --- /dev/null +++ b/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js @@ -0,0 +1,24 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { makeGetAccount } from 'flavours/glitch/selectors'; +import { injectIntl } from 'react-intl'; +import { removeFromListEditor, addToListEditor } from 'flavours/glitch/actions/lists'; +import Account from '../components/account'; + +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = (state, { accountId, added }) => ({ + account: getAccount(state, accountId), + added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added, + }); + + return mapStateToProps; +}; + +const mapDispatchToProps = (dispatch, { accountId }) => ({ + onRemove: () => dispatch(removeFromListEditor(accountId)), + onAdd: () => dispatch(addToListEditor(accountId)), +}); + +export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account)); diff --git a/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js b/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js new file mode 100644 index 000000000..5af20efbd --- /dev/null +++ b/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { injectIntl } from 'react-intl'; +import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; +import Search from '../components/search'; + +const mapStateToProps = state => ({ + value: state.getIn(['listEditor', 'suggestions', 'value']), +}); + +const mapDispatchToProps = dispatch => ({ + onSubmit: value => dispatch(fetchListSuggestions(value)), + onClear: () => dispatch(clearListSuggestions()), + onChange: value => dispatch(changeListSuggestions(value)), +}); + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Search)); diff --git a/app/javascript/flavours/glitch/features/list_editor/index.js b/app/javascript/flavours/glitch/features/list_editor/index.js index e6df4755a..b3be3070a 100644 --- a/app/javascript/flavours/glitch/features/list_editor/index.js +++ b/app/javascript/flavours/glitch/features/list_editor/index.js @@ -5,8 +5,8 @@ import { connect } from 'react-redux'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { injectIntl } from 'react-intl'; import { setupListEditor, clearListSuggestions, resetListEditor } from 'flavours/glitch/actions/lists'; -import Account from './components/account'; -import Search from './components/search'; +import AccountContainer from './containers/account_container'; +import SearchContainer from './containers/search_container'; import Motion from 'flavours/glitch/util/optional_motion'; import spring from 'react-motion/lib/spring'; @@ -56,11 +56,11 @@ export default class ListEditor extends ImmutablePureComponent {

{title}

- +
- {accountIds.map(accountId => )} + {accountIds.map(accountId => )}
{showSearch &&
} @@ -68,7 +68,7 @@ export default class ListEditor extends ImmutablePureComponent { {({ x }) => (
- {searchAccountIds.map(accountId => )} + {searchAccountIds.map(accountId => )}
) }
-- cgit From 360fbf1bd430ae43f28444b41e01d305c85c4935 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Mon, 20 Aug 2018 14:22:19 +0200 Subject: Add pinned accounts editor --- app/javascript/flavours/glitch/actions/accounts.js | 84 ++++++++++++++++++++++ .../glitch/features/getting_started_misc/index.js | 7 +- .../containers/account_container.js | 24 +++++++ .../containers/search_container.js | 21 ++++++ .../features/pinned_accounts_editor/index.js | 78 ++++++++++++++++++++ .../glitch/features/ui/components/modal_root.js | 2 + .../flavours/glitch/reducers/accounts.js | 4 ++ app/javascript/flavours/glitch/reducers/index.js | 2 + .../glitch/reducers/pinned_accounts_editor.js | 57 +++++++++++++++ .../flavours/glitch/util/async-components.js | 4 ++ 10 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js create mode 100644 app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js create mode 100644 app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js create mode 100644 app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/actions/accounts.js b/app/javascript/flavours/glitch/actions/accounts.js index d5c4a02f9..d67ab112e 100644 --- a/app/javascript/flavours/glitch/actions/accounts.js +++ b/app/javascript/flavours/glitch/actions/accounts.js @@ -72,6 +72,17 @@ export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST'; export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS'; export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL'; +export const PINNED_ACCOUNTS_FETCH_REQUEST = 'PINNED_ACCOUNTS_FETCH_REQUEST'; +export const PINNED_ACCOUNTS_FETCH_SUCCESS = 'PINNED_ACCOUNTS_FETCH_SUCCESS'; +export const PINNED_ACCOUNTS_FETCH_FAIL = 'PINNED_ACCOUNTS_FETCH_FAIL'; + +export const PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY = 'PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY'; +export const PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR = 'PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR'; +export const PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE = 'PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE'; + +export const PINNED_ACCOUNTS_EDITOR_RESET = 'PINNED_ACCOUNTS_EDITOR_RESET'; + + export function fetchAccount(id) { return (dispatch, getState) => { dispatch(fetchRelationships([id])); @@ -733,3 +744,76 @@ export function unpinAccountFail(error) { error, }; }; + +export function fetchPinnedAccounts() { + return (dispatch, getState) => { + dispatch(fetchPinnedAccountsRequest()); + + api(getState).get(`/api/v1/endorsements`, { params: { limit: 0 } }) + .then(({ data }) => dispatch(fetchPinnedAccountsSuccess(data))) + .catch(err => dispatch(fetchPinnedAccountsFail(err))); + }; +}; + +export function fetchPinnedAccountsRequest() { + return { + type: PINNED_ACCOUNTS_FETCH_REQUEST, + }; +}; + +export function fetchPinnedAccountsSuccess(accounts, next) { + return { + type: PINNED_ACCOUNTS_FETCH_SUCCESS, + accounts, + next, + }; +}; + +export function fetchPinnedAccountsFail(error) { + return { + type: PINNED_ACCOUNTS_FETCH_FAIL, + error, + }; +}; + +export function fetchPinnedAccountsSuggestions(q) { + return (dispatch, getState) => { + const params = { + q, + resolve: false, + limit: 4, + following: true, + }; + + api(getState).get('/api/v1/accounts/search', { params }) + .then(({ data }) => dispatch(fetchPinnedAccountsSuggestionsReady(q, data))); + }; +}; + +export function fetchPinnedAccountsSuggestionsReady(query, accounts) { + return { + type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY, + query, + accounts, + }; +}; + +export function clearPinnedAccountsSuggestions() { + return { + type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR, + }; +}; + +export function changePinnedAccountsSuggestions(value) { + return { + type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE, + value, + } +}; + +export function resetPinnedAccountsEditor() { + return { + type: PINNED_ACCOUNTS_EDITOR_RESET, + }; +}; + diff --git a/app/javascript/flavours/glitch/features/getting_started_misc/index.js b/app/javascript/flavours/glitch/features/getting_started_misc/index.js index cbd4b8fe2..ee4452472 100644 --- a/app/javascript/flavours/glitch/features/getting_started_misc/index.js +++ b/app/javascript/flavours/glitch/features/getting_started_misc/index.js @@ -21,6 +21,7 @@ const messages = defineMessages({ pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' }, info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }, keyboard_shortcuts: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Keyboard shortcuts' }, + featured_users: { id: 'navigation_bar.featured_users', defaultMessage: 'Featured users' }, }); @connect() @@ -33,10 +34,13 @@ export default class gettingStartedMisc extends ImmutablePureComponent { }; openOnboardingModal = (e) => { - e.preventDefault(); this.props.dispatch(openModal('ONBOARDING')); } + openFeaturedAccountsModal = (e) => { + this.props.dispatch(openModal('PINNED_ACCOUNTS_EDITOR')); + } + render () { const { intl } = this.props; @@ -50,6 +54,7 @@ export default class gettingStartedMisc extends ImmutablePureComponent { + diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js new file mode 100644 index 000000000..149d05c32 --- /dev/null +++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/account_container.js @@ -0,0 +1,24 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { makeGetAccount } from 'flavours/glitch/selectors'; +import { injectIntl } from 'react-intl'; +import { pinAccount, unpinAccount } from 'flavours/glitch/actions/accounts'; +import Account from 'flavours/glitch/features/list_editor/components/account'; + +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = (state, { accountId, added }) => ({ + account: getAccount(state, accountId), + added: typeof added === 'undefined' ? state.getIn(['pinnedAccountsEditor', 'accounts', 'items']).includes(accountId) : added, + }); + + return mapStateToProps; +}; + +const mapDispatchToProps = (dispatch, { accountId }) => ({ + onRemove: () => dispatch(unpinAccount(accountId)), + onAdd: () => dispatch(pinAccount(accountId)), +}); + +export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account)); diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js new file mode 100644 index 000000000..5a1efce0a --- /dev/null +++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { injectIntl } from 'react-intl'; +import { + fetchPinnedAccountsSuggestions, + clearPinnedAccountsSuggestions, + changePinnedAccountsSuggestions +} from '../../../actions/accounts'; +import Search from 'flavours/glitch/features/list_editor/components/search'; + +const mapStateToProps = state => ({ + value: state.getIn(['pinnedAccountsEditor', 'suggestions', 'value']), +}); + +const mapDispatchToProps = dispatch => ({ + onSubmit: value => dispatch(fetchPinnedAccountsSuggestions(value)), + onClear: () => dispatch(clearPinnedAccountsSuggestions()), + onChange: value => dispatch(changePinnedAccountsSuggestions(value)), +}); + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Search)); diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js new file mode 100644 index 000000000..7484e458e --- /dev/null +++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js @@ -0,0 +1,78 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { connect } from 'react-redux'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { injectIntl, FormattedMessage } from 'react-intl'; +import { fetchPinnedAccounts, clearPinnedAccountsSuggestions, resetPinnedAccountsEditor } from 'flavours/glitch/actions/accounts'; +import AccountContainer from './containers/account_container'; +import SearchContainer from './containers/search_container'; +import Motion from 'flavours/glitch/util/optional_motion'; +import spring from 'react-motion/lib/spring'; + +const mapStateToProps = state => ({ + accountIds: state.getIn(['pinnedAccountsEditor', 'accounts', 'items']), + searchAccountIds: state.getIn(['pinnedAccountsEditor', 'suggestions', 'items']), +}); + +const mapDispatchToProps = dispatch => ({ + onInitialize: () => dispatch(fetchPinnedAccounts()), + onClear: () => dispatch(clearPinnedAccountsSuggestions()), + onReset: () => dispatch(resetPinnedAccountsEditor()), +}); + +@connect(mapStateToProps, mapDispatchToProps) +@injectIntl +export default class PinnedAccountsEditor extends ImmutablePureComponent { + + static propTypes = { + onClose: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + onInitialize: PropTypes.func.isRequired, + onClear: PropTypes.func.isRequired, + onReset: PropTypes.func.isRequired, + title: PropTypes.string.isRequired, + accountIds: ImmutablePropTypes.list.isRequired, + searchAccountIds: ImmutablePropTypes.list.isRequired, + }; + + componentDidMount () { + const { onInitialize } = this.props; + onInitialize(); + } + + componentWillUnmount () { + const { onReset } = this.props; + onReset(); + } + + render () { + const { accountIds, searchAccountIds, onClear } = this.props; + const showSearch = searchAccountIds.size > 0; + + return ( +
+

+ + + +
+
+ {accountIds.map(accountId => )} +
+ + {showSearch &&
} + + + {({ x }) => + (
+ {searchAccountIds.map(accountId => )} +
) + } +
+
+
+ ); + } + +} diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js index 23a7603d8..c9f54804a 100644 --- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js +++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js @@ -19,6 +19,7 @@ import { SettingsModal, EmbedModal, ListEditor, + PinnedAccountsEditor, } from 'flavours/glitch/util/async-components'; const MODAL_COMPONENTS = { @@ -36,6 +37,7 @@ const MODAL_COMPONENTS = { 'EMBED': EmbedModal, 'LIST_EDITOR': ListEditor, 'FOCAL_POINT': () => Promise.resolve({ default: FocalPointModal }), + 'PINNED_ACCOUNTS_EDITOR': PinnedAccountsEditor, }; export default class ModalRoot extends React.PureComponent { diff --git a/app/javascript/flavours/glitch/reducers/accounts.js b/app/javascript/flavours/glitch/reducers/accounts.js index c38b3cc95..c2f016a87 100644 --- a/app/javascript/flavours/glitch/reducers/accounts.js +++ b/app/javascript/flavours/glitch/reducers/accounts.js @@ -6,6 +6,8 @@ import { FOLLOWING_EXPAND_SUCCESS, FOLLOW_REQUESTS_FETCH_SUCCESS, FOLLOW_REQUESTS_EXPAND_SUCCESS, + PINNED_ACCOUNTS_FETCH_SUCCESS, + PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY, } from 'flavours/glitch/actions/accounts'; import { BLOCKS_FETCH_SUCCESS, @@ -141,6 +143,8 @@ export default function accounts(state = initialState, action) { case MUTES_EXPAND_SUCCESS: case LIST_ACCOUNTS_FETCH_SUCCESS: case LIST_EDITOR_SUGGESTIONS_READY: + case PINNED_ACCOUNTS_FETCH_SUCCESS: + case PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY: return action.accounts ? normalizeAccounts(state, action.accounts) : state; case NOTIFICATIONS_EXPAND_SUCCESS: case SEARCH_FETCH_SUCCESS: diff --git a/app/javascript/flavours/glitch/reducers/index.js b/app/javascript/flavours/glitch/reducers/index.js index 7b7bc2ca2..218a5ac8f 100644 --- a/app/javascript/flavours/glitch/reducers/index.js +++ b/app/javascript/flavours/glitch/reducers/index.js @@ -28,6 +28,7 @@ import custom_emojis from './custom_emojis'; import lists from './lists'; import listEditor from './list_editor'; import filters from './filters'; +import pinnedAccountsEditor from './pinned_accounts_editor'; const reducers = { dropdown_menu, @@ -59,6 +60,7 @@ const reducers = { lists, listEditor, filters, + pinnedAccountsEditor, }; export default combineReducers(reducers); diff --git a/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js b/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js new file mode 100644 index 000000000..267521bb8 --- /dev/null +++ b/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js @@ -0,0 +1,57 @@ +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import { + PINNED_ACCOUNTS_EDITOR_RESET, + PINNED_ACCOUNTS_FETCH_REQUEST, + PINNED_ACCOUNTS_FETCH_SUCCESS, + PINNED_ACCOUNTS_FETCH_FAIL, + PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY, + PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR, + PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE, + ACCOUNT_PIN_SUCCESS, + ACCOUNT_UNPIN_SUCCESS, +} from '../actions/accounts'; + +const initialState = ImmutableMap({ + accounts: ImmutableMap({ + items: ImmutableList(), + loaded: false, + isLoading: false, + }), + + suggestions: ImmutableMap({ + value: '', + items: ImmutableList(), + }), +}); + +export default function listEditorReducer(state = initialState, action) { + switch(action.type) { + case PINNED_ACCOUNTS_EDITOR_RESET: + return initialState; + case PINNED_ACCOUNTS_FETCH_REQUEST: + return state.setIn(['accounts', 'isLoading'], true); + case PINNED_ACCOUNTS_FETCH_FAIL: + return state.setIn(['accounts', 'isLoading'], false); + case PINNED_ACCOUNTS_FETCH_SUCCESS: + return state.update('accounts', accounts => accounts.withMutations(map => { + map.set('isLoading', false); + map.set('loaded', true); + map.set('items', ImmutableList(action.accounts.map(item => item.id))); + })); + case PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE: + return state.setIn(['suggestions', 'value'], action.value); + case PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_READY: + return state.setIn(['suggestions', 'items'], ImmutableList(action.accounts.map(item => item.id))); + case PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR: + return state.update('suggestions', suggestions => suggestions.withMutations(map => { + map.set('items', ImmutableList()); + map.set('value', ''); + })); + case ACCOUNT_PIN_SUCCESS: + return state.updateIn(['accounts', 'items'], list => list.unshift(action.relationship.id)); + case ACCOUNT_UNPIN_SUCCESS: + return state.updateIn(['accounts', 'items'], list => list.filterNot(item => item === action.relationship.id)); + default: + return state; + } +}; diff --git a/app/javascript/flavours/glitch/util/async-components.js b/app/javascript/flavours/glitch/util/async-components.js index 3d6d3d1f4..557ce317e 100644 --- a/app/javascript/flavours/glitch/util/async-components.js +++ b/app/javascript/flavours/glitch/util/async-components.js @@ -38,6 +38,10 @@ export function ListEditor () { return import(/* webpackChunkName: "flavours/glitch/async/list_editor" */'flavours/glitch/features/list_editor'); } +export function PinnedAccountsEditor () { + return import(/* webpackChunkName: "flavours/glitch/async/pinned_accounts_editor" */'flavours/glitch/features/pinned_accounts_editor'); +} + export function DirectTimeline() { return import(/* webpackChunkName: "flavours/glitch/async/direct_timeline" */'flavours/glitch/features/direct_timeline'); } -- cgit From 801919fc9b7ab12ca6652b03cce027224df59718 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Mon, 20 Aug 2018 11:29:42 +0200 Subject: Add hashtag trendline support to glitch-soc flavour Port Mastodon's hashtag stats thing to glitch-soc. This doesn't change how hashtags are ordered, and doesn't add a trending hashtags section, but it does change how hashtag searches are rendered, displaying a trend line alongside each hashtag. --- app/javascript/flavours/glitch/actions/search.js | 2 +- .../flavours/glitch/components/hashtag.js | 34 +++++++++ .../glitch/features/drawer/results/index.js | 11 +-- app/javascript/flavours/glitch/reducers/search.js | 4 +- .../flavours/glitch/styles/components/search.scss | 84 +++++++++++++++++++--- app/javascript/flavours/glitch/util/numbers.js | 10 +++ 6 files changed, 123 insertions(+), 22 deletions(-) create mode 100644 app/javascript/flavours/glitch/components/hashtag.js create mode 100644 app/javascript/flavours/glitch/util/numbers.js (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/actions/search.js b/app/javascript/flavours/glitch/actions/search.js index 13885c600..ec65bdf28 100644 --- a/app/javascript/flavours/glitch/actions/search.js +++ b/app/javascript/flavours/glitch/actions/search.js @@ -32,7 +32,7 @@ export function submitSearch() { dispatch(fetchSearchRequest()); - api(getState).get('/api/v1/search', { + api(getState).get('/api/v2/search', { params: { q: value, resolve: true, diff --git a/app/javascript/flavours/glitch/components/hashtag.js b/app/javascript/flavours/glitch/components/hashtag.js new file mode 100644 index 000000000..88689cc6c --- /dev/null +++ b/app/javascript/flavours/glitch/components/hashtag.js @@ -0,0 +1,34 @@ +import React from 'react'; +import { Sparklines, SparklinesCurve } from 'react-sparklines'; +import { Link } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { shortNumberFormat } from 'flavours/glitch/util/numbers'; + +const Hashtag = ({ hashtag }) => ( +
+
+ + #{hashtag.get('name')} + + + {shortNumberFormat(hashtag.getIn(['history', 0, 'accounts']))} }} /> +
+ +
+ {shortNumberFormat(hashtag.getIn(['history', 0, 'uses']))} +
+ +
+ day.get('uses')).toArray()}> + + +
+
+); + +Hashtag.propTypes = { + hashtag: ImmutablePropTypes.map.isRequired, +}; + +export default Hashtag; diff --git a/app/javascript/flavours/glitch/features/drawer/results/index.js b/app/javascript/flavours/glitch/features/drawer/results/index.js index 23dc0e3cf..ac7a14ef4 100644 --- a/app/javascript/flavours/glitch/features/drawer/results/index.js +++ b/app/javascript/flavours/glitch/features/drawer/results/index.js @@ -12,6 +12,7 @@ import { Link } from 'react-router-dom'; // Components. import AccountContainer from 'flavours/glitch/containers/account_container'; import StatusContainer from 'flavours/glitch/containers/status_container'; +import Hashtag from 'flavours/glitch/components/hashtag'; // Utils. import Motion from 'flavours/glitch/util/optional_motion'; @@ -98,15 +99,7 @@ export default function DrawerResults ({
- {hashtags.map( - hashtag => ( - #{hashtag} - ) - )} + {hashtags.map(hashtag => )}
) : null}
diff --git a/app/javascript/flavours/glitch/reducers/search.js b/app/javascript/flavours/glitch/reducers/search.js index dc6be97e2..9a525bf47 100644 --- a/app/javascript/flavours/glitch/reducers/search.js +++ b/app/javascript/flavours/glitch/reducers/search.js @@ -9,7 +9,7 @@ import { COMPOSE_REPLY, COMPOSE_DIRECT, } from 'flavours/glitch/actions/compose'; -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; const initialState = ImmutableMap({ value: '', @@ -39,7 +39,7 @@ export default function search(state = initialState, action) { return state.set('results', ImmutableMap({ accounts: ImmutableList(action.results.accounts.map(item => item.id)), statuses: ImmutableList(action.results.statuses.map(item => item.id)), - hashtags: ImmutableList(action.results.hashtags), + hashtags: fromJS(action.results.hashtags), })).set('submitted', true); default: return state; diff --git a/app/javascript/flavours/glitch/styles/components/search.scss b/app/javascript/flavours/glitch/styles/components/search.scss index 91861ea19..f9e4b5883 100644 --- a/app/javascript/flavours/glitch/styles/components/search.scss +++ b/app/javascript/flavours/glitch/styles/components/search.scss @@ -90,16 +90,80 @@ font-weight: 500; } -.search-results__hashtag { - display: block; - padding: 10px; - color: $secondary-text-color; - text-decoration: none; +.trends { + &__header { + color: $dark-text-color; + background: lighten($ui-base-color, 2%); + border-bottom: 1px solid darken($ui-base-color, 4%); + font-weight: 500; + padding: 15px; + font-size: 16px; + cursor: default; - &:hover, - &:active, - &:focus { - color: lighten($secondary-text-color, 4%); - text-decoration: underline; + .fa { + display: inline-block; + margin-right: 5px; + } + } + + &__item { + display: flex; + align-items: center; + padding: 15px; + border-bottom: 1px solid lighten($ui-base-color, 8%); + + &:last-child { + border-bottom: 0; + } + + &__name { + flex: 1 1 auto; + color: $dark-text-color; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + strong { + font-weight: 500; + } + + a { + color: $darker-text-color; + text-decoration: none; + font-size: 14px; + font-weight: 500; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + &:hover, + &:focus, + &:active { + span { + text-decoration: underline; + } + } + } + } + + &__current { + flex: 0 0 auto; + width: 100px; + font-size: 24px; + line-height: 36px; + font-weight: 500; + text-align: center; + color: $secondary-text-color; + } + + &__sparkline { + flex: 0 0 auto; + width: 50px; + + path { + stroke: lighten($highlight-text-color, 6%) !important; + } + } } } diff --git a/app/javascript/flavours/glitch/util/numbers.js b/app/javascript/flavours/glitch/util/numbers.js new file mode 100644 index 000000000..fdd8269ae --- /dev/null +++ b/app/javascript/flavours/glitch/util/numbers.js @@ -0,0 +1,10 @@ +import React, { Fragment } from 'react'; +import { FormattedNumber } from 'react-intl'; + +export const shortNumberFormat = number => { + if (number < 1000) { + return ; + } else { + return K; + } +}; -- cgit From 87ad942d768c99ebc965a3a4e76a3f79e313c110 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Mon, 20 Aug 2018 11:56:34 +0200 Subject: [Glitch] Display replies count in web UI Port 4df9cabb22cbed8f9cd8af45325ecdcc7c72d6cb to glitch-soc --- .../flavours/glitch/components/status_action_bar.js | 15 ++++++++++++++- .../flavours/glitch/styles/components/status.scss | 20 ++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index cd9a2ac67..70aada0e9 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -33,6 +33,16 @@ const messages = defineMessages({ embed: { id: 'status.embed', defaultMessage: 'Embed' }, }); +const obfuscatedCount = count => { + if (count < 0) { + return 0; + } else if (count <= 1) { + return count; + } else { + return '1+'; + } +}; + @injectIntl export default class StatusActionBar extends ImmutablePureComponent { @@ -190,7 +200,10 @@ export default class StatusActionBar extends ImmutablePureComponent { return (
- +
+ + {obfuscatedCount(status.get('replies_count'))} +
{shareButton} diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss index cd17bb4fa..fbc26ed2a 100644 --- a/app/javascript/flavours/glitch/styles/components/status.scss +++ b/app/javascript/flavours/glitch/styles/components/status.scss @@ -417,15 +417,31 @@ align-items: center; display: flex; margin-top: 8px; + + &__counter { + display: inline-flex; + margin-right: 11px; + align-items: center; + + .status__action-bar-button { + margin-right: 4px; + } + + &__label { + display: inline-block; + width: 14px; + font-size: 12px; + font-weight: 500; + color: $action-button-color; + } + } } .status__action-bar-button { - float: left; margin-right: 18px; } .status__action-bar-dropdown { - float: left; height: 23.15px; width: 23.15px; } -- cgit From d62ea55d51ab487d792f99209b98abcdeac218f2 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Wed, 22 Aug 2018 13:01:12 +0200 Subject: Move layout options to their own section --- .../glitch/features/local_settings/page/index.js | 61 ++++++++++++---------- 1 file changed, 32 insertions(+), 29 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js index d3b7b00b5..5608066b0 100644 --- a/app/javascript/flavours/glitch/features/local_settings/page/index.js +++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js @@ -33,35 +33,38 @@ export default class LocalSettingsPage extends React.PureComponent { ({ intl, onChange, settings }) => (

- - - - - - - - - +
+

+ + + + + + + + + +

Date: Wed, 22 Aug 2018 13:17:21 +0200 Subject: Add glitch-soc local setting to display reply counters Defaults to false. --- .../flavours/glitch/components/status.js | 1 + .../glitch/components/status_action_bar.js | 25 ++++++++++++++++++---- .../glitch/features/local_settings/page/index.js | 8 +++++++ .../flavours/glitch/reducers/local_settings.js | 1 + 4 files changed, 31 insertions(+), 4 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index da1f74e6d..a87721ef8 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -528,6 +528,7 @@ export default class Status extends ImmutablePureComponent { {...other} status={status} account={status.get('account')} + showReplyCount={settings.get('show_reply_count')} /> ) : null} {notification ? ( diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index 70aada0e9..8a840030a 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -66,6 +66,7 @@ export default class StatusActionBar extends ImmutablePureComponent { onPin: PropTypes.func, onBookmark: PropTypes.func, withDismiss: PropTypes.bool, + showReplyCount: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -73,6 +74,7 @@ export default class StatusActionBar extends ImmutablePureComponent { // evaluate to false. See react-immutable-pure-component for usage. updateOnProps = [ 'status', + 'showReplyCount', 'withDismiss', ] @@ -144,7 +146,7 @@ export default class StatusActionBar extends ImmutablePureComponent { } render () { - const { status, intl, withDismiss } = this.props; + const { status, intl, withDismiss, showReplyCount } = this.props; const mutingConversation = status.get('muted'); const anonymousAccess = !me; @@ -198,12 +200,27 @@ export default class StatusActionBar extends ImmutablePureComponent { ); - return ( -
+ let replyButton = ( + + ); + if (showReplyCount) { + replyButton = (
- + {replyButton} {obfuscatedCount(status.get('replies_count'))}
+ ); + } + + return ( +
+ {replyButton} {shareButton} diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js index 5608066b0..f88e23c47 100644 --- a/app/javascript/flavours/glitch/features/local_settings/page/index.js +++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js @@ -33,6 +33,14 @@ export default class LocalSettingsPage extends React.PureComponent { ({ intl, onChange, settings }) => (

+ + +