diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2022-09-29 04:39:33 +0200 |
---|---|---|
committer | Claire <claire.github-309c@sitedethib.com> | 2022-10-09 15:29:14 +0200 |
commit | 0ff1d62c7a5f6b62799a028364b819a5b0686af1 (patch) | |
tree | 867e570c7cca9f1b239731765628d666c6a037b5 /app/javascript/flavours | |
parent | 022547fbdf28351a6cf67ef281e372e9eff455b4 (diff) |
[Glitch] Add logged-out access to the web UI
Port part of 43b5d5e38d2b8ad8f1d1ad0911c3c1718159c912 to glitch-soc Signed-off-by: Claire <claire.github-309c@sitedethib.com>
Diffstat (limited to 'app/javascript/flavours')
18 files changed, 275 insertions, 68 deletions
diff --git a/app/javascript/flavours/glitch/actions/accounts.js b/app/javascript/flavours/glitch/actions/accounts.js index f5871beb3..750740879 100644 --- a/app/javascript/flavours/glitch/actions/accounts.js +++ b/app/javascript/flavours/glitch/actions/accounts.js @@ -553,10 +553,12 @@ export function expandFollowingFail(id, error) { export function fetchRelationships(accountIds) { return (dispatch, getState) => { - const loadedRelationships = getState().get('relationships'); + const state = getState(); + const loadedRelationships = state.get('relationships'); const newAccountIds = accountIds.filter(id => loadedRelationships.get(id, null) === null); + const signedIn = !!state.getIn(['meta', 'me']); - if (newAccountIds.length === 0) { + if (!signedIn || newAccountIds.length === 0) { return; } diff --git a/app/javascript/flavours/glitch/actions/markers.js b/app/javascript/flavours/glitch/actions/markers.js index a086def97..6a0549f7f 100644 --- a/app/javascript/flavours/glitch/actions/markers.js +++ b/app/javascript/flavours/glitch/actions/markers.js @@ -1,6 +1,7 @@ import api from 'flavours/glitch/util/api'; import { debounce } from 'lodash'; import compareId from 'flavours/glitch/util/compare_id'; +import { List as ImmutableList } from 'immutable'; export const MARKERS_FETCH_REQUEST = 'MARKERS_FETCH_REQUEST'; export const MARKERS_FETCH_SUCCESS = 'MARKERS_FETCH_SUCCESS'; @@ -11,7 +12,7 @@ export const synchronouslySubmitMarkers = () => (dispatch, getState) => { const accessToken = getState().getIn(['meta', 'access_token'], ''); const params = _buildParams(getState()); - if (Object.keys(params).length === 0) { + if (Object.keys(params).length === 0 || accessToken === '') { return; } @@ -63,7 +64,7 @@ export const synchronouslySubmitMarkers = () => (dispatch, getState) => { const _buildParams = (state) => { const params = {}; - const lastHomeId = state.getIn(['timelines', 'home', 'items']).find(item => item !== null); + const lastHomeId = state.getIn(['timelines', 'home', 'items'], ImmutableList()).find(item => item !== null); const lastNotificationId = state.getIn(['notifications', 'lastReadId']); if (lastHomeId && compareId(lastHomeId, state.getIn(['markers', 'home'])) > 0) { @@ -82,9 +83,10 @@ const _buildParams = (state) => { }; const debouncedSubmitMarkers = debounce((dispatch, getState) => { - const params = _buildParams(getState()); + const accessToken = getState().getIn(['meta', 'access_token'], ''); + const params = _buildParams(getState()); - if (Object.keys(params).length === 0) { + if (Object.keys(params).length === 0 || accessToken === '') { return; } diff --git a/app/javascript/flavours/glitch/containers/mastodon.js b/app/javascript/flavours/glitch/containers/mastodon.js index d07b2b3d0..a6ec5845d 100644 --- a/app/javascript/flavours/glitch/containers/mastodon.js +++ b/app/javascript/flavours/glitch/containers/mastodon.js @@ -31,7 +31,7 @@ const createIdentityContext = state => ({ signedIn: !!state.meta.me, accountId: state.meta.me, accessToken: state.meta.access_token, - permissions: state.role.permissions, + permissions: state.role ? state.role.permissions : 0, }); export default class Mastodon extends React.PureComponent { diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js index cc2a7d4d4..866122cbd 100644 --- a/app/javascript/flavours/glitch/features/account/components/header.js +++ b/app/javascript/flavours/glitch/features/account/components/header.js @@ -124,6 +124,7 @@ class Header extends ImmutablePureComponent { render () { const { account, hidden, intl, domain } = this.props; + const { signedIn } = this.context.identity; if (!account) { return null; @@ -157,12 +158,12 @@ class Header extends ImmutablePureComponent { } if (me !== account.get('id')) { - if (!account.get('relationship')) { // Wait until the relationship is loaded + if (signedIn && !account.get('relationship')) { // Wait until the relationship is loaded actionBtn = ''; } else if (account.getIn(['relationship', 'requested'])) { actionBtn = <Button className={classNames('logo-button', { 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />; } else if (!account.getIn(['relationship', 'blocking'])) { - actionBtn = <Button className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']), 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />; + actionBtn = <Button className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']), 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={signedIn ? this.props.onFollow : undefined} />; } else if (account.getIn(['relationship', 'blocking'])) { actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />; } @@ -182,7 +183,7 @@ class Header extends ImmutablePureComponent { lockedIcon = <Icon id='lock' title={intl.formatMessage(messages.account_locked)} />; } - if (account.get('id') !== me && !suspended) { + if (signedIn && account.get('id') !== me && !suspended) { menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention }); menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect }); menu.push(null); @@ -209,7 +210,7 @@ class Header extends ImmutablePureComponent { menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }); - } else { + } else if (signedIn) { if (account.getIn(['relationship', 'following'])) { if (!account.getIn(['relationship', 'muting'])) { if (account.getIn(['relationship', 'showing_reblogs'])) { @@ -242,7 +243,7 @@ class Header extends ImmutablePureComponent { menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport }); } - if (account.get('acct') !== account.get('username')) { + if (signedIn && account.get('acct') !== account.get('username')) { const domain = account.get('acct').split('@')[1]; menu.push(null); @@ -301,7 +302,7 @@ class Header extends ImmutablePureComponent { </React.Fragment> )} - <DropdownMenuContainer items={menu} icon='ellipsis-v' size={24} direction='right' /> + <DropdownMenuContainer disabled={menu.length === 0} items={menu} icon='ellipsis-v' size={24} direction='right' /> </div> )} </div> @@ -313,7 +314,7 @@ class Header extends ImmutablePureComponent { </h1> </div> - <AccountNoteContainer account={account} /> + {signedIn && <AccountNoteContainer account={account} />} {!(suspended || hidden) && ( <div className='account__header__extra'> diff --git a/app/javascript/flavours/glitch/features/explore/results.js b/app/javascript/flavours/glitch/features/explore/results.js index 8eac63c2c..b50ca5465 100644 --- a/app/javascript/flavours/glitch/features/explore/results.js +++ b/app/javascript/flavours/glitch/features/explore/results.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { FormattedMessage } from 'react-intl'; +import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; import { expandSearch } from 'flavours/glitch/actions/search'; import Account from 'flavours/glitch/containers/account_container'; @@ -11,9 +11,14 @@ import { List as ImmutableList } from 'immutable'; import LoadMore from 'flavours/glitch/components/load_more'; import LoadingIndicator from 'flavours/glitch/components/loading_indicator'; +const messages = defineMessages({ + title: { id: 'search_results.title', defaultMessage: 'Search for {q}' }, +}); + const mapStateToProps = state => ({ isLoading: state.getIn(['search', 'isLoading']), results: state.getIn(['search', 'results']), + q: state.getIn(['search', 'searchTerm']), }); const appendLoadMore = (id, list, onLoadMore) => { @@ -37,6 +42,7 @@ const renderStatuses = (results, onLoadMore) => appendLoadMore('statuses', resul )), onLoadMore); export default @connect(mapStateToProps) +@injectIntl class Results extends React.PureComponent { static propTypes = { @@ -44,6 +50,8 @@ class Results extends React.PureComponent { isLoading: PropTypes.bool, multiColumn: PropTypes.bool, dispatch: PropTypes.func.isRequired, + q: PropTypes.string, + intl: PropTypes.object, }; state = { @@ -64,7 +72,7 @@ class Results extends React.PureComponent { } render () { - const { isLoading, results } = this.props; + const { intl, isLoading, q, results } = this.props; const { type } = this.state; let filteredResults = ImmutableList(); diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js b/app/javascript/flavours/glitch/features/hashtag_timeline/index.js index 87a52b269..1b73f4b8a 100644 --- a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js +++ b/app/javascript/flavours/glitch/features/hashtag_timeline/index.js @@ -31,6 +31,10 @@ class HashtagTimeline extends React.PureComponent { disconnects = []; + static contextTypes = { + identity: PropTypes.object, + }; + static propTypes = { params: PropTypes.object.isRequired, columnId: PropTypes.string, @@ -158,6 +162,11 @@ class HashtagTimeline extends React.PureComponent { handleFollow = () => { const { dispatch, params, tag } = this.props; const { id } = params; + const { signedIn } = this.context.identity; + + if (!signedIn) { + return; + } if (tag.get('following')) { dispatch(unfollowHashtag(id)); @@ -170,6 +179,7 @@ class HashtagTimeline extends React.PureComponent { const { hasUnread, columnId, multiColumn, tag, intl } = this.props; const { id, local } = this.props.params; const pinned = !!columnId; + const { signedIn } = this.context.identity; let followButton; @@ -177,7 +187,7 @@ class HashtagTimeline extends React.PureComponent { const following = tag.get('following'); followButton = ( - <button className={classNames('column-header__button')} onClick={this.handleFollow} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-pressed={following ? 'true' : 'false'}> + <button className={classNames('column-header__button')} onClick={this.handleFollow} disabled={!signedIn} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-pressed={following ? 'true' : 'false'}> <Icon id={following ? 'user-times' : 'user-plus'} fixedWidth className='column-header__icon' /> </button> ); diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.js b/app/javascript/flavours/glitch/features/ui/components/columns_area.js index 048251fa6..5f5018105 100644 --- a/app/javascript/flavours/glitch/features/ui/components/columns_area.js +++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.js @@ -60,6 +60,7 @@ class ColumnsArea extends ImmutablePureComponent { static contextTypes = { router: PropTypes.object.isRequired, + identity: PropTypes.object.isRequired, }; static propTypes = { @@ -213,11 +214,12 @@ class ColumnsArea extends ImmutablePureComponent { render () { const { columns, children, singleColumn, intl, navbarUnder, openSettings } = this.props; const { shouldAnimate, renderComposePanel } = this.state; + const { signedIn } = this.context.identity; const columnIndex = getIndex(this.context.router.history.location.pathname); if (singleColumn) { - const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/publish' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>; + const floatingActionButton = (!signedIn || shouldHideFAB(this.context.router.history.location.pathname)) ? null : <Link key='floating-action-button' to='/publish' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>; const content = columnIndex !== -1 ? ( <ReactSwipeableViews key='content' hysteresis={0.2} threshold={15} index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={disableSwiping}> diff --git a/app/javascript/flavours/glitch/features/ui/components/compose_panel.js b/app/javascript/flavours/glitch/features/ui/components/compose_panel.js index 498f09ab6..298c15a8a 100644 --- a/app/javascript/flavours/glitch/features/ui/components/compose_panel.js +++ b/app/javascript/flavours/glitch/features/ui/components/compose_panel.js @@ -1,16 +1,40 @@ import React from 'react'; +import PropTypes from 'prop-types'; import SearchContainer from 'flavours/glitch/features/compose/containers/search_container'; import ComposeFormContainer from 'flavours/glitch/features/compose/containers/compose_form_container'; import NavigationContainer from 'flavours/glitch/features/compose/containers/navigation_container'; import LinkFooter from './link_footer'; -const ComposePanel = () => ( - <div className='compose-panel'> - <SearchContainer openInRoute /> - <NavigationContainer /> - <ComposeFormContainer singleColumn /> - <LinkFooter withHotkeys /> - </div> -); +export default +class ComposePanel extends React.PureComponent { -export default ComposePanel; + static contextTypes = { + identity: PropTypes.object.isRequired, + }; + + render() { + const { signedIn } = this.context.identity; + + return ( + <div className='compose-panel'> + <SearchContainer openInRoute /> + + {!signedIn && ( + <React.Fragment> + <div className='flex-spacer' /> + </React.Fragment> + )} + + {signedIn && ( + <React.Fragment> + <NavigationContainer /> + <ComposeFormContainer singleColumn /> + </React.Fragment> + )} + + <LinkFooter withHotkeys /> + </div> + ); + } + +}; diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.js index 67fa067f9..bb8f89e0f 100644 --- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js +++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.js @@ -48,18 +48,48 @@ class LinkFooter extends React.PureComponent { } render () { + const { signedIn, permissions } = this.context.identity; + + const items = []; + + if ((this.context.identity.permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS) { + items.push(<a key='invites' href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a>); + } + + if (signedIn && securityLink) { + items.push(<a key='security' href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a>); + } + + if (!limitedFederationMode) { + items.push(<a key='about' href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a>); + } + + if (profileDirectory) { + items.push(<Link key='directory' to='/directory'><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></Link>); + } + + items.push(<a key='apps' href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a>); + + if (privacyPolicyLink) { + items.push(<a key='terms' href={privacyPolicyLink} target='_blank'><FormattedMessage id='getting_started.privacy_policy' defaultMessage='Privacy Policy' /></a>); + } + + if (signedIn) { + items.push(<a key='developers' href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a>); + } + + items.push(<a key='docs' href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a>); + + if (signedIn) { + items.push(<a key='logout' href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a>); + } + return ( <div className='getting-started__footer'> <ul> - {((this.context.identity.permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS) && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>} - {!!securityLink && <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>} - {!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>} - {profileDirectory && <li><Link to='/directory'><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></Link> · </li>} - <li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li> - <li><a href={privacyPolicyLink} target='_blank'><FormattedMessage id='getting_started.privacy_policy' defaultMessage='Privacy Policy' /></a> · </li> - <li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li> - <li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li> - <li><a href={signOutLink} onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li> + {items.map((item, index, array) => ( + <li>{item} { index === array.length - 1 ? null : ' · ' }</li> + ))} </ul> <p> diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js index f4d649456..453276775 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js @@ -1,5 +1,6 @@ import React from 'react'; -import { NavLink, withRouter } from 'react-router-dom'; +import PropTypes from 'prop-types'; +import { NavLink, Link } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; import Icon from 'flavours/glitch/components/icon'; import { showTrends } from 'flavours/glitch/util/initial_state'; @@ -8,30 +9,70 @@ import NotificationsCounterIcon from './notifications_counter_icon'; import FollowRequestsNavLink from './follow_requests_nav_link'; import ListPanel from './list_panel'; import TrendsContainer from 'flavours/glitch/features/getting_started/containers/trends_container'; +import SignInBanner from './sign_in_banner'; -const NavigationPanel = ({ onOpenSettings }) => ( - <div className='navigation-panel'> - <NavLink className='column-link column-link--transparent' to='/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon className='column-link__icon' id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink> - <NavLink className='column-link column-link--transparent' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon className='column-link__icon' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink> - <FollowRequestsNavLink /> - { showTrends && <NavLink className='column-link column-link--transparent' to='/explore' data-preview-title-id='explore.title' data-preview-icon='hashtag'><Icon className='column-link__icon' id='hashtag' fixedWidth /><FormattedMessage id='explore.title' defaultMessage='Explore' /></NavLink> } - <NavLink className='column-link column-link--transparent' to='/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink> - <NavLink className='column-link column-link--transparent' exact to='/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon className='column-link__icon' id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink> - <NavLink className='column-link column-link--transparent' to='/conversations'><Icon className='column-link__icon' id='envelope' fixedWidth /><FormattedMessage id='navigation_bar.direct' defaultMessage='Direct messages' /></NavLink> - <NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink> - <NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink> +export default class NavigationPanel extends React.Component { - <ListPanel /> + static contextTypes = { + router: PropTypes.object.isRequired, + identity: PropTypes.object.isRequired, + }; - <hr /> + static propTypes = { + onOpenSettings: PropTypes.func, + }; - {!!preferencesLink && <a className='column-link column-link--transparent' href={preferencesLink} target='_blank'><Icon className='column-link__icon' id='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' id='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' id='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>} + render() { + const { signedIn } = this.context.identity; + const { onOpenSettings } = this.props; - {showTrends && <div className='flex-spacer' />} - {showTrends && <TrendsContainer />} - </div> -); + return ( + <div className='navigation-panel'> + {signedIn && ( + <React.Fragment> + <NavLink className='column-link column-link--transparent' to='/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon className='column-link__icon' id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink> + <NavLink className='column-link column-link--transparent' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon className='column-link__icon' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink> + <FollowRequestsNavLink /> + </React.Fragment> + )} -export default withRouter(NavigationPanel); + { showTrends && <NavLink className='column-link column-link--transparent' to='/explore' data-preview-title-id='explore.title' data-preview-icon='hashtag'><Icon className='column-link__icon' id='hashtag' fixedWidth /><FormattedMessage id='explore.title' defaultMessage='Explore' /></NavLink> } + + <NavLink className='column-link column-link--transparent' to='/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink> + <NavLink className='column-link column-link--transparent' exact to='/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon className='column-link__icon' id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink> + + {!signedIn && ( + <React.Fragment> + <hr /> + <SignInBanner /> + </React.Fragment> + )} + + {signedIn && ( + <React.Fragment> + <NavLink className='column-link column-link--transparent' to='/conversations'><Icon className='column-link__icon' id='envelope' fixedWidth /><FormattedMessage id='navigation_bar.direct' defaultMessage='Direct messages' /></NavLink> + <NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink> + <NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink> + + <ListPanel /> + + <hr /> + + {!!preferencesLink && <a className='column-link column-link--transparent' href={preferencesLink} target='_blank'><Icon className='column-link__icon' id='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' id='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' id='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>} + </React.Fragment> + )} + + {showTrends && ( + <React.Fragment> + <div className='flex-spacer' /> + <TrendsContainer /> + </React.Fragment> + )} + + </div> + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.js b/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.js new file mode 100644 index 000000000..c8403a8ad --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.js @@ -0,0 +1,11 @@ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; + +const SignInBanner = () => ( + <div className='sign-in-banner'> + <p><FormattedMessage id='sign_in_banner.text' defaultMessage='Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.' /></p> + <a href='/auth/sign_in' className='button button--block'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a> + </div> +); + +export default SignInBanner; diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index 7b547fd5b..15661914b 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -121,6 +121,10 @@ const keyMap = { class SwitchingColumnsArea extends React.PureComponent { + static contextTypes = { + identity: PropTypes.object, + }; + static propTypes = { children: PropTypes.node, location: PropTypes.object, @@ -157,12 +161,25 @@ class SwitchingColumnsArea extends React.PureComponent { render () { const { children, mobile, navbarUnder } = this.props; - const redirect = mobile ? <Redirect from='/' to='/home' exact /> : <Redirect from='/' to='/getting-started' exact />; + const { signedIn } = this.context.identity; + + let redirect; + + if (signedIn) { + if (mobile) { + redirect = <Redirect from='/' to='/home' exact />; + } else { + redirect = <Redirect from='/' to='/getting-started' exact />; + } + } else { + redirect = <Redirect from='/' to='/explore' exact />; + } return ( <ColumnsAreaContainer ref={this.setRef} singleColumn={mobile} navbarUnder={navbarUnder}> <WrappedSwitch> {redirect} + <WrappedRoute path='/getting-started' component={GettingStarted} content={children} /> <WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} /> @@ -219,6 +236,10 @@ export default @connect(mapStateToProps) @withRouter class UI extends React.Component { + static contextTypes = { + identity: PropTypes.object.isRequired, + }; + static propTypes = { dispatch: PropTypes.func.isRequired, children: PropTypes.node, @@ -358,6 +379,8 @@ class UI extends React.Component { } componentDidMount () { + const { signedIn } = this.context.identity; + window.addEventListener('beforeunload', this.handleBeforeUnload, false); window.addEventListener('resize', this.handleResize, { passive: true }); @@ -374,16 +397,18 @@ class UI extends React.Component { this.favicon = new Favico({ animation:"none" }); // On first launch, redirect to the follow recommendations page - if (this.props.firstLaunch) { + if (signedIn && this.props.firstLaunch) { this.context.router.history.replace('/start'); this.props.dispatch(closeOnboarding()); } - this.props.dispatch(fetchMarkers()); - this.props.dispatch(expandHomeTimeline()); - this.props.dispatch(expandNotifications()); + if (signedIn) { + this.props.dispatch(fetchMarkers()); + this.props.dispatch(expandHomeTimeline()); + this.props.dispatch(expandNotifications()); - setTimeout(() => this.props.dispatch(fetchRules()), 3000); + setTimeout(() => this.props.dispatch(fetchRules()), 3000); + } this.hotkeys.__mousetrap__.stopCallback = (e, element) => { return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName); diff --git a/app/javascript/flavours/glitch/styles/_mixins.scss b/app/javascript/flavours/glitch/styles/_mixins.scss index c92bc8608..9f6314f3f 100644 --- a/app/javascript/flavours/glitch/styles/_mixins.scss +++ b/app/javascript/flavours/glitch/styles/_mixins.scss @@ -60,6 +60,7 @@ font-family: inherit; background: $ui-base-color; color: $darker-text-color; + border-radius: 4px; font-size: 14px; margin: 0; } diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index 1440682f3..f5b712499 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -117,6 +117,7 @@ box-sizing: border-box; width: 100%; background: lighten($ui-base-color, 4%); + border-radius: 4px 4px 0 0; color: $highlight-text-color; cursor: pointer; flex: 0 0 auto; @@ -204,6 +205,17 @@ color: $highlight-text-color; } } + + &--logo { + background: transparent; + padding: 10px; + + &:hover, + &:focus, + &:active { + background: transparent; + } + } } .column-link__icon { @@ -255,6 +267,7 @@ display: flex; font-size: 16px; background: lighten($ui-base-color, 4%); + border-radius: 4px 4px 0 0; flex: 0 0 auto; cursor: pointer; position: relative; @@ -309,6 +322,7 @@ > .scrollable { background: $ui-base-color; + border-radius: 0 0 4px 4px; } } diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 14fbc61b5..cf5339d36 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -110,6 +110,7 @@ &:hover { border-color: lighten($ui-primary-color, 4%); color: lighten($darker-text-color, 4%); + text-decoration: none; } &:disabled { @@ -1756,3 +1757,4 @@ noscript { @import 'single_column'; @import 'announcements'; @import 'explore'; +@import 'signed_out'; diff --git a/app/javascript/flavours/glitch/styles/components/signed_out.scss b/app/javascript/flavours/glitch/styles/components/signed_out.scss new file mode 100644 index 000000000..2b5cccd92 --- /dev/null +++ b/app/javascript/flavours/glitch/styles/components/signed_out.scss @@ -0,0 +1,8 @@ +.sign-in-banner { + padding: 10px; + + p { + color: $darker-text-color; + margin-bottom: 20px; + } +} diff --git a/app/javascript/flavours/glitch/styles/components/single_column.scss b/app/javascript/flavours/glitch/styles/components/single_column.scss index 3843bcd68..bbb8b456c 100644 --- a/app/javascript/flavours/glitch/styles/components/single_column.scss +++ b/app/javascript/flavours/glitch/styles/components/single_column.scss @@ -6,6 +6,26 @@ height: calc(100% - 10px); overflow-y: hidden; + .hero-widget { + box-shadow: none; + + &__text, + &__img, + &__img img { + border-radius: 0; + } + + &__text { + padding: 15px; + color: $secondary-text-color; + + strong { + font-weight: 700; + color: $primary-text-color; + } + } + } + .search__input { line-height: 18px; font-size: 16px; @@ -21,10 +41,6 @@ flex: 0 1 48px; } - .flex-spacer { - background: transparent; - } - .composer { flex: 1; overflow-y: hidden; @@ -61,6 +77,14 @@ flex: 0 0 auto; } + .logo { + height: 30px; + width: auto; + } +} + +.navigation-panel, +.compose-panel { hr { flex: 0 0 auto; border: 0; diff --git a/app/javascript/flavours/glitch/util/initial_state.js b/app/javascript/flavours/glitch/util/initial_state.js index 90dada4b3..30f789a06 100644 --- a/app/javascript/flavours/glitch/util/initial_state.js +++ b/app/javascript/flavours/glitch/util/initial_state.js @@ -11,6 +11,7 @@ const initialState = element && function () { const getMeta = (prop) => initialState && initialState.meta && initialState.meta[prop]; +export const domain = getMeta('domain'); export const reduceMotion = getMeta('reduce_motion'); export const autoPlayGif = getMeta('auto_play_gif'); export const displayMedia = getMeta('display_media') || (getMeta('display_sensitive_media') ? 'show_all' : 'default'); @@ -37,5 +38,6 @@ export const useSystemEmojiFont = getMeta('system_emoji_font'); export const showTrends = getMeta('trends'); export const disableSwiping = getMeta('disable_swiping'); export const languages = initialState && initialState.languages; +export const server = initialState && initialState.server; export default initialState; |