diff options
Diffstat (limited to 'app/javascript')
-rw-r--r-- | app/javascript/mastodon/actions/compose.js | 8 | ||||
-rw-r--r-- | app/javascript/mastodon/features/compose/components/navigation_bar.js | 5 | ||||
-rw-r--r-- | app/javascript/mastodon/features/compose/index.js | 13 | ||||
-rw-r--r-- | app/javascript/mastodon/features/ui/index.js | 15 | ||||
-rw-r--r-- | app/javascript/mastodon/reducers/compose.js | 8 | ||||
-rw-r--r-- | app/javascript/packs/public.js | 14 | ||||
-rw-r--r-- | app/javascript/styles/accounts.scss | 13 | ||||
-rw-r--r-- | app/javascript/styles/components.scss | 68 | ||||
-rw-r--r-- | app/javascript/styles/forms.scss | 4 |
9 files changed, 145 insertions, 3 deletions
diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 2ce4e9b4e..5a486f9bb 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -28,6 +28,7 @@ export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE'; export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE'; export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE'; export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE'; +export const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE'; export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT'; @@ -288,3 +289,10 @@ export function insertEmojiCompose(position, emoji) { emoji, }; }; + +export function changeComposing(value) { + return { + type: COMPOSE_COMPOSING_CHANGE, + value, + }; +} diff --git a/app/javascript/mastodon/features/compose/components/navigation_bar.js b/app/javascript/mastodon/features/compose/components/navigation_bar.js index 7983edb85..b0bc0958e 100644 --- a/app/javascript/mastodon/features/compose/components/navigation_bar.js +++ b/app/javascript/mastodon/features/compose/components/navigation_bar.js @@ -1,6 +1,8 @@ import React from 'react'; +import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Avatar from '../../../components/avatar'; +import IconButton from '../../../components/icon_button'; import Permalink from '../../../components/permalink'; import { FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; @@ -9,6 +11,7 @@ export default class NavigationBar extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, + onClose: PropTypes.func.isRequired, }; render () { @@ -25,6 +28,8 @@ export default class NavigationBar extends ImmutablePureComponent { <a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a> </div> + + <IconButton title='' icon='close' onClick={this.props.onClose} /> </div> ); } diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index 69bead689..66b0746c5 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -13,6 +13,7 @@ import SearchContainer from './containers/search_container'; import Motion from 'react-motion/lib/Motion'; import spring from 'react-motion/lib/spring'; import SearchResultsContainer from './containers/search_results_container'; +import { changeComposing } from '../../actions/compose'; const messages = defineMessages({ start: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, @@ -59,6 +60,14 @@ export default class Compose extends React.PureComponent { this.props.dispatch(openModal('SETTINGS', {})); } + onFocus = () => { + this.props.dispatch(changeComposing(true)); + } + + onBlur = () => { + this.props.dispatch(changeComposing(false)); + } + render () { const { multiColumn, showSearch, intl } = this.props; @@ -96,8 +105,8 @@ export default class Compose extends React.PureComponent { <SearchContainer /> <div className='drawer__pager'> - <div className='drawer__inner'> - <NavigationContainer /> + <div className='drawer__inner' onFocus={this.onFocus}> + <NavigationContainer onClose={this.onBlur} /> <ComposeFormContainer /> </div> diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 5a0398eb4..1edc7504c 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -45,6 +45,7 @@ const mapStateToProps = state => ({ systemFontUi: state.getIn(['meta', 'system_font_ui']), layout: state.getIn(['local_settings', 'layout']), isWide: state.getIn(['local_settings', 'stretch']), + isComposing: state.getIn(['compose', 'is_composing']), }); @connect(mapStateToProps) @@ -56,6 +57,7 @@ export default class UI extends React.PureComponent { layout: PropTypes.string, isWide: PropTypes.bool, systemFontUi: PropTypes.bool, + isComposing: PropTypes.bool, }; state = { @@ -137,6 +139,19 @@ export default class UI extends React.PureComponent { this.props.dispatch(refreshNotifications()); } + shouldComponentUpdate (nextProps) { + if (nextProps.isComposing !== this.props.isComposing) { + // Avoid expensive update just to toggle a class + this.node.classList.toggle('is-composing', nextProps.isComposing); + + return false; + } + + // Why isn't this working?!? + // return super.shouldComponentUpdate(nextProps, nextState); + return true; + } + componentWillUnmount () { window.removeEventListener('resize', this.handleResize); document.removeEventListener('dragenter', this.handleDragEnter); diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 4dce634a4..7c98854a2 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -21,6 +21,7 @@ import { COMPOSE_SPOILERNESS_CHANGE, COMPOSE_SPOILER_TEXT_CHANGE, COMPOSE_VISIBILITY_CHANGE, + COMPOSE_COMPOSING_CHANGE, COMPOSE_EMOJI_INSERT, } from '../actions/compose'; import { TIMELINE_DELETE } from '../actions/timelines'; @@ -41,6 +42,7 @@ const initialState = ImmutableMap({ focusDate: null, preselectDate: null, in_reply_to: null, + is_composing: false, is_submitting: false, is_uploading: false, progress: 0, @@ -154,7 +156,9 @@ export default function compose(state = initialState, action) { case COMPOSE_MOUNT: return state.set('mounted', true); case COMPOSE_UNMOUNT: - return state.set('mounted', false); + return state + .set('mounted', false) + .set('is_composing', false); case COMPOSE_ADVANCED_OPTIONS_CHANGE: return state .set('advanced_options', @@ -182,6 +186,8 @@ export default function compose(state = initialState, action) { return state .set('text', action.text) .set('idempotencyKey', uuid()); + case COMPOSE_COMPOSING_CHANGE: + return state.set('is_composing', action.value); case COMPOSE_REPLY: return state.withMutations(map => { map.set('in_reply_to', action.status.get('id')); diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index e34c47fd0..e9bb4a42e 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -90,6 +90,20 @@ function main() { noteCounter.textContent = 500 - length(noteWithoutMetadata); } }); + + delegate(document, '#account_avatar', 'change', ({ target }) => { + const avatar = document.querySelector('.card.compact .avatar img'); + const [file] = target.files || []; + const url = URL.createObjectURL(file); + avatar.src = url; + }); + + delegate(document, '#account_header', 'change', ({ target }) => { + const header = document.querySelector('.card.compact'); + const [file] = target.files || []; + const url = URL.createObjectURL(file); + header.style.backgroundImage = `url(${url})`; + }); } loadPolyfills().then(main).catch(error => { diff --git a/app/javascript/styles/accounts.scss b/app/javascript/styles/accounts.scss index 95b097f41..3d5c1a692 100644 --- a/app/javascript/styles/accounts.scss +++ b/app/javascript/styles/accounts.scss @@ -31,6 +31,19 @@ } } + &.compact { + padding: 30px 0; + border-radius: 4px; + + .avatar { + margin-bottom: 0; + + img { + object-fit: cover; + } + } + } + .name { display: block; position: relative; diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 3e80569a9..d67e2ca69 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -1464,6 +1464,11 @@ .permalink { text-decoration: none; } + + .icon-button { + pointer-events: none; + opacity: 0; + } } .navigation-bar__profile { @@ -4160,3 +4165,66 @@ noscript { margin: 20px 0; } } + +@media screen and (max-width: 1024px) and (max-height: 400px) { + $duration: 400ms; + $delay: 100ms; + + .tabs-bar, + .search { + will-change: margin-top; + transition: margin-top $duration $delay; + } + + .navigation-bar { + will-change: padding-bottom; + transition: padding-bottom $duration $delay; + } + + .navigation-bar { + & > a:first-child { + will-change: margin-top, margin-left, width; + transition: margin-top $duration $delay, margin-left $duration ($duration + $delay); + } + + & > .navigation-bar__profile-edit { + will-change: margin-top; + transition: margin-top $duration $delay; + } + + & > .icon-button { + will-change: opacity; + transition: opacity $duration $delay; + } + } + + .is-composing { + .tabs-bar, + .search { + margin-top: -50px; + } + + .navigation-bar { + padding-bottom: 0; + + & > a:first-child { + margin-top: -50px; + margin-left: -40px; + } + + .navigation-bar__profile { + padding-top: 2px; + } + + .navigation-bar__profile-edit { + position: absolute; + margin-top: -50px; + } + + .icon-button { + pointer-events: auto; + opacity: 1; + } + } + } +} diff --git a/app/javascript/styles/forms.scss b/app/javascript/styles/forms.scss index e1de36d55..c467aa7db 100644 --- a/app/javascript/styles/forms.scss +++ b/app/javascript/styles/forms.scss @@ -40,6 +40,10 @@ code { } } + .card { + margin-bottom: 15px; + } + strong { font-weight: 500; } |