From d61a6271c68ecca1745f2683d25ec58573dd2819 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Sun, 9 Jun 2019 12:07:23 +0200 Subject: Add DM conversations mode similar to upstream --- .../flavours/glitch/components/avatar_composite.js | 104 +++++++++++++++++++++ .../flavours/glitch/components/display_name.js | 44 ++++++++- .../flavours/glitch/components/status.js | 23 ++++- .../glitch/components/status_action_bar.js | 20 ++-- .../flavours/glitch/components/status_header.js | 82 ++++++++++------ .../flavours/glitch/components/status_icons.js | 6 +- 6 files changed, 229 insertions(+), 50 deletions(-) create mode 100644 app/javascript/flavours/glitch/components/avatar_composite.js (limited to 'app/javascript/flavours/glitch/components') diff --git a/app/javascript/flavours/glitch/components/avatar_composite.js b/app/javascript/flavours/glitch/components/avatar_composite.js new file mode 100644 index 000000000..c52df043a --- /dev/null +++ b/app/javascript/flavours/glitch/components/avatar_composite.js @@ -0,0 +1,104 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { autoPlayGif } from 'flavours/glitch/util/initial_state'; + +export default class AvatarComposite extends React.PureComponent { + + static propTypes = { + accounts: ImmutablePropTypes.list.isRequired, + animate: PropTypes.bool, + size: PropTypes.number.isRequired, + }; + + static defaultProps = { + animate: autoPlayGif, + }; + + renderItem (account, size, index) { + const { animate } = this.props; + + let width = 50; + let height = 100; + let top = 'auto'; + let left = 'auto'; + let bottom = 'auto'; + let right = 'auto'; + + if (size === 1) { + width = 100; + } + + if (size === 4 || (size === 3 && index > 0)) { + height = 50; + } + + if (size === 2) { + if (index === 0) { + right = '2px'; + } else { + left = '2px'; + } + } else if (size === 3) { + if (index === 0) { + right = '2px'; + } else if (index > 0) { + left = '2px'; + } + + if (index === 1) { + bottom = '2px'; + } else if (index > 1) { + top = '2px'; + } + } else if (size === 4) { + if (index === 0 || index === 2) { + right = '2px'; + } + + if (index === 1 || index === 3) { + left = '2px'; + } + + if (index < 2) { + bottom = '2px'; + } else { + top = '2px'; + } + } + + const style = { + left: left, + top: top, + right: right, + bottom: bottom, + width: `${width}%`, + height: `${height}%`, + backgroundSize: 'cover', + backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`, + }; + + return ( + this.props.onAccountClick(account.get('id'), e)} + title={`@${account.get('acct')}`} + key={account.get('id')} + > +
+ + ); + } + + render() { + const { accounts, size } = this.props; + + return ( +
+ {accounts.take(4).map((account, i) => this.renderItem(account, accounts.size, i))} +
+ ); + } + +} diff --git a/app/javascript/flavours/glitch/components/display_name.js b/app/javascript/flavours/glitch/components/display_name.js index a26cff049..7f6ef5a5d 100644 --- a/app/javascript/flavours/glitch/components/display_name.js +++ b/app/javascript/flavours/glitch/components/display_name.js @@ -10,24 +10,56 @@ export default function DisplayName ({ className, inline, localDomain, + others, + onAccountClick, }) { const computedClass = classNames('display-name', { inline }, className); if (!account) return null; + let displayName, suffix; + let acct = account.get('acct'); + if (acct.indexOf('@') === -1 && localDomain) { acct = `${acct}@${localDomain}`; } - // The result. - return account ? ( + if (others && others.size > 0) { + displayName = others.take(2).map(a => ( + onAccountClick(a.get('id'), e)} + title={`@${a.get('acct')}`} + > + + + + + )).reduce((prev, cur) => [prev, ', ', cur]); + + if (others.size - 2 > 0) { + displayName.push(` +${others.size - 2}`); + } + + suffix = ( + onAccountClick(account.get('id'), e)}> + @{acct} + + ); + } else { + displayName = ; + suffix = @{acct}; + } + + return ( - + {displayName} {inline ? ' ' : null} - @{acct} + {suffix} - ) : null; + ); } // Props. @@ -36,4 +68,6 @@ DisplayName.propTypes = { className: PropTypes.string, inline: PropTypes.bool, localDomain: PropTypes.string, + others: ImmutablePropTypes.list, + handleClick: PropTypes.func, }; diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 7014cab17..4b9364ae5 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -66,6 +66,7 @@ export default class Status extends ImmutablePureComponent { containerId: PropTypes.string, id: PropTypes.string, status: ImmutablePropTypes.map, + otherAccounts: ImmutablePropTypes.list, account: ImmutablePropTypes.map, onReply: PropTypes.func, onFavourite: PropTypes.func, @@ -83,6 +84,7 @@ export default class Status extends ImmutablePureComponent { muted: PropTypes.bool, collapse: PropTypes.bool, hidden: PropTypes.bool, + unread: PropTypes.bool, prepend: PropTypes.string, withDismiss: PropTypes.bool, onMoveUp: PropTypes.func, @@ -93,6 +95,7 @@ export default class Status extends ImmutablePureComponent { intl: PropTypes.object.isRequired, cacheMediaWidth: PropTypes.func, cachedMediaWidth: PropTypes.number, + onClick: PropTypes.func, }; state = { @@ -321,17 +324,21 @@ export default class Status extends ImmutablePureComponent { const { status } = this.props; const { isCollapsed } = this.state; if (!router) return; - if (destination === undefined) { - destination = `/statuses/${ - status.getIn(['reblog', 'id'], status.get('id')) - }`; - } + if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey)) { if (isCollapsed) this.setCollapsed(false); else if (e.shiftKey) { this.setCollapsed(true); document.getSelection().removeAllRanges(); + } else if (this.props.onClick) { + this.props.onClick(); + return; } else { + if (destination === undefined) { + destination = `/statuses/${ + status.getIn(['reblog', 'id'], status.get('id')) + }`; + } let state = {...router.history.location.state}; state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1; router.history.push(destination, state); @@ -441,6 +448,7 @@ export default class Status extends ImmutablePureComponent { intl, status, account, + otherAccounts, settings, collapsed, muted, @@ -450,6 +458,7 @@ export default class Status extends ImmutablePureComponent { onOpenMedia, notification, hidden, + unread, featured, ...other } = this.props; @@ -617,6 +626,7 @@ export default class Status extends ImmutablePureComponent { collapsed: isCollapsed, 'has-background': isCollapsed && background, 'status__wrapper-reply': !!status.get('in_reply_to_id'), + read: unread === false, muted, }, 'focusable'); @@ -647,6 +657,7 @@ export default class Status extends ImmutablePureComponent { friend={account} collapsed={isCollapsed} parseClick={parseClick} + otherAccounts={otherAccounts} /> ) : null} @@ -656,6 +667,7 @@ export default class Status extends ImmutablePureComponent { collapsible={settings.getIn(['collapsed', 'enabled'])} collapsed={isCollapsed} setCollapsed={setCollapsed} + directMessage={!!otherAccounts} /> ) : 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 4c398fd19..85bc4a976 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -71,6 +71,7 @@ export default class StatusActionBar extends ImmutablePureComponent { onBookmark: PropTypes.func, withDismiss: PropTypes.bool, showReplyCount: PropTypes.bool, + directMessage: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -191,7 +192,7 @@ export default class StatusActionBar extends ImmutablePureComponent { } render () { - const { status, intl, withDismiss, showReplyCount } = this.props; + const { status, intl, withDismiss, showReplyCount, directMessage } = this.props; const mutingConversation = status.get('muted'); const anonymousAccess = !me; @@ -282,14 +283,15 @@ export default class StatusActionBar extends ImmutablePureComponent { return (
{replyButton} - - - {shareButton} - - -
- -
+ {!directMessage && [ + , + , + shareButton, + , +
+ +
, + ]}
diff --git a/app/javascript/flavours/glitch/components/status_header.js b/app/javascript/flavours/glitch/components/status_header.js index f9321904c..23cff286a 100644 --- a/app/javascript/flavours/glitch/components/status_header.js +++ b/app/javascript/flavours/glitch/components/status_header.js @@ -6,6 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; // Mastodon imports. import Avatar from './avatar'; import AvatarOverlay from './avatar_overlay'; +import AvatarComposite from './avatar_composite'; import DisplayName from './display_name'; export default class StatusHeader extends React.PureComponent { @@ -14,12 +15,18 @@ export default class StatusHeader extends React.PureComponent { status: ImmutablePropTypes.map.isRequired, friend: ImmutablePropTypes.map, parseClick: PropTypes.func.isRequired, + otherAccounts: ImmutablePropTypes.list, }; // Handles clicks on account name/image + handleClick = (id, e) => { + const { parseClick } = this.props; + parseClick(e, `/accounts/${id}`); + } + handleAccountClick = (e) => { - const { status, parseClick } = this.props; - parseClick(e, `/accounts/${status.getIn(['account', 'id'])}`); + const { status } = this.props; + this.handleClick(status.getIn(['account', 'id']), e); } // Rendering. @@ -27,36 +34,55 @@ export default class StatusHeader extends React.PureComponent { const { status, friend, + otherAccounts, } = this.props; const account = status.get('account'); - return ( - - ); + let statusAvatar; + if (otherAccounts && otherAccounts.size > 0) { + statusAvatar = ; + } else if (friend === undefined || friend === null) { + statusAvatar = ; + } else { + statusAvatar = ; + } + + if (!otherAccounts) { + return ( + + ); + } else { + // This is a DM conversation + return ( +
+ + {statusAvatar} + + + + + +
+ ); + } } } diff --git a/app/javascript/flavours/glitch/components/status_icons.js b/app/javascript/flavours/glitch/components/status_icons.js index c9747650f..4a2c62881 100644 --- a/app/javascript/flavours/glitch/components/status_icons.js +++ b/app/javascript/flavours/glitch/components/status_icons.js @@ -22,6 +22,7 @@ export default class StatusIcons extends React.PureComponent { mediaIcon: PropTypes.string, collapsible: PropTypes.bool, collapsed: PropTypes.bool, + directMessage: PropTypes.bool, setCollapsed: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, }; @@ -42,6 +43,7 @@ export default class StatusIcons extends React.PureComponent { mediaIcon, collapsible, collapsed, + directMessage, intl, } = this.props; @@ -59,9 +61,7 @@ export default class StatusIcons extends React.PureComponent { aria-hidden='true' /> ) : null} - {( - - )} + {!directMessage && } {collapsible ? ( Date: Fri, 7 Jun 2019 18:57:31 +0200 Subject: Minor cleanup --- app/javascript/flavours/glitch/components/status.js | 2 -- 1 file changed, 2 deletions(-) (limited to 'app/javascript/flavours/glitch/components') diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 4b9364ae5..f6d73475a 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -114,8 +114,6 @@ export default class Status extends ImmutablePureComponent { 'account', 'settings', 'prepend', - 'boostModal', - 'favouriteModal', 'muted', 'collapse', 'notification', -- cgit From 610b4b44c4f70583f2f3082dc8f494fadb0681ef Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 23 May 2019 01:35:22 +0200 Subject: [Glitch] Add single-column mode Port 9ddeb30f90f9402eb567c88354d4956fcfdf0361 to glitch-soc Signed-off-by: Thibaut Girka --- .../glitch/components/autosuggest_input.js | 2 +- .../flavours/glitch/features/compose/index.js | 10 +- .../glitch/features/getting_started/index.js | 17 +- .../glitch/features/ui/components/columns_area.js | 10 +- .../ui/components/notifications_counter_icon.js | 24 +++ .../glitch/features/ui/components/tabs_bar.js | 27 +--- .../flavours/glitch/features/ui/index.js | 14 +- .../flavours/glitch/reducers/settings.js | 2 + .../flavours/glitch/styles/components/columns.scss | 42 +---- .../flavours/glitch/styles/components/drawer.scss | 5 + .../flavours/glitch/styles/components/index.scss | 177 ++++++++++++++++++++- 11 files changed, 247 insertions(+), 83 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js (limited to 'app/javascript/flavours/glitch/components') diff --git a/app/javascript/flavours/glitch/components/autosuggest_input.js b/app/javascript/flavours/glitch/components/autosuggest_input.js index 5fc952d8e..a6ae3bc75 100644 --- a/app/javascript/flavours/glitch/components/autosuggest_input.js +++ b/app/javascript/flavours/glitch/components/autosuggest_input.js @@ -49,7 +49,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { autoFocus: PropTypes.bool, className: PropTypes.string, id: PropTypes.string, - searchTokens: PropTypes.arrayOf(PropTypes.string), + searchTokens: ImmutablePropTypes.list, maxLength: PropTypes.number, }; diff --git a/app/javascript/flavours/glitch/features/compose/index.js b/app/javascript/flavours/glitch/features/compose/index.js index a7795a04d..d3070a199 100644 --- a/app/javascript/flavours/glitch/features/compose/index.js +++ b/app/javascript/flavours/glitch/features/compose/index.js @@ -61,12 +61,12 @@ class Compose extends React.PureComponent {
{!isSearchPage &&
+ - {multiColumn && ( -
- {mascot ? :
- )} + +
+ {mascot ? :
} diff --git a/app/javascript/flavours/glitch/features/getting_started/index.js b/app/javascript/flavours/glitch/features/getting_started/index.js index 7b645c9d0..25fff1974 100644 --- a/app/javascript/flavours/glitch/features/getting_started/index.js +++ b/app/javascript/flavours/glitch/features/getting_started/index.js @@ -10,10 +10,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { me, invitesEnabled, version } from 'flavours/glitch/util/initial_state'; import { fetchFollowRequests } from 'flavours/glitch/actions/accounts'; +import { changeSetting } from 'flavours/glitch/actions/settings'; import { List as ImmutableList } from 'immutable'; import { createSelector } from 'reselect'; import { fetchLists } from 'flavours/glitch/actions/lists'; import { preferencesLink, profileLink, signOutLink } from 'flavours/glitch/util/backend_links'; +import Toggle from 'react-toggle'; const messages = defineMessages({ heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, @@ -52,6 +54,7 @@ const makeMapStateToProps = () => { columns: state.getIn(['settings', 'columns']), unreadFollowRequests: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size, unreadNotifications: state.getIn(['notifications', 'unread']), + forceSingleColumn: state.getIn(['settings', 'forceSingleColumn'], false), }); return mapStateToProps; @@ -61,6 +64,7 @@ const mapDispatchToProps = dispatch => ({ fetchFollowRequests: () => dispatch(fetchFollowRequests()), fetchLists: () => dispatch(fetchLists()), openSettings: () => dispatch(openModal('SETTINGS', {})), + changeForceSingleColumn: checked => dispatch(changeSetting(['forceSingleColumn'], checked)), }); const badgeDisplay = (number, limit) => { @@ -88,6 +92,8 @@ export default class GettingStarted extends ImmutablePureComponent { lists: ImmutablePropTypes.list, fetchLists: PropTypes.func.isRequired, openSettings: PropTypes.func.isRequired, + forceSingleColumn: PropTypes.bool, + changeForceSingleColumn: PropTypes.func.isRequired, }; componentWillMount () { @@ -102,8 +108,12 @@ export default class GettingStarted extends ImmutablePureComponent { } } + handleForceSingleColumnChange = ({ target }) => { + this.props.changeForceSingleColumn(target.checked); + } + render () { - const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings } = this.props; + const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings, forceSingleColumn } = this.props; const navItems = []; let listItems = []; @@ -183,6 +193,11 @@ export default class GettingStarted extends ImmutablePureComponent {

+ + ); } 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 0fe580b9b..5e680a61c 100644 --- a/app/javascript/flavours/glitch/features/ui/components/columns_area.js +++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.js @@ -5,7 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import ReactSwipeableViews from 'react-swipeable-views'; -import { links, getIndex, getLink } from './tabs_bar'; +import TabsBar, { links, getIndex, getLink } from './tabs_bar'; import { Link } from 'react-router-dom'; import BundleContainer from '../containers/bundle_container'; @@ -139,7 +139,7 @@ export default class ColumnsArea extends ImmutablePureComponent { ; return ( -
+
{view}
); @@ -164,13 +164,17 @@ export default class ColumnsArea extends ImmutablePureComponent { const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : ; return columnIndex !== -1 ? [ + , + {links.map(this.renderView)} , floatingActionButton, ] : [ -
{children}
, + , + +
{children}
, floatingActionButton, ]; diff --git a/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js b/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js new file mode 100644 index 000000000..137658b94 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import Icon from 'flavours/glitch/components/icon'; + +const mapStateToProps = state => ({ + count: state.getIn(['notifications', 'unread']), + showBadge: state.getIn(['local_settings', 'notifications', 'tab_badge']), +}); + +const formatNumber = num => num > 99 ? '99+' : num; + +const NotificationsCounterIcon = ({ count, showBadge }) => ( + + + {showBadge && count > 0 && {formatNumber(count)}} + +); + +NotificationsCounterIcon.propTypes = { + count: PropTypes.number.isRequired, +}; + +export default connect(mapStateToProps)(NotificationsCounterIcon); diff --git a/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js b/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js index b44a21a42..8963b16b3 100644 --- a/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js +++ b/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js @@ -4,34 +4,11 @@ import { NavLink, withRouter } from 'react-router-dom'; import { FormattedMessage, injectIntl } from 'react-intl'; import { debounce } from 'lodash'; import { isUserTouching } from 'flavours/glitch/util/is_mobile'; -import { connect } from 'react-redux'; - -const mapStateToProps = state => ({ - unreadNotifications: state.getIn(['notifications', 'unread']), - showBadge: state.getIn(['local_settings', 'notifications', 'tab_badge']), -}); - -@connect(mapStateToProps) -class NotificationsIcon extends React.PureComponent { - static propTypes = { - unreadNotifications: PropTypes.number, - showBadge: PropTypes.bool, - }; - - render() { - const { unreadNotifications, showBadge } = this.props; - return ( - - - { showBadge && unreadNotifications > 0 &&
} - - ); - } -} +import NotificationsCounterIcon from './notifications_counter_icon'; export const links = [ , - , + , , , diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index f8fff934d..36156a5ef 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -2,7 +2,6 @@ import React from 'react'; import NotificationsContainer from './containers/notifications_container'; import PropTypes from 'prop-types'; import LoadingBarContainer from './containers/loading_bar_container'; -import TabsBar from './components/tabs_bar'; import ModalContainer from './containers/modal_container'; import { connect } from 'react-redux'; import { Redirect, withRouter } from 'react-router-dom'; @@ -69,6 +68,7 @@ const mapStateToProps = state => ({ unreadNotifications: state.getIn(['notifications', 'unread']), showFaviconBadge: state.getIn(['local_settings', 'notifications', 'favicon_badge']), hicolorPrivacyIcons: state.getIn(['local_settings', 'hicolor_privacy_icons']), + forceSingleColumn: state.getIn(['settings', 'forceSingleColumn'], false), }); const keyMap = { @@ -126,6 +126,7 @@ export default class UI extends React.Component { dropdownMenuIsOpen: PropTypes.bool, unreadNotifications: PropTypes.number, showFaviconBadge: PropTypes.bool, + forceSingleColumn: PropTypes.bool, }; state = { @@ -431,7 +432,9 @@ export default class UI extends React.Component { render () { const { width, draggingOver } = this.state; - const { children, layout, isWide, navbarUnder, dropdownMenuIsOpen } = this.props; + const { children, layout, isWide, navbarUnder, dropdownMenuIsOpen, forceSingleColumn } = this.props; + const singleColumn = forceSingleColumn || isMobile(width, layout); + const redirect = singleColumn ? : ; const columnsClass = layout => { switch (layout) { @@ -475,11 +478,9 @@ export default class UI extends React.Component { return (
- {navbarUnder ? null : ()} - - + - + {redirect} @@ -518,7 +519,6 @@ export default class UI extends React.Component { - {navbarUnder ? () : null} diff --git a/app/javascript/flavours/glitch/reducers/settings.js b/app/javascript/flavours/glitch/reducers/settings.js index a37863a69..a568183df 100644 --- a/app/javascript/flavours/glitch/reducers/settings.js +++ b/app/javascript/flavours/glitch/reducers/settings.js @@ -15,6 +15,8 @@ const initialState = ImmutableMap({ skinTone: 1, + forceSingleColumn: false, + home: ImmutableMap({ shows: ImmutableMap({ reblog: true, diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index 0bf01ed8d..50438393e 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -13,16 +13,6 @@ position: relative; } -@include limited-single-column('screen and (min-width: 360px)', $parent: null) { - .columns-area { - padding: 10px; - } - - .react-swipeable-view-container .columns-area { - height: calc(100% - 20px) !important; - } -} - .react-swipeable-view-container { &, .columns-area, @@ -63,34 +53,6 @@ overflow: hidden; } -@include limited-single-column('screen and (min-width: 360px)', $parent: null) { - .tabs-bar { - margin: 10px; - margin-bottom: 0; - } -} - -:root { // Overrides .wide stylings for mobile view - @include single-column('screen and (max-width: 630px)', $parent: null) { - .column { - flex: auto; - width: 100%; - min-width: 0; - max-width: none; - padding: 0; - } - - .columns-area { - flex-direction: column; - } - - .search__input, - .autosuggest-textarea__textarea { - font-size: 16px; - } - } -} - @include multi-columns('screen and (min-width: 631px)', $parent: null) { .columns-area { padding: 0; @@ -442,6 +404,10 @@ contain: strict; } + & > span { + max-width: 400px; + } + a { color: $highlight-text-color; text-decoration: none; diff --git a/app/javascript/flavours/glitch/styles/components/drawer.scss b/app/javascript/flavours/glitch/styles/components/drawer.scss index 9f426448f..07558a8d0 100644 --- a/app/javascript/flavours/glitch/styles/components/drawer.scss +++ b/app/javascript/flavours/glitch/styles/components/drawer.scss @@ -276,6 +276,7 @@ background: lighten($ui-base-color, 13%) url('data:image/svg+xml;utf8,') no-repeat bottom / 100% auto; flex: 1; min-height: 47px; + display: none; > img { display: block; @@ -295,6 +296,10 @@ border: none; cursor: inherit; } + + @media screen and (min-height: 640px) { + display: block; + } } .pseudo-drawer { diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 9db64bbcb..8f385e15a 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -553,6 +553,7 @@ } .tabs-bar { + box-sizing: border-box; display: flex; background: lighten($ui-base-color, 8%); flex: 0 0 auto; @@ -590,15 +591,130 @@ } } - span:last-child { + span { margin-left: 5px; display: none; } } -@include multi-columns('screen and (min-width: 631px)', $parent: null) { +@media screen and (min-width: 600px) { + .tabs-bar__link { + span { + display: inline; + } + } +} + +.columns-area--mobile { + flex-direction: column; + width: 100%; + max-width: 600px; + margin: 0 auto; + + .column, + .drawer { + width: 100%; + height: 100%; + padding: 0; + } + + .search__input, + .autosuggest-textarea__textarea { + font-size: 16px; + } + + @media screen and (min-width: 360px) { + padding: 10px; + } + + @media screen and (min-width: 630px) { + .detailed-status { + padding: 15px; + + .media-gallery, + .video-player { + margin-top: 15px; + } + } + + .account__header__bar { + padding: 5px 10px; + } + + .navigation-bar, + .compose-form { + padding: 15px; + } + + .compose-form .compose-form__publish .compose-form__publish-button-wrapper { + padding-top: 15px; + } + + .status { + padding: 15px 15px 15px (48px + 15px * 2); + min-height: 48px + 2px; + + &__avatar { + left: 15px; + top: 17px; + } + + &__content { + padding-top: 5px; + } + + &__prepend { + margin-left: 48px + 15px * 2; + padding-top: 15px; + } + + &__prepend-icon-wrapper { + left: -32px; + } + + .media-gallery, + &__action-bar, + .video-player { + margin-top: 10px; + } + } + } +} + +@media screen and (min-width: 360px) { .tabs-bar { - display: none; + margin: 10px auto; + margin-bottom: 0; + width: calc(100% - 20px); + max-width: 600px; + } + + .react-swipeable-view-container .columns-area--mobile { + height: calc(100% - 20px) !important; + } + + .getting-started__wrapper, + .getting-started__trends, + .search { + margin-bottom: 10px; + } +} + +.icon-with-badge { + position: relative; + + &__badge { + position: absolute; + right: -13px; + top: -13px; + background: $ui-highlight-color; + border: 2px solid lighten($ui-base-color, 8%); + padding: 1px 6px; + border-radius: 6px; + font-size: 10px; + font-weight: 500; + line-height: 14px; + color: $primary-text-color; } } @@ -1272,6 +1388,61 @@ height: 1em; } +.navigational-toggle { + padding: 10px; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 14px; + color: $dark-text-color; +} + +.layout-toggle { + display: flex; + padding: 5px; + + button { + box-sizing: border-box; + flex: 0 0 50%; + background: transparent; + padding: 5px; + border: 0; + position: relative; + + &:hover, + &:focus, + &:active { + svg path:first-child { + fill: lighten($ui-base-color, 16%); + } + } + } + + svg { + width: 100%; + height: auto; + + path:first-child { + fill: lighten($ui-base-color, 12%); + } + + path:last-child { + fill: darken($ui-base-color, 14%); + } + } + + &__active { + color: $ui-highlight-color; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: lighten($ui-base-color, 12%); + border-radius: 50%; + padding: 0.35rem; + } +} + ::-webkit-scrollbar-thumb { border-radius: 0; } -- cgit From d99a661f08398238838bf576e86c4be706ee7aa0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 25 May 2019 21:27:00 +0200 Subject: [Glitch] Add responsive panels to the single-column layout Port 1e5532e693d9533ee37f553aeb191e284178fa52 to glitch-soc Signed-off-by: Thibaut Girka --- app/javascript/flavours/glitch/actions/compose.js | 26 ++-- app/javascript/flavours/glitch/actions/statuses.js | 7 +- .../glitch/components/autosuggest_input.js | 2 +- .../features/compose/components/navigation_bar.js | 2 +- .../glitch/features/compose/components/search.js | 11 +- .../glitch/features/getting_started/index.js | 17 +-- .../flavours/glitch/features/search/index.js | 17 +++ .../glitch/features/ui/components/columns_area.js | 14 +- .../glitch/features/ui/components/compose_panel.js | 41 ++++++ .../glitch/features/ui/components/list_panel.js | 55 ++++++++ .../features/ui/components/navigation_panel.js | 27 ++++ .../ui/components/notifications_counter_icon.js | 6 +- .../glitch/features/ui/components/tabs_bar.js | 13 +- .../flavours/glitch/features/ui/index.js | 9 +- .../flavours/glitch/reducers/settings.js | 2 - .../flavours/glitch/styles/components/columns.scss | 57 ++++---- .../flavours/glitch/styles/components/index.scss | 157 +++++++++++++++++++-- .../flavours/glitch/util/async-components.js | 4 + .../flavours/glitch/util/initial_state.js | 1 + 19 files changed, 374 insertions(+), 94 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/search/index.js create mode 100644 app/javascript/flavours/glitch/features/ui/components/compose_panel.js create mode 100644 app/javascript/flavours/glitch/features/ui/components/list_panel.js create mode 100644 app/javascript/flavours/glitch/features/ui/components/navigation_panel.js (limited to 'app/javascript/flavours/glitch/components') diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js index 2fb97fa17..093952316 100644 --- a/app/javascript/flavours/glitch/actions/compose.js +++ b/app/javascript/flavours/glitch/actions/compose.js @@ -68,6 +68,14 @@ const messages = defineMessages({ uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' }, }); +const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 3); + +export const ensureComposeIsVisible = (getState, routerHistory) => { + if (!getState().getIn(['compose', 'mounted']) && window.innerWidth < COMPOSE_PANEL_BREAKPOINT) { + routerHistory.push('/statuses/new'); + } +}; + export function changeCompose(text) { return { type: COMPOSE_CHANGE, @@ -81,16 +89,14 @@ export function cycleElefriendCompose() { }; }; -export function replyCompose(status, router) { +export function replyCompose(status, routerHistory) { return (dispatch, getState) => { dispatch({ type: COMPOSE_REPLY, status: status, }); - if (router && !getState().getIn(['compose', 'mounted'])) { - router.push('/statuses/new'); - } + ensureComposeIsVisible(getState, routerHistory); }; }; @@ -106,29 +112,25 @@ export function resetCompose() { }; }; -export function mentionCompose(account, router) { +export function mentionCompose(account, routerHistory) { return (dispatch, getState) => { dispatch({ type: COMPOSE_MENTION, account: account, }); - if (!getState().getIn(['compose', 'mounted'])) { - router.push('/statuses/new'); - } + ensureComposeIsVisible(getState, routerHistory); }; }; -export function directCompose(account, router) { +export function directCompose(account, routerHistory) { return (dispatch, getState) => { dispatch({ type: COMPOSE_DIRECT, account: account, }); - if (!getState().getIn(['compose', 'mounted'])) { - router.push('/statuses/new'); - } + ensureComposeIsVisible(getState, routerHistory); }; }; diff --git a/app/javascript/flavours/glitch/actions/statuses.js b/app/javascript/flavours/glitch/actions/statuses.js index 7e22a7f98..4d2bda78b 100644 --- a/app/javascript/flavours/glitch/actions/statuses.js +++ b/app/javascript/flavours/glitch/actions/statuses.js @@ -2,6 +2,7 @@ import api from 'flavours/glitch/util/api'; import { deleteFromTimelines } from './timelines'; import { importFetchedStatus, importFetchedStatuses } from './importer'; +import { ensureComposeIsVisible } from './compose'; export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST'; export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS'; @@ -80,7 +81,7 @@ export function redraft(status, raw_text, content_type) { }; }; -export function deleteStatus(id, router, withRedraft = false) { +export function deleteStatus(id, routerHistory, withRedraft = false) { return (dispatch, getState) => { let status = getState().getIn(['statuses', id]); @@ -97,9 +98,7 @@ export function deleteStatus(id, router, withRedraft = false) { if (withRedraft) { dispatch(redraft(status, response.data.text, response.data.content_type)); - if (!getState().getIn(['compose', 'mounted'])) { - router.push('/statuses/new'); - } + ensureComposeIsVisible(getState, routerHistory); } }).catch(error => { dispatch(deleteStatusFail(id, error)); diff --git a/app/javascript/flavours/glitch/components/autosuggest_input.js b/app/javascript/flavours/glitch/components/autosuggest_input.js index a6ae3bc75..5fc952d8e 100644 --- a/app/javascript/flavours/glitch/components/autosuggest_input.js +++ b/app/javascript/flavours/glitch/components/autosuggest_input.js @@ -49,7 +49,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { autoFocus: PropTypes.bool, className: PropTypes.string, id: PropTypes.string, - searchTokens: ImmutablePropTypes.list, + searchTokens: PropTypes.arrayOf(PropTypes.string), maxLength: PropTypes.number, }; diff --git a/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js b/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js index 59172bb23..3148434f1 100644 --- a/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js +++ b/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js @@ -17,7 +17,7 @@ export default class NavigationBar extends ImmutablePureComponent {
{this.props.account.get('acct')} - + diff --git a/app/javascript/flavours/glitch/features/compose/components/search.js b/app/javascript/flavours/glitch/features/compose/components/search.js index 5fed1567a..5f8c6cd52 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.js +++ b/app/javascript/flavours/glitch/features/compose/components/search.js @@ -60,6 +60,10 @@ class SearchPopout extends React.PureComponent { export default @injectIntl class Search extends React.PureComponent { + static contextTypes = { + router: PropTypes.object.isRequired, + }; + static propTypes = { value: PropTypes.string.isRequired, submitted: PropTypes.bool, @@ -67,6 +71,7 @@ class Search extends React.PureComponent { onSubmit: PropTypes.func.isRequired, onClear: PropTypes.func.isRequired, onShow: PropTypes.func.isRequired, + openInRoute: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -109,8 +114,10 @@ class Search extends React.PureComponent { const { onSubmit } = this.props; switch (e.key) { case 'Enter': - if (onSubmit) { - onSubmit(); + onSubmit(); + + if (this.props.openInRoute) { + this.context.router.history.push('/search'); } break; case 'Escape': diff --git a/app/javascript/flavours/glitch/features/getting_started/index.js b/app/javascript/flavours/glitch/features/getting_started/index.js index 25fff1974..7b645c9d0 100644 --- a/app/javascript/flavours/glitch/features/getting_started/index.js +++ b/app/javascript/flavours/glitch/features/getting_started/index.js @@ -10,12 +10,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { me, invitesEnabled, version } from 'flavours/glitch/util/initial_state'; import { fetchFollowRequests } from 'flavours/glitch/actions/accounts'; -import { changeSetting } from 'flavours/glitch/actions/settings'; import { List as ImmutableList } from 'immutable'; import { createSelector } from 'reselect'; import { fetchLists } from 'flavours/glitch/actions/lists'; import { preferencesLink, profileLink, signOutLink } from 'flavours/glitch/util/backend_links'; -import Toggle from 'react-toggle'; const messages = defineMessages({ heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, @@ -54,7 +52,6 @@ const makeMapStateToProps = () => { columns: state.getIn(['settings', 'columns']), unreadFollowRequests: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size, unreadNotifications: state.getIn(['notifications', 'unread']), - forceSingleColumn: state.getIn(['settings', 'forceSingleColumn'], false), }); return mapStateToProps; @@ -64,7 +61,6 @@ const mapDispatchToProps = dispatch => ({ fetchFollowRequests: () => dispatch(fetchFollowRequests()), fetchLists: () => dispatch(fetchLists()), openSettings: () => dispatch(openModal('SETTINGS', {})), - changeForceSingleColumn: checked => dispatch(changeSetting(['forceSingleColumn'], checked)), }); const badgeDisplay = (number, limit) => { @@ -92,8 +88,6 @@ export default class GettingStarted extends ImmutablePureComponent { lists: ImmutablePropTypes.list, fetchLists: PropTypes.func.isRequired, openSettings: PropTypes.func.isRequired, - forceSingleColumn: PropTypes.bool, - changeForceSingleColumn: PropTypes.func.isRequired, }; componentWillMount () { @@ -108,12 +102,8 @@ export default class GettingStarted extends ImmutablePureComponent { } } - handleForceSingleColumnChange = ({ target }) => { - this.props.changeForceSingleColumn(target.checked); - } - render () { - const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings, forceSingleColumn } = this.props; + const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings } = this.props; const navItems = []; let listItems = []; @@ -193,11 +183,6 @@ export default class GettingStarted extends ImmutablePureComponent {

- - ); } diff --git a/app/javascript/flavours/glitch/features/search/index.js b/app/javascript/flavours/glitch/features/search/index.js new file mode 100644 index 000000000..b35c8ed49 --- /dev/null +++ b/app/javascript/flavours/glitch/features/search/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import SearchContainer from 'flavours/glitch/features/compose/containers/search_container'; +import SearchResultsContainer from 'flavours/glitch/features/compose/containers/search_results_container'; + +const Search = () => ( +
+ + +
+
+ +
+
+
+); + +export default Search; 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 d3d8aebd1..2e28bbe50 100644 --- a/app/javascript/flavours/glitch/features/ui/components/columns_area.js +++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.js @@ -13,6 +13,8 @@ import ColumnLoading from './column_loading'; import DrawerLoading from './drawer_loading'; import BundleColumnError from './bundle_column_error'; import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses, BookmarkedStatuses, ListTimeline } from 'flavours/glitch/util/async-components'; +import ComposePanel from './compose_panel'; +import NavigationPanel from './navigation_panel'; import detectPassiveEvents from 'detect-passive-events'; import { scrollRight } from 'flavours/glitch/util/scroll'; @@ -173,14 +175,22 @@ export default class ColumnsArea extends ImmutablePureComponent { return (
-
+
+
+ +
+
{content}
-
+
+
+ +
+
{floatingActionButton}
diff --git a/app/javascript/flavours/glitch/features/ui/components/compose_panel.js b/app/javascript/flavours/glitch/features/ui/components/compose_panel.js new file mode 100644 index 000000000..8acf89950 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/compose_panel.js @@ -0,0 +1,41 @@ +import React from 'react'; +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 { invitesEnabled, version, repository, source_url } from 'mastodon/initial_state'; +import { Link } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; + +const ComposePanel = () => ( +
+ + + + +
+ +
+
    + {invitesEnabled &&
  • ·
  • } +
  • ·
  • +
  • ·
  • +
  • ·
  • +
  • ·
  • +
  • ·
  • +
  • ·
  • +
  • ·
  • +
  • +
+ +

+ {repository} (v{version}) }} + /> +

+
+
+); + +export default ComposePanel; diff --git a/app/javascript/flavours/glitch/features/ui/components/list_panel.js b/app/javascript/flavours/glitch/features/ui/components/list_panel.js new file mode 100644 index 000000000..50592d357 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/list_panel.js @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { fetchLists } from 'flavours/glitch/actions/lists'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import { NavLink, withRouter } from 'react-router-dom'; +import Icon from 'flavours/glitch/components/icon'; + +const getOrderedLists = createSelector([state => state.get('lists')], lists => { + if (!lists) { + return lists; + } + + return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title'))); +}); + +const mapStateToProps = state => ({ + lists: getOrderedLists(state), +}); + +export default @withRouter +@connect(mapStateToProps) +class ListPanel extends ImmutablePureComponent { + + static propTypes = { + dispatch: PropTypes.func.isRequired, + lists: ImmutablePropTypes.list, + }; + + componentDidMount () { + const { dispatch } = this.props; + dispatch(fetchLists()); + } + + render () { + const { lists } = this.props; + + if (!lists) { + return null; + } + + return ( +
+
+ + {lists.map(list => ( + {list.get('title')} + ))} +
+ ); + } + +} diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js new file mode 100644 index 000000000..68dde7c5c --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js @@ -0,0 +1,27 @@ +import React from 'react'; +import { NavLink, withRouter } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; +import Icon from 'flavours/glitch/components/icon'; +import NotificationsCounterIcon from './notifications_counter_icon'; +import ListPanel from './list_panel'; + +const NavigationPanel = () => ( +
+ + + + + + + + + + +
+ + + +
+); + +export default withRouter(NavigationPanel); diff --git a/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js b/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js index 137658b94..25df35264 100644 --- a/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js +++ b/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js @@ -10,15 +10,17 @@ const mapStateToProps = state => ({ const formatNumber = num => num > 99 ? '99+' : num; -const NotificationsCounterIcon = ({ count, showBadge }) => ( +const NotificationsCounterIcon = ({ count, className, showBadge }) => ( - + {showBadge && count > 0 && {formatNumber(count)}} ); NotificationsCounterIcon.propTypes = { count: PropTypes.number.isRequired, + showBadge: PropTypes.bool, + className: PropTypes.string, }; export default connect(mapStateToProps)(NotificationsCounterIcon); diff --git a/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js b/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js index 8963b16b3..dbd08aa2b 100644 --- a/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js +++ b/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js @@ -7,14 +7,13 @@ import { isUserTouching } from 'flavours/glitch/util/is_mobile'; import NotificationsCounterIcon from './notifications_counter_icon'; export const links = [ - , - , + , + , - , - , - , - - , + , + , + , + , ]; export function getIndex (path) { diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index 36156a5ef..f08b2dab8 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -44,10 +44,11 @@ import { Mutes, PinnedStatuses, Lists, + Search, GettingStartedMisc, } from 'flavours/glitch/util/async-components'; import { HotKeys } from 'react-hotkeys'; -import { me } from 'flavours/glitch/util/initial_state'; +import { me, forceSingleColumn } from 'flavours/glitch/util/initial_state'; import { defineMessages, injectIntl } from 'react-intl'; // Dummy import, to make sure that ends up in the application bundle. @@ -68,7 +69,6 @@ const mapStateToProps = state => ({ unreadNotifications: state.getIn(['notifications', 'unread']), showFaviconBadge: state.getIn(['local_settings', 'notifications', 'favicon_badge']), hicolorPrivacyIcons: state.getIn(['local_settings', 'hicolor_privacy_icons']), - forceSingleColumn: state.getIn(['settings', 'forceSingleColumn'], false), }); const keyMap = { @@ -126,7 +126,6 @@ export default class UI extends React.Component { dropdownMenuIsOpen: PropTypes.bool, unreadNotifications: PropTypes.number, showFaviconBadge: PropTypes.bool, - forceSingleColumn: PropTypes.bool, }; state = { @@ -432,7 +431,7 @@ export default class UI extends React.Component { render () { const { width, draggingOver } = this.state; - const { children, layout, isWide, navbarUnder, dropdownMenuIsOpen, forceSingleColumn } = this.props; + const { children, layout, isWide, navbarUnder, dropdownMenuIsOpen } = this.props; const singleColumn = forceSingleColumn || isMobile(width, layout); const redirect = singleColumn ? : ; @@ -494,7 +493,7 @@ export default class UI extends React.Component { - + diff --git a/app/javascript/flavours/glitch/reducers/settings.js b/app/javascript/flavours/glitch/reducers/settings.js index a568183df..a37863a69 100644 --- a/app/javascript/flavours/glitch/reducers/settings.js +++ b/app/javascript/flavours/glitch/reducers/settings.js @@ -15,8 +15,6 @@ const initialState = ImmutableMap({ skinTone: 1, - forceSingleColumn: false, - home: ImmutableMap({ shows: ImmutableMap({ reblog: true, diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index f372a4830..80d198ff7 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -26,7 +26,12 @@ display: flex; justify-content: flex-end; + &--start { + justify-content: flex-start; + } + &__inner { + width: 285px; pointer-events: auto; height: 100%; } @@ -178,9 +183,31 @@ padding: 15px; text-decoration: none; - &:hover { + &:hover, + &:focus, + &:active { background: lighten($ui-base-color, 11%); } + + &:focus { + outline: 0; + } + + &--transparent { + background: transparent; + color: $ui-secondary-color; + + &:hover, + &:focus, + &:active { + background: transparent; + color: $primary-text-color; + } + + &.active { + color: $ui-highlight-color; + } + } } .column-link__icon { @@ -506,31 +533,3 @@ margin: 0 5px; } } - -.floating-action-button { - position: fixed; - display: flex; - justify-content: center; - align-items: center; - width: 3.9375rem; - height: 3.9375rem; - bottom: 1.3125rem; - right: 1.3125rem; - background: darken($ui-highlight-color, 3%); - color: $white; - border-radius: 50%; - font-size: 21px; - line-height: 21px; - text-decoration: none; - box-shadow: 2px 3px 9px rgba($base-shadow-color, 0.4); - - &:hover, - &:focus, - &:active { - background: lighten($ui-highlight-color, 7%); - } - - @media screen and (min-width: 630px) { - display: none; - } -} diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 53b678c9a..81098e52c 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -564,6 +564,7 @@ display: block; flex: 1 1 auto; padding: 15px 10px; + padding-bottom: 13px; color: $primary-text-color; text-decoration: none; text-align: center; @@ -588,6 +589,7 @@ &:active { @include multi-columns('screen and (min-width: 631px)') { background: lighten($ui-base-color, 14%); + border-bottom-color: lighten($ui-base-color, 14%); } } @@ -617,11 +619,21 @@ padding: 0; } - .search__input, .autosuggest-textarea__textarea { font-size: 16px; } + .search__input { + line-height: 18px; + font-size: 16px; + padding: 15px; + padding-right: 30px; + } + + .search__icon .fa { + top: 15px; + } + @media screen and (min-width: 360px) { padding: 10px 0; } @@ -677,6 +689,58 @@ margin-top: 10px; } } + + .account { + padding: 15px 10px; + } + + .notification { + &__message { + margin-left: 48px + 15px * 2; + padding-top: 15px; + } + + &__favourite-icon-wrapper { + left: -32px; + } + + .status { + padding-top: 8px; + } + + .account { + padding-top: 8px; + } + + .account__avatar-wrapper { + margin-left: 17px; + margin-right: 15px; + } + } + } +} + +.floating-action-button { + position: fixed; + display: flex; + justify-content: center; + align-items: center; + width: 3.9375rem; + height: 3.9375rem; + bottom: 1.3125rem; + right: 1.3125rem; + background: darken($ui-highlight-color, 3%); + color: $white; + border-radius: 50%; + font-size: 21px; + line-height: 21px; + text-decoration: none; + box-shadow: 2px 3px 9px rgba($base-shadow-color, 0.4); + + &:hover, + &:focus, + &:active { + background: lighten($ui-highlight-color, 7%); } } @@ -698,12 +762,41 @@ } } +@media screen and (max-width: 600px + (285px * 1) + (10px * 1)) { + .columns-area__panels__pane--compositional { + display: none; + } +} + +@media screen and (min-width: 600px + (285px * 1) + (10px * 1)) { + .floating-action-button, + .tabs-bar__link.optional { + display: none; + } + + .search-page .search { + display: none; + } +} + +@media screen and (max-width: 600px + (285px * 2) + (10px * 2)) { + .columns-area__panels__pane--navigational { + display: none; + } +} + +@media screen and (min-width: 600px + (285px * 2) + (10px * 2)) { + .tabs-bar { + display: none; + } +} + .icon-with-badge { position: relative; &__badge { position: absolute; - right: -13px; + left: 9px; top: -13px; background: $ui-highlight-color; border: 2px solid lighten($ui-base-color, 8%); @@ -716,6 +809,57 @@ } } +.column-link--transparent .icon-with-badge__badge { + border-color: darken($ui-base-color, 8%); +} + +.compose-panel { + width: 285px; + margin-top: 10px; + display: flex; + flex-direction: column; + height: 100%; + + .search__input { + line-height: 18px; + font-size: 16px; + padding: 15px; + padding-right: 30px; + } + + .search__icon .fa { + top: 15px; + } + + .navigation-bar { + padding-top: 20px; + padding-bottom: 20px; + } + + .flex-spacer { + background: transparent; + } + + .autosuggest-textarea__textarea { + max-height: 200px; + } + + .compose-form__upload-thumbnail { + height: 80px; + } +} + +.navigation-panel { + margin-top: 10px; + + hr { + border: 0; + background: transparent; + border-top: 1px solid lighten($ui-base-color, 4%); + margin: 10px 0; + } +} + .scrollable { overflow-y: scroll; overflow-x: hidden; @@ -1386,15 +1530,6 @@ height: 1em; } -.navigational-toggle { - padding: 10px; - display: flex; - align-items: center; - justify-content: space-between; - font-size: 14px; - color: $dark-text-color; -} - .layout-toggle { display: flex; padding: 5px; diff --git a/app/javascript/flavours/glitch/util/async-components.js b/app/javascript/flavours/glitch/util/async-components.js index 094952204..f2aeda834 100644 --- a/app/javascript/flavours/glitch/util/async-components.js +++ b/app/javascript/flavours/glitch/util/async-components.js @@ -149,3 +149,7 @@ export function GettingStartedMisc () { export function ListAdder () { return import(/* webpackChunkName: "features/glitch/async/list_adder" */'flavours/glitch/features/list_adder'); } + +export function Search () { + return import(/*webpackChunkName: "features/glitch/async/search" */'flavours/glitch/features/search'); +} diff --git a/app/javascript/flavours/glitch/util/initial_state.js b/app/javascript/flavours/glitch/util/initial_state.js index 99d8a4dbc..72fd2e6f4 100644 --- a/app/javascript/flavours/glitch/util/initial_state.js +++ b/app/javascript/flavours/glitch/util/initial_state.js @@ -28,5 +28,6 @@ export const version = getMeta('version'); export const mascot = getMeta('mascot'); export const isStaff = getMeta('is_staff'); export const defaultContentType = getMeta('default_content_type'); +export const forceSingleColumn = !getMeta('advanced_layout'); export default initialState; -- cgit From ff88387a4afff249c14511780c59e485a49631b8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 26 May 2019 02:55:37 +0200 Subject: [Glitch] Improvements to the single column layout Port 0e445ebb1392c8dbce320509d219f16c7c221406 to glitch-soc Signed-off-by: Thibaut Girka --- app/javascript/flavours/glitch/actions/compose.js | 2 +- .../flavours/glitch/components/icon_with_badge.js | 20 ++++++++++ .../glitch/features/follow_requests/index.js | 2 +- .../glitch/features/getting_started/index.js | 19 ++++++++-- .../ui/components/follow_requests_nav_link.js | 44 ++++++++++++++++++++++ .../glitch/features/ui/components/list_panel.js | 4 +- .../features/ui/components/navigation_panel.js | 2 + .../ui/components/notifications_counter_icon.js | 25 ++---------- 8 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 app/javascript/flavours/glitch/components/icon_with_badge.js create mode 100644 app/javascript/flavours/glitch/features/ui/components/follow_requests_nav_link.js (limited to 'app/javascript/flavours/glitch/components') diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js index 093952316..69cc6827f 100644 --- a/app/javascript/flavours/glitch/actions/compose.js +++ b/app/javascript/flavours/glitch/actions/compose.js @@ -68,7 +68,7 @@ const messages = defineMessages({ uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' }, }); -const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 3); +const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1); export const ensureComposeIsVisible = (getState, routerHistory) => { if (!getState().getIn(['compose', 'mounted']) && window.innerWidth < COMPOSE_PANEL_BREAKPOINT) { diff --git a/app/javascript/flavours/glitch/components/icon_with_badge.js b/app/javascript/flavours/glitch/components/icon_with_badge.js new file mode 100644 index 000000000..4a15ee5b4 --- /dev/null +++ b/app/javascript/flavours/glitch/components/icon_with_badge.js @@ -0,0 +1,20 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Icon from 'flavours/glitch/components/icon'; + +const formatNumber = num => num > 40 ? '40+' : num; + +const IconWithBadge = ({ id, count, className }) => ( + + + {count > 0 && {formatNumber(count)}} + +); + +IconWithBadge.propTypes = { + id: PropTypes.string.isRequired, + count: PropTypes.number.isRequired, + className: PropTypes.string, +}; + +export default IconWithBadge; diff --git a/app/javascript/flavours/glitch/features/follow_requests/index.js b/app/javascript/flavours/glitch/features/follow_requests/index.js index bce6338ea..d0845769e 100644 --- a/app/javascript/flavours/glitch/features/follow_requests/index.js +++ b/app/javascript/flavours/glitch/features/follow_requests/index.js @@ -59,7 +59,7 @@ export default class FollowRequests extends ImmutablePureComponent { } return ( - + diff --git a/app/javascript/flavours/glitch/features/getting_started/index.js b/app/javascript/flavours/glitch/features/getting_started/index.js index 7b645c9d0..b07789562 100644 --- a/app/javascript/flavours/glitch/features/getting_started/index.js +++ b/app/javascript/flavours/glitch/features/getting_started/index.js @@ -73,9 +73,15 @@ const badgeDisplay = (number, limit) => { } }; -@connect(makeMapStateToProps, mapDispatchToProps) -@injectIntl -export default class GettingStarted extends ImmutablePureComponent { +const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2); + + export default @connect(makeMapStateToProps, mapDispatchToProps) + @injectIntl + class GettingStarted extends ImmutablePureComponent { + + static contextTypes = { + router: PropTypes.object.isRequired, + }; static propTypes = { intl: PropTypes.object.isRequired, @@ -97,6 +103,11 @@ export default class GettingStarted extends ImmutablePureComponent { componentDidMount () { const { myAccount, fetchFollowRequests } = this.props; + if (window.innerWidth >= NAVIGATION_PANEL_BREAKPOINT) { + this.context.router.history.replace('/timelines/home'); + return; + } + if (myAccount.get('locked')) { fetchFollowRequests(); } @@ -135,7 +146,7 @@ export default class GettingStarted extends ImmutablePureComponent { } if (myAccount.get('locked')) { - navItems.push(); + navItems.push(); } navItems.push(); diff --git a/app/javascript/flavours/glitch/features/ui/components/follow_requests_nav_link.js b/app/javascript/flavours/glitch/features/ui/components/follow_requests_nav_link.js new file mode 100644 index 000000000..189f403bd --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/follow_requests_nav_link.js @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { fetchFollowRequests } from 'flavours/glitch/actions/accounts'; +import { connect } from 'react-redux'; +import { NavLink, withRouter } from 'react-router-dom'; +import IconWithBadge from 'flavours/glitch/components/icon_with_badge'; +import { me } from 'flavours/glitch/util/initial_state'; +import { List as ImmutableList } from 'immutable'; +import { FormattedMessage } from 'react-intl'; + +const mapStateToProps = state => ({ + locked: state.getIn(['accounts', me, 'locked']), + count: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size, +}); + +export default @withRouter +@connect(mapStateToProps) +class FollowRequestsNavLink extends React.Component { + + static propTypes = { + dispatch: PropTypes.func.isRequired, + locked: PropTypes.bool, + count: PropTypes.number.isRequired, + }; + + componentDidMount () { + const { dispatch, locked } = this.props; + + if (locked) { + dispatch(fetchFollowRequests()); + } + } + + render () { + const { locked, count } = this.props; + + if (!locked || count === 0) { + return null; + } + + return ; + } + +} diff --git a/app/javascript/flavours/glitch/features/ui/components/list_panel.js b/app/javascript/flavours/glitch/features/ui/components/list_panel.js index 50592d357..b2e6925b7 100644 --- a/app/javascript/flavours/glitch/features/ui/components/list_panel.js +++ b/app/javascript/flavours/glitch/features/ui/components/list_panel.js @@ -13,7 +13,7 @@ const getOrderedLists = createSelector([state => state.get('lists')], lists => { return lists; } - return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title'))); + return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title'))).take(4); }); const mapStateToProps = state => ({ @@ -37,7 +37,7 @@ class ListPanel extends ImmutablePureComponent { render () { const { lists } = this.props; - if (!lists) { + if (!lists || lists.isEmpty()) { return null; } 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 68dde7c5c..c75bffe4d 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js @@ -3,12 +3,14 @@ import { NavLink, withRouter } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; import Icon from 'flavours/glitch/components/icon'; import NotificationsCounterIcon from './notifications_counter_icon'; +import FollowRequestsNavLink from './follow_requests_nav_link'; import ListPanel from './list_panel'; const NavigationPanel = () => (
+ diff --git a/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js b/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js index 25df35264..679c02dce 100644 --- a/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js +++ b/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js @@ -1,26 +1,9 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import Icon from 'flavours/glitch/components/icon'; +import IconWithBadge from 'flavours/glitch/components/icon'; const mapStateToProps = state => ({ - count: state.getIn(['notifications', 'unread']), - showBadge: state.getIn(['local_settings', 'notifications', 'tab_badge']), + count: state.getIn(['local_settings', 'notifications', 'tab_badge']) ? state.getIn(['notifications', 'unread']) : 0, + icon: 'bell', }); -const formatNumber = num => num > 99 ? '99+' : num; - -const NotificationsCounterIcon = ({ count, className, showBadge }) => ( - - - {showBadge && count > 0 && {formatNumber(count)}} - -); - -NotificationsCounterIcon.propTypes = { - count: PropTypes.number.isRequired, - showBadge: PropTypes.bool, - className: PropTypes.string, -}; - -export default connect(mapStateToProps)(NotificationsCounterIcon); +export default connect(mapStateToProps)(IconWithBadge); -- cgit From 1329308bc716b32bf2de57c9302c1434acbfe980 Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Sun, 2 Jun 2019 17:05:54 +0900 Subject: [Glitch] Improvement variable height in single column layout Port d93b82af87de90eaa29eb54a423722fb9fb45b38 to glitch-soc Signed-off-by: Thibaut Girka --- .../glitch/components/autosuggest_textarea.js | 56 ++++++++++++---------- .../features/compose/components/compose_form.js | 45 +++++++++-------- .../glitch/features/ui/components/compose_panel.js | 3 -- .../flavours/glitch/features/ui/index.js | 2 +- .../glitch/styles/components/composer.scss | 17 ++++++- .../flavours/glitch/styles/components/index.scss | 27 ++++++++++- .../glitch/styles/mastodon-light/diff.scss | 2 +- 7 files changed, 94 insertions(+), 58 deletions(-) (limited to 'app/javascript/flavours/glitch/components') diff --git a/app/javascript/flavours/glitch/components/autosuggest_textarea.js b/app/javascript/flavours/glitch/components/autosuggest_textarea.js index cf3907fbf..7a6b9d8ac 100644 --- a/app/javascript/flavours/glitch/components/autosuggest_textarea.js +++ b/app/javascript/flavours/glitch/components/autosuggest_textarea.js @@ -192,7 +192,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { } render () { - const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props; + const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props; const { suggestionsHidden } = this.state; const style = { direction: 'ltr' }; @@ -200,34 +200,38 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { style.direction = 'rtl'; } - return ( -
-