forked from treehouse/mastodon
[Glitch] Add trends UI with admin and user settings
Port 9072fe5ab6
to glitch-soc
Signed-off-by: Thibaut Girka <thib@sitedethib.com>
signup-info-prompt
parent
3c70fb9146
commit
8b630f7e54
|
@ -0,0 +1,32 @@
|
||||||
|
import api from 'flavours/glitch/util/api';
|
||||||
|
|
||||||
|
export const TRENDS_FETCH_REQUEST = 'TRENDS_FETCH_REQUEST';
|
||||||
|
export const TRENDS_FETCH_SUCCESS = 'TRENDS_FETCH_SUCCESS';
|
||||||
|
export const TRENDS_FETCH_FAIL = 'TRENDS_FETCH_FAIL';
|
||||||
|
|
||||||
|
export const fetchTrends = () => (dispatch, getState) => {
|
||||||
|
dispatch(fetchTrendsRequest());
|
||||||
|
|
||||||
|
api(getState)
|
||||||
|
.get('/api/v1/trends')
|
||||||
|
.then(({ data }) => dispatch(fetchTrendsSuccess(data)))
|
||||||
|
.catch(err => dispatch(fetchTrendsFail(err)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchTrendsRequest = () => ({
|
||||||
|
type: TRENDS_FETCH_REQUEST,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fetchTrendsSuccess = trends => ({
|
||||||
|
type: TRENDS_FETCH_SUCCESS,
|
||||||
|
trends,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fetchTrendsFail = error => ({
|
||||||
|
type: TRENDS_FETCH_FAIL,
|
||||||
|
error,
|
||||||
|
skipLoading: true,
|
||||||
|
skipAlert: true,
|
||||||
|
});
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import Hashtag from 'flavours/glitch/components/hashtag';
|
||||||
|
|
||||||
|
export default class Trends extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
trends: ImmutablePropTypes.list,
|
||||||
|
fetchTrends: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
this.props.fetchTrends();
|
||||||
|
this.refreshInterval = setInterval(() => this.props.fetchTrends(), 36000);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
if (this.refreshInterval) {
|
||||||
|
clearInterval(this.refreshInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { trends } = this.props;
|
||||||
|
|
||||||
|
if (!trends || trends.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='getting-started__trends'>
|
||||||
|
{trends.take(3).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { fetchTrends } from '../../../actions/trends';
|
||||||
|
import Trends from '../components/trends';
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
trends: state.getIn(['trends', 'items']),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
fetchTrends: () => dispatch(fetchTrends()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(Trends);
|
|
@ -8,7 +8,7 @@ import { openModal } from 'flavours/glitch/actions/modal';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
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 { me, profile_directory } from 'flavours/glitch/util/initial_state';
|
import { me, profile_directory, showTrends } from 'flavours/glitch/util/initial_state';
|
||||||
import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
|
import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
@ -16,6 +16,7 @@ import { fetchLists } from 'flavours/glitch/actions/lists';
|
||||||
import { preferencesLink } from 'flavours/glitch/util/backend_links';
|
import { preferencesLink } from 'flavours/glitch/util/backend_links';
|
||||||
import NavigationBar from '../compose/components/navigation_bar';
|
import NavigationBar from '../compose/components/navigation_bar';
|
||||||
import LinkFooter from 'flavours/glitch/features/ui/components/link_footer';
|
import LinkFooter from 'flavours/glitch/features/ui/components/link_footer';
|
||||||
|
import TrendsContainer from './containers/trends_container';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
||||||
|
@ -182,6 +183,8 @@ const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
|
||||||
|
|
||||||
<LinkFooter />
|
<LinkFooter />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{multiColumn && showTrends && <TrendsContainer />}
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,12 @@ import React from 'react';
|
||||||
import { NavLink, withRouter } from 'react-router-dom';
|
import { NavLink, withRouter } from 'react-router-dom';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import Icon from 'flavours/glitch/components/icon';
|
import Icon from 'flavours/glitch/components/icon';
|
||||||
import { profile_directory } from 'flavours/glitch/util/initial_state';
|
import { profile_directory, showTrends } from 'flavours/glitch/util/initial_state';
|
||||||
import { preferencesLink, relationshipsLink } from 'flavours/glitch/util/backend_links';
|
import { preferencesLink, relationshipsLink } from 'flavours/glitch/util/backend_links';
|
||||||
import NotificationsCounterIcon from './notifications_counter_icon';
|
import NotificationsCounterIcon from './notifications_counter_icon';
|
||||||
import FollowRequestsNavLink from './follow_requests_nav_link';
|
import FollowRequestsNavLink from './follow_requests_nav_link';
|
||||||
import ListPanel from './list_panel';
|
import ListPanel from './list_panel';
|
||||||
|
import TrendsContainer from 'flavours/glitch/features/getting_started/containers/trends_container';
|
||||||
|
|
||||||
const NavigationPanel = ({ onOpenSettings }) => (
|
const NavigationPanel = ({ onOpenSettings }) => (
|
||||||
<div className='navigation-panel'>
|
<div className='navigation-panel'>
|
||||||
|
@ -27,6 +28,9 @@ const NavigationPanel = ({ onOpenSettings }) => (
|
||||||
{!!preferencesLink && <a className='column-link column-link--transparent' href={preferencesLink} target='_blank'><Icon className='column-link__icon' icon='cog' fixedWidth /><FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' /></a>}
|
{!!preferencesLink && <a className='column-link column-link--transparent' href={preferencesLink} target='_blank'><Icon className='column-link__icon' icon='cog' fixedWidth /><FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' /></a>}
|
||||||
<a className='column-link column-link--transparent' href='#' onClick={onOpenSettings}><Icon className='column-link__icon' icon='cogs' fixedWidth /><FormattedMessage id='navigation_bar.app_settings' defaultMessage='App settings' /></a>
|
<a className='column-link column-link--transparent' href='#' onClick={onOpenSettings}><Icon className='column-link__icon' icon='cogs' fixedWidth /><FormattedMessage id='navigation_bar.app_settings' defaultMessage='App settings' /></a>
|
||||||
{!!relationshipsLink && <a className='column-link column-link--transparent' href={relationshipsLink} target='_blank'><Icon className='column-link__icon' icon='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>}
|
{!!relationshipsLink && <a className='column-link column-link--transparent' href={relationshipsLink} target='_blank'><Icon className='column-link__icon' icon='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>}
|
||||||
|
|
||||||
|
{showTrends && <div className='flex-spacer' />}
|
||||||
|
{showTrends && <TrendsContainer />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import suggestions from './suggestions';
|
||||||
import pinnedAccountsEditor from './pinned_accounts_editor';
|
import pinnedAccountsEditor from './pinned_accounts_editor';
|
||||||
import polls from './polls';
|
import polls from './polls';
|
||||||
import identity_proofs from './identity_proofs';
|
import identity_proofs from './identity_proofs';
|
||||||
|
import trends from './trends';
|
||||||
|
|
||||||
const reducers = {
|
const reducers = {
|
||||||
dropdown_menu,
|
dropdown_menu,
|
||||||
|
@ -69,6 +70,7 @@ const reducers = {
|
||||||
suggestions,
|
suggestions,
|
||||||
pinnedAccountsEditor,
|
pinnedAccountsEditor,
|
||||||
polls,
|
polls,
|
||||||
|
trends,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default combineReducers(reducers);
|
export default combineReducers(reducers);
|
||||||
|
|
|
@ -15,6 +15,10 @@ const initialState = ImmutableMap({
|
||||||
|
|
||||||
skinTone: 1,
|
skinTone: 1,
|
||||||
|
|
||||||
|
trends: ImmutableMap({
|
||||||
|
show: true,
|
||||||
|
}),
|
||||||
|
|
||||||
home: ImmutableMap({
|
home: ImmutableMap({
|
||||||
shows: ImmutableMap({
|
shows: ImmutableMap({
|
||||||
reblog: true,
|
reblog: true,
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { TRENDS_FETCH_REQUEST, TRENDS_FETCH_SUCCESS, TRENDS_FETCH_FAIL } from '../actions/trends';
|
||||||
|
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||||
|
|
||||||
|
const initialState = ImmutableMap({
|
||||||
|
items: ImmutableList(),
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function trendsReducer(state = initialState, action) {
|
||||||
|
switch(action.type) {
|
||||||
|
case TRENDS_FETCH_REQUEST:
|
||||||
|
return state.set('isLoading', true);
|
||||||
|
case TRENDS_FETCH_SUCCESS:
|
||||||
|
return state.withMutations(map => {
|
||||||
|
map.set('items', fromJS(action.trends));
|
||||||
|
map.set('isLoading', false);
|
||||||
|
});
|
||||||
|
case TRENDS_FETCH_FAIL:
|
||||||
|
return state.set('isLoading', false);
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
|
@ -903,6 +903,38 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__trends {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
opacity: 1;
|
||||||
|
animation: fade 150ms linear;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
@media screen and (max-height: 810px) {
|
||||||
|
.trends__item:nth-child(3) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-height: 720px) {
|
||||||
|
.trends__item:nth-child(2) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-height: 670px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trends__item {
|
||||||
|
border-bottom: 0;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&__current {
|
||||||
|
color: $darker-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-link__badge {
|
.column-link__badge {
|
||||||
|
|
|
@ -147,7 +147,8 @@
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 36px;
|
line-height: 36px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-align: center;
|
text-align: right;
|
||||||
|
padding-right: 15px;
|
||||||
color: $secondary-text-color;
|
color: $secondary-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +156,12 @@
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
|
|
||||||
path {
|
path:first-child {
|
||||||
|
fill: rgba($highlight-text-color, 0.25) !important;
|
||||||
|
fill-opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
path:last-child {
|
||||||
stroke: lighten($highlight-text-color, 6%) !important;
|
stroke: lighten($highlight-text-color, 6%) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,13 +54,24 @@
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
height: calc(100% - 20px);
|
height: calc(100% - 20px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
& > a {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
|
flex: 0 0 auto;
|
||||||
border: 0;
|
border: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-top: 1px solid lighten($ui-base-color, 4%);
|
border-top: 1px solid lighten($ui-base-color, 4%);
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-spacer {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 600px) {
|
@media screen and (min-width: 600px) {
|
||||||
|
@ -216,7 +227,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.getting-started__wrapper,
|
.getting-started__wrapper,
|
||||||
.getting-started__trends,
|
|
||||||
.search {
|
.search {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,5 +33,6 @@ export const forceSingleColumn = getMeta('advanced_layout') === false;
|
||||||
export const useBlurhash = getMeta('use_blurhash');
|
export const useBlurhash = getMeta('use_blurhash');
|
||||||
export const usePendingItems = getMeta('use_pending_items');
|
export const usePendingItems = getMeta('use_pending_items');
|
||||||
export const useSystemEmojiFont = getMeta('system_emoji_font');
|
export const useSystemEmojiFont = getMeta('system_emoji_font');
|
||||||
|
export const showTrends = getMeta('trends');
|
||||||
|
|
||||||
export default initialState;
|
export default initialState;
|
||||||
|
|
Loading…
Reference in New Issue