diff --git a/Gemfile.lock b/Gemfile.lock index 33e355a06b..9ae2d7c417 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -154,6 +154,7 @@ GEM net-http-persistent (~> 4.0) nokogiri (~> 1, >= 1.10.8) base64 (0.1.1) + bcp47_spec (0.2.1) bcrypt (3.1.19) better_errors (2.10.1) erubi (>= 1.0.0) @@ -375,19 +376,19 @@ GEM reline (>= 0.3.8) jmespath (1.6.2) json (2.6.3) - json-canonicalization (0.3.2) + json-canonicalization (1.0.0) json-jwt (1.15.3) activesupport (>= 4.2) aes_key_wrap bindata httpclient - json-ld (3.2.5) + json-ld (3.3.1) htmlentities (~> 4.3) - json-canonicalization (~> 0.3, >= 0.3.2) + json-canonicalization (~> 1.0) link_header (~> 0.0, >= 0.0.8) multi_json (~> 1.15) rack (>= 2.2, < 4) - rdf (~> 3.2, >= 3.2.10) + rdf (~> 3.3) json-ld-preloaded (3.2.2) json-ld (~> 3.2) rdf (~> 3.2) @@ -596,7 +597,8 @@ GEM zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.0.6) - rdf (3.2.11) + rdf (3.3.1) + bcp47_spec (~> 0.2) link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.6.1) rdf (~> 3.2) diff --git a/app/javascript/flavours/glitch/components/account.jsx b/app/javascript/flavours/glitch/components/account.jsx index 6342ef6f48..00bf537bbb 100644 --- a/app/javascript/flavours/glitch/components/account.jsx +++ b/app/javascript/flavours/glitch/components/account.jsx @@ -17,7 +17,6 @@ import { Avatar } from './avatar'; import { Button } from './button'; import { FollowersCounter } from './counters'; import { DisplayName } from './display_name'; -import { IconButton } from './icon_button'; import Permalink from './permalink'; import { RelativeTimestamp } from './relative_timestamp'; @@ -45,10 +44,7 @@ class Account extends ImmutablePureComponent { intl: PropTypes.object.isRequired, hidden: PropTypes.bool, minimal: PropTypes.bool, - actionIcon: PropTypes.string, - actionTitle: PropTypes.string, defaultAction: PropTypes.string, - onActionClick: PropTypes.func, withBio: PropTypes.bool, }; @@ -76,12 +72,8 @@ class Account extends ImmutablePureComponent { this.props.onMuteNotifications(this.props.account, false); }; - handleAction = () => { - this.props.onActionClick(this.props.account); - }; - render () { - const { account, intl, hidden, withBio, onActionClick, actionIcon, actionTitle, defaultAction, size, minimal } = this.props; + const { account, intl, hidden, withBio, defaultAction, size, minimal } = this.props; if (!account) { return ; @@ -98,9 +90,7 @@ class Account extends ImmutablePureComponent { let buttons; - if (actionIcon && onActionClick) { - buttons = ; - } else if (!actionIcon && account.get('id') !== me && account.get('relationship', null) !== null) { + if (account.get('id') !== me && account.get('relationship', null) !== null) { const following = account.getIn(['relationship', 'following']); const requested = account.getIn(['relationship', 'requested']); const blocking = account.getIn(['relationship', 'blocking']); diff --git a/app/javascript/flavours/glitch/components/avatar.tsx b/app/javascript/flavours/glitch/components/avatar.tsx index e69e9950d1..7ed2d003ef 100644 --- a/app/javascript/flavours/glitch/components/avatar.tsx +++ b/app/javascript/flavours/glitch/components/avatar.tsx @@ -5,49 +5,44 @@ import { autoPlayGif } from '../initial_state'; import type { Account } from '../types/resources'; interface Props { - account: Account | undefined; - className?: string; + account: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there size: number; style?: React.CSSProperties; inline?: boolean; + animate?: boolean; } export const Avatar: React.FC = ({ account, - className, + animate = autoPlayGif, size = 20, inline = false, style: styleFromParent, }) => { - const { hovering, handleMouseEnter, handleMouseLeave } = - useHovering(autoPlayGif); + const { hovering, handleMouseEnter, handleMouseLeave } = useHovering(animate); const style = { ...styleFromParent, width: `${size}px`, height: `${size}px`, - backgroundSize: `${size}px ${size}px`, }; - if (account) { - style.backgroundImage = `url(${account.get( - hovering ? 'avatar' : 'avatar_static', - )})`; - } + const src = + hovering || animate + ? account?.get('avatar') + : account?.get('avatar_static'); return (
+ > + {src && {account?.get('acct')}} +
); }; diff --git a/app/javascript/flavours/glitch/components/avatar_composite.jsx b/app/javascript/flavours/glitch/components/avatar_composite.jsx index 762834f0fc..c736f1dd53 100644 --- a/app/javascript/flavours/glitch/components/avatar_composite.jsx +++ b/app/javascript/flavours/glitch/components/avatar_composite.jsx @@ -5,6 +5,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { autoPlayGif } from '../initial_state'; +import { Avatar } from './avatar'; + export default class AvatarComposite extends PureComponent { static propTypes = { @@ -76,12 +78,12 @@ export default class AvatarComposite extends PureComponent { bottom: bottom, width: `${width}%`, height: `${height}%`, - backgroundSize: 'cover', - backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`, }; return ( -
+
+ +
); } diff --git a/app/javascript/flavours/glitch/components/avatar_overlay.jsx b/app/javascript/flavours/glitch/components/avatar_overlay.jsx deleted file mode 100644 index d8215a4785..0000000000 --- a/app/javascript/flavours/glitch/components/avatar_overlay.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; - -import ImmutablePropTypes from 'react-immutable-proptypes'; - -import { autoPlayGif } from 'flavours/glitch/initial_state'; - -export default class AvatarOverlay extends PureComponent { - - static propTypes = { - account: ImmutablePropTypes.map.isRequired, - friend: ImmutablePropTypes.map.isRequired, - animate: PropTypes.bool, - }; - - static defaultProps = { - animate: autoPlayGif, - }; - - render() { - const { account, friend, animate } = this.props; - - const baseStyle = { - backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`, - }; - - const overlayStyle = { - backgroundImage: `url(${friend.get(animate ? 'avatar' : 'avatar_static')})`, - }; - - return ( -
-
-
-
- ); - } - -} diff --git a/app/javascript/flavours/glitch/components/avatar_overlay.tsx b/app/javascript/flavours/glitch/components/avatar_overlay.tsx new file mode 100644 index 0000000000..da9c293856 --- /dev/null +++ b/app/javascript/flavours/glitch/components/avatar_overlay.tsx @@ -0,0 +1,56 @@ +import { useHovering } from '../hooks/useHovering'; +import { autoPlayGif } from '../initial_state'; +import type { Account } from '../types/resources'; + +interface Props { + account: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there + friend: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there + size?: number; + baseSize?: number; + overlaySize?: number; +} + +export const AvatarOverlay: React.FC = ({ + account, + friend, + size = 46, + baseSize = 36, + overlaySize = 24, +}) => { + const { hovering, handleMouseEnter, handleMouseLeave } = + useHovering(autoPlayGif); + const accountSrc = hovering + ? account?.get('avatar') + : account?.get('avatar_static'); + const friendSrc = hovering + ? friend?.get('avatar') + : friend?.get('avatar_static'); + + return ( +
+
+
+ {accountSrc && {account?.get('acct')}} +
+
+
+
+ {friendSrc && {friend?.get('acct')}} +
+
+
+ ); +}; diff --git a/app/javascript/flavours/glitch/components/column.jsx b/app/javascript/flavours/glitch/components/column.jsx index e42a539052..22d6eabed7 100644 --- a/app/javascript/flavours/glitch/components/column.jsx +++ b/app/javascript/flavours/glitch/components/column.jsx @@ -12,7 +12,6 @@ export default class Column extends PureComponent { static propTypes = { children: PropTypes.node, extraClasses: PropTypes.string, - name: PropTypes.string, label: PropTypes.string, bindToDocument: PropTypes.bool, }; @@ -62,10 +61,10 @@ export default class Column extends PureComponent { } render () { - const { children, extraClasses, name, label } = this.props; + const { label, children, extraClasses } = this.props; return ( -
+
{children}
); diff --git a/app/javascript/flavours/glitch/components/status.jsx b/app/javascript/flavours/glitch/components/status.jsx index a47b3b4d95..94754f0f29 100644 --- a/app/javascript/flavours/glitch/components/status.jsx +++ b/app/javascript/flavours/glitch/components/status.jsx @@ -84,6 +84,7 @@ class Status extends ImmutablePureComponent { previousId: PropTypes.string, nextInReplyToId: PropTypes.string, rootId: PropTypes.string, + onClick: PropTypes.func, onReply: PropTypes.func, onFavourite: PropTypes.func, onReblog: PropTypes.func, @@ -116,7 +117,6 @@ class Status extends ImmutablePureComponent { intl: PropTypes.object.isRequired, cacheMediaWidth: PropTypes.func, cachedMediaWidth: PropTypes.number, - onClick: PropTypes.func, scrollKey: PropTypes.string, deployPictureInPicture: PropTypes.func, settings: ImmutablePropTypes.map.isRequired, @@ -576,7 +576,7 @@ class Status extends ImmutablePureComponent { openProfile: this.handleHotkeyOpenProfile, moveUp: this.handleHotkeyMoveUp, moveDown: this.handleHotkeyMoveDown, - toggleSpoiler: this.handleExpandedToggle, + toggleHidden: this.handleExpandedToggle, bookmark: this.handleHotkeyBookmark, toggleCollapse: this.handleHotkeyCollapse, toggleSensitive: this.handleHotkeyToggleSensitive, diff --git a/app/javascript/flavours/glitch/components/status_header.jsx b/app/javascript/flavours/glitch/components/status_header.jsx index cf7ab5365e..1c51707cef 100644 --- a/app/javascript/flavours/glitch/components/status_header.jsx +++ b/app/javascript/flavours/glitch/components/status_header.jsx @@ -6,7 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; // Mastodon imports. import { Avatar } from './avatar'; -import AvatarOverlay from './avatar_overlay'; +import { AvatarOverlay } from './avatar_overlay'; import { DisplayName } from './display_name'; export default class StatusHeader extends PureComponent { @@ -39,7 +39,7 @@ export default class StatusHeader extends PureComponent { let statusAvatar; if (friend === undefined || friend === null) { - statusAvatar = ; + statusAvatar = ; } else { statusAvatar = ; } diff --git a/app/javascript/flavours/glitch/containers/mastodon.jsx b/app/javascript/flavours/glitch/containers/mastodon.jsx index 1ab20d0227..070e94fe8f 100644 --- a/app/javascript/flavours/glitch/containers/mastodon.jsx +++ b/app/javascript/flavours/glitch/containers/mastodon.jsx @@ -22,6 +22,7 @@ import { store } from 'flavours/glitch/store'; const title = process.env.NODE_ENV === 'production' ? siteTitle : `${siteTitle} (Dev)`; const hydrateAction = hydrateStore(initialState); + store.dispatch(hydrateAction); // check for deprecated local settings @@ -71,8 +72,8 @@ export default class Mastodon extends PureComponent { } } - shouldUpdateScroll (_, { location }) { - return !(location.state?.mastodonModalKey); + shouldUpdateScroll (prevRouterProps, { location }) { + return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey); } render () { diff --git a/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx b/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx index 4ddf0dc524..720acab43f 100644 --- a/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx +++ b/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx @@ -38,7 +38,7 @@ class FeaturedTags extends ImmutablePureComponent { name={featuredTag.get('name')} href={featuredTag.get('url')} to={`/@${account.get('acct')}/tagged/${featuredTag.get('name')}`} - uses={featuredTag.get('statuses_count')} + uses={featuredTag.get('statuses_count') * 1} withGraph={false} description={((featuredTag.get('statuses_count') * 1) > 0) ? intl.formatMessage(messages.lastStatusAt, { date: intl.formatDate(featuredTag.get('last_status_at'), { month: 'short', day: '2-digit' }) }) : intl.formatMessage(messages.empty)} /> diff --git a/app/javascript/flavours/glitch/features/account/components/header.jsx b/app/javascript/flavours/glitch/features/account/components/header.jsx index c64bfb3b51..f0cfa0f813 100644 --- a/app/javascript/flavours/glitch/features/account/components/header.jsx +++ b/app/javascript/flavours/glitch/features/account/components/header.jsx @@ -11,7 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { Avatar } from 'flavours/glitch/components/avatar'; import { Button } from 'flavours/glitch/components/button'; -import { Icon } from 'flavours/glitch/components/icon'; +import { Icon } from 'flavours/glitch/components/icon'; import { IconButton } from 'flavours/glitch/components/icon_button'; import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container'; import { autoPlayGif, me, domain } from 'flavours/glitch/initial_state'; diff --git a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.jsx b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.jsx index aaa872b0aa..87189ecf1b 100644 --- a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.jsx +++ b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.jsx @@ -76,7 +76,7 @@ export default class MediaItem extends ImmutablePureComponent { if (['audio', 'video'].includes(attachment.get('type'))) { content = ( {attachment.get('description')} : null; return ( - + ; return ( - + ; return ( - + { class CommunityTimeline extends PureComponent { - static defaultProps = { - onlyMedia: false, - }; - static contextTypes = { identity: PropTypes.object, }; + static defaultProps = { + onlyMedia: false, + }; + static propTypes = { dispatch: PropTypes.func.isRequired, columnId: PropTypes.string, @@ -128,7 +128,7 @@ class CommunityTimeline extends PureComponent { const pinned = !!columnId; return ( - + - - {this.props.account.get('acct')} - + + {username} +
- @{this.props.account.get('acct')} + @{username} { profileLink !== undefined && ( diff --git a/app/javascript/flavours/glitch/features/compose/components/search.jsx b/app/javascript/flavours/glitch/features/compose/components/search.jsx index ed717f215b..64732068ed 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/search.jsx @@ -92,25 +92,6 @@ class Search extends PureComponent { } }; - handleBlur = () => { - this.setState({ expanded: false, selectedOption: -1 }); - }; - - handleFocus = () => { - const { onShow, singleColumn } = this.props; - - this.setState({ expanded: true, selectedOption: -1 }); - onShow(); - - if (this.searchForm && !singleColumn) { - const { left, right } = this.searchForm.getBoundingClientRect(); - - if (left < 0 || right > (window.innerWidth || document.documentElement.clientWidth)) { - this.searchForm.scrollIntoView(); - } - } - }; - handleKeyDown = (e) => { const { selectedOption } = this.state; const options = searchEnabled ? this._getOptions().concat(this.defaultOptions) : this._getOptions(); @@ -161,8 +142,23 @@ class Search extends PureComponent { } }; - findTarget = () => { - return this.searchForm; + handleFocus = () => { + const { onShow, singleColumn } = this.props; + + this.setState({ expanded: true, selectedOption: -1 }); + onShow(); + + if (this.searchForm && !singleColumn) { + const { left, right } = this.searchForm.getBoundingClientRect(); + + if (left < 0 || right > (window.innerWidth || document.documentElement.clientWidth)) { + this.searchForm.scrollIntoView(); + } + } + }; + + handleBlur = () => { + this.setState({ expanded: false, selectedOption: -1 }); }; handleHashtagClick = () => { diff --git a/app/javascript/flavours/glitch/features/compose/components/search_results.jsx b/app/javascript/flavours/glitch/features/compose/components/search_results.jsx index e8379a9326..af7b8a6c7b 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search_results.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/search_results.jsx @@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { Icon } from 'flavours/glitch/components/icon'; +import { Icon } from 'flavours/glitch/components/icon'; import { LoadMore } from 'flavours/glitch/components/load_more'; import { SearchSection } from 'flavours/glitch/features/explore/components/search_section'; @@ -69,6 +69,7 @@ class SearchResults extends ImmutablePureComponent { ); } + return (
diff --git a/app/javascript/flavours/glitch/features/favourited_statuses/index.jsx b/app/javascript/flavours/glitch/features/favourited_statuses/index.jsx index 9c745be813..1a19a03afa 100644 --- a/app/javascript/flavours/glitch/features/favourited_statuses/index.jsx +++ b/app/javascript/flavours/glitch/features/favourited_statuses/index.jsx @@ -77,7 +77,7 @@ class Favourites extends ImmutablePureComponent { const emptyMessage = ; return ( - + + )} /> + + - ))} + )).reverse()} {announcements.size > 1 && ( diff --git a/app/javascript/flavours/glitch/features/getting_started/index.jsx b/app/javascript/flavours/glitch/features/getting_started/index.jsx index e803c4949c..ea2537482b 100644 --- a/app/javascript/flavours/glitch/features/getting_started/index.jsx +++ b/app/javascript/flavours/glitch/features/getting_started/index.jsx @@ -173,7 +173,7 @@ class GettingStarted extends ImmutablePureComponent { } return ( - +
{!multiColumn && signedIn && } diff --git a/app/javascript/flavours/glitch/features/home_timeline/index.jsx b/app/javascript/flavours/glitch/features/home_timeline/index.jsx index d3547ed3be..20351833a0 100644 --- a/app/javascript/flavours/glitch/features/home_timeline/index.jsx +++ b/app/javascript/flavours/glitch/features/home_timeline/index.jsx @@ -196,7 +196,7 @@ class HomeTimeline extends PureComponent { } return ( - + ({ +const MapStateToProps = (state, { listId, added }) => ({ list: state.get('lists').get(listId), added: typeof added === 'undefined' ? state.getIn(['listAdder', 'lists', 'items']).includes(listId) : added, }); @@ -69,4 +69,4 @@ class List extends ImmutablePureComponent { } -export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(List)); +export default connect(MapStateToProps, mapDispatchToProps)(injectIntl(List)); diff --git a/app/javascript/flavours/glitch/features/list_editor/components/account.jsx b/app/javascript/flavours/glitch/features/list_editor/components/account.jsx index 91a2bdb79d..4618bd1c16 100644 --- a/app/javascript/flavours/glitch/features/list_editor/components/account.jsx +++ b/app/javascript/flavours/glitch/features/list_editor/components/account.jsx @@ -1,20 +1,39 @@ import PropTypes from 'prop-types'; -import { defineMessages } from 'react-intl'; +import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import { connect } from 'react-redux'; +import { removeFromListEditor, addToListEditor } from '../../../actions/lists'; import { Avatar } from '../../../components/avatar'; import { DisplayName } from '../../../components/display_name'; import { IconButton } from '../../../components/icon_button'; +import { makeGetAccount } from '../../../selectors'; const messages = defineMessages({ remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' }, add: { id: 'lists.account.add', defaultMessage: 'Add to list' }, }); -export default class Account extends ImmutablePureComponent { +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = (state, { accountId, added }) => ({ + account: getAccount(state, accountId), + added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added, + }); + + return mapStateToProps; +}; + +const mapDispatchToProps = (dispatch, { accountId }) => ({ + onRemove: () => dispatch(removeFromListEditor(accountId)), + onAdd: () => dispatch(addToListEditor(accountId)), +}); + +class Account extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, @@ -56,3 +75,5 @@ export default class Account extends ImmutablePureComponent { } } + +export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(Account)); diff --git a/app/javascript/flavours/glitch/features/list_editor/components/search.jsx b/app/javascript/flavours/glitch/features/list_editor/components/search.jsx index 43c0358ba6..5434ba0dcb 100644 --- a/app/javascript/flavours/glitch/features/list_editor/components/search.jsx +++ b/app/javascript/flavours/glitch/features/list_editor/components/search.jsx @@ -1,17 +1,31 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { defineMessages } from 'react-intl'; +import { defineMessages, injectIntl } from 'react-intl'; import classNames from 'classnames'; +import { connect } from 'react-redux'; + import { Icon } from 'flavours/glitch/components/icon'; +import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; + const messages = defineMessages({ search: { id: 'lists.search', defaultMessage: 'Search among people you follow' }, }); -export default class Search extends PureComponent { +const mapStateToProps = state => ({ + value: state.getIn(['listEditor', 'suggestions', 'value']), +}); + +const mapDispatchToProps = dispatch => ({ + onSubmit: value => dispatch(fetchListSuggestions(value)), + onClear: () => dispatch(clearListSuggestions()), + onChange: value => dispatch(changeListSuggestions(value)), +}); + +class Search extends PureComponent { static propTypes = { intl: PropTypes.object.isRequired, @@ -63,3 +77,5 @@ export default class Search extends PureComponent { } } + +export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Search)); diff --git a/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js b/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js deleted file mode 100644 index 329a1dcb91..0000000000 --- a/app/javascript/flavours/glitch/features/list_editor/containers/account_container.js +++ /dev/null @@ -1,26 +0,0 @@ -import { injectIntl } from 'react-intl'; - -import { connect } from 'react-redux'; - -import { removeFromListEditor, addToListEditor } from 'flavours/glitch/actions/lists'; -import { makeGetAccount } from 'flavours/glitch/selectors'; - -import Account from '../components/account'; - -const makeMapStateToProps = () => { - const getAccount = makeGetAccount(); - - const mapStateToProps = (state, { accountId, added }) => ({ - account: getAccount(state, accountId), - added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added, - }); - - return mapStateToProps; -}; - -const mapDispatchToProps = (dispatch, { accountId }) => ({ - onRemove: () => dispatch(removeFromListEditor(accountId)), - onAdd: () => dispatch(addToListEditor(accountId)), -}); - -export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account)); diff --git a/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js b/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js deleted file mode 100644 index a7f0009a84..0000000000 --- a/app/javascript/flavours/glitch/features/list_editor/containers/search_container.js +++ /dev/null @@ -1,18 +0,0 @@ -import { injectIntl } from 'react-intl'; - -import { connect } from 'react-redux'; - -import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; -import Search from '../components/search'; - -const mapStateToProps = state => ({ - value: state.getIn(['listEditor', 'suggestions', 'value']), -}); - -const mapDispatchToProps = dispatch => ({ - onSubmit: value => dispatch(fetchListSuggestions(value)), - onClear: () => dispatch(clearListSuggestions()), - onChange: value => dispatch(changeListSuggestions(value)), -}); - -export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Search)); diff --git a/app/javascript/flavours/glitch/features/list_editor/index.jsx b/app/javascript/flavours/glitch/features/list_editor/index.jsx index dca690a3ec..85e90169e8 100644 --- a/app/javascript/flavours/glitch/features/list_editor/index.jsx +++ b/app/javascript/flavours/glitch/features/list_editor/index.jsx @@ -11,10 +11,9 @@ import spring from 'react-motion/lib/spring'; import { setupListEditor, clearListSuggestions, resetListEditor } from '../../actions/lists'; import Motion from '../ui/util/optional_motion'; +import Account from './components/account'; import EditListForm from './components/edit_list_form'; -import AccountContainer from './containers/account_container'; -import SearchContainer from './containers/search_container'; - +import Search from './components/search'; const mapStateToProps = state => ({ accountIds: state.getIn(['listEditor', 'accounts', 'items']), @@ -58,21 +57,21 @@ class ListEditor extends ImmutablePureComponent {
- +
- {accountIds.map(accountId => )} + {accountIds.map(accountId => )}
{showSearch &&
} - {({ x }) => - (
- {searchAccountIds.map(accountId => )} -
) - } + {({ x }) => ( +
+ {searchAccountIds.map(accountId => )} +
+ )}
diff --git a/app/javascript/flavours/glitch/features/mutes/index.jsx b/app/javascript/flavours/glitch/features/mutes/index.jsx index f9c42378a5..947fe4c9b2 100644 --- a/app/javascript/flavours/glitch/features/mutes/index.jsx +++ b/app/javascript/flavours/glitch/features/mutes/index.jsx @@ -61,7 +61,7 @@ class Mutes extends ImmutablePureComponent { const emptyMessage = ; return ( - + diff --git a/app/javascript/flavours/glitch/features/onboarding/share.jsx b/app/javascript/flavours/glitch/features/onboarding/share.jsx index a313ee2e8d..488096654c 100644 --- a/app/javascript/flavours/glitch/features/onboarding/share.jsx +++ b/app/javascript/flavours/glitch/features/onboarding/share.jsx @@ -13,7 +13,7 @@ import SwipeableViews from 'react-swipeable-views'; import Column from 'flavours/glitch/components/column'; import ColumnBackButton from 'flavours/glitch/components/column_back_button'; -import { Icon } from 'flavours/glitch/components/icon'; +import { Icon } from 'flavours/glitch/components/icon'; import { me, domain } from 'flavours/glitch/initial_state'; import ArrowSmallRight from './components/arrow_small_right'; diff --git a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx index fc2390e36f..8fd2b98883 100644 --- a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx +++ b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx @@ -122,8 +122,8 @@ class Footer extends ImmutablePureComponent { } }; - _performReblog = (privacy) => { - const { dispatch, status } = this.props; + _performReblog = (status, privacy) => { + const { dispatch } = this.props; dispatch(reblog(status, privacy)); }; @@ -135,7 +135,7 @@ class Footer extends ImmutablePureComponent { if (status.get('reblogged')) { dispatch(unreblog(status)); } else if ((e && e.shiftKey) || !boostModal) { - this._performReblog(); + this._performReblog(status); } else { dispatch(initBoostModal({ status, onReblog: this._performReblog })); } diff --git a/app/javascript/flavours/glitch/features/public_timeline/index.jsx b/app/javascript/flavours/glitch/features/public_timeline/index.jsx index 8c373d99ab..79719fd2db 100644 --- a/app/javascript/flavours/glitch/features/public_timeline/index.jsx +++ b/app/javascript/flavours/glitch/features/public_timeline/index.jsx @@ -133,7 +133,7 @@ class PublicTimeline extends PureComponent { const pinned = !!columnId; return ( - +
-
·
+
+ · +
} /> @@ -61,3 +63,5 @@ export default class StatusCheckBox extends PureComponent { } } + +export default StatusCheckBox; diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.jsx b/app/javascript/flavours/glitch/features/status/components/action_bar.jsx index 820ced0dc6..e42dcecb17 100644 --- a/app/javascript/flavours/glitch/features/status/components/action_bar.jsx +++ b/app/javascript/flavours/glitch/features/status/components/action_bar.jsx @@ -61,13 +61,13 @@ class ActionBar extends PureComponent { onFavourite: PropTypes.func.isRequired, onReactionAdd: PropTypes.func.isRequired, onBookmark: PropTypes.func.isRequired, - onMute: PropTypes.func, - onMuteConversation: PropTypes.func, - onBlock: PropTypes.func, onDelete: PropTypes.func.isRequired, onEdit: PropTypes.func.isRequired, onDirect: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, + onMute: PropTypes.func, + onBlock: PropTypes.func, + onMuteConversation: PropTypes.func, onReport: PropTypes.func, onPin: PropTypes.func, onEmbed: PropTypes.func, @@ -119,14 +119,14 @@ class ActionBar extends PureComponent { this.props.onMute(this.props.status.get('account')); }; - handleConversationMuteClick = () => { - this.props.onMuteConversation(this.props.status); - }; - handleBlockClick = () => { this.props.onBlock(this.props.status); }; + handleConversationMuteClick = () => { + this.props.onMuteConversation(this.props.status); + }; + handleReport = () => { this.props.onReport(this.props.status); }; diff --git a/app/javascript/flavours/glitch/features/status/components/card.jsx b/app/javascript/flavours/glitch/features/status/components/card.jsx index a64314efa3..4e7ccf96f9 100644 --- a/app/javascript/flavours/glitch/features/status/components/card.jsx +++ b/app/javascript/flavours/glitch/features/status/components/card.jsx @@ -3,13 +3,13 @@ import { PureComponent } from 'react'; import { FormattedMessage } from 'react-intl'; -import classnames from 'classnames'; +import classNames from 'classnames'; import Immutable from 'immutable'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { Blurhash } from 'flavours/glitch/components/blurhash'; -import { Icon } from 'flavours/glitch/components/icon'; +import { Icon } from 'flavours/glitch/components/icon'; import { useBlurhash } from 'flavours/glitch/initial_state'; import { decode as decodeIDNA } from 'flavours/glitch/utils/idna'; @@ -148,7 +148,7 @@ export default class Card extends PureComponent { const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name'); const horizontal = (!compact && card.get('width') > card.get('height')) || card.get('type') !== 'link' || embedded; const interactive = card.get('type') !== 'link'; - const className = classnames('status-card', { horizontal, compact, interactive }); + const className = classNames('status-card', { horizontal, compact, interactive }); const title = interactive ? {card.get('title')} : {card.get('title')}; const language = card.get('language') || ''; @@ -171,15 +171,17 @@ export default class Card extends PureComponent { let embed = ''; let canvas = ( ); + const thumbnailDescription = card.get('image_description'); const thumbnail = {thumbnailDescription}; + let spoilerButton = ( ); + spoilerButton = ( -
+
{spoilerButton}
); @@ -209,15 +212,14 @@ export default class Card extends PureComponent { {canvas} {thumbnail} - {revealed && ( + {revealed ? (
- + {horizontal && }
- )} - {!revealed && spoilerButton} + ) : spoilerButton}
); } diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.jsx b/app/javascript/flavours/glitch/features/status/components/detailed_status.jsx index 1968f2c937..6bbe7f4678 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.jsx +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.jsx @@ -12,7 +12,7 @@ import { AnimatedNumber } from 'flavours/glitch/components/animated_number'; import AttachmentList from 'flavours/glitch/components/attachment_list'; import EditedTimestamp from 'flavours/glitch/components/edited_timestamp'; import { getHashtagBarForStatus } from 'flavours/glitch/components/hashtag_bar'; -import { Icon } from 'flavours/glitch/components/icon'; +import { Icon } from 'flavours/glitch/components/icon'; import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder'; import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon'; import PollContainer from 'flavours/glitch/containers/poll_container'; diff --git a/app/javascript/flavours/glitch/features/status/index.jsx b/app/javascript/flavours/glitch/features/status/index.jsx index 15a04e230b..afe920b876 100644 --- a/app/javascript/flavours/glitch/features/status/index.jsx +++ b/app/javascript/flavours/glitch/features/status/index.jsx @@ -726,7 +726,7 @@ class Status extends ImmutablePureComponent { bookmark: this.handleHotkeyBookmark, mention: this.handleHotkeyMention, openProfile: this.handleHotkeyOpenProfile, - toggleSpoiler: this.handleToggleHidden, + toggleHidden: this.handleToggleHidden, toggleSensitive: this.handleHotkeyToggleSensitive, openMedia: this.handleHotkeyOpenMedia, }; diff --git a/app/javascript/flavours/glitch/features/ui/components/bundle.jsx b/app/javascript/flavours/glitch/features/ui/components/bundle.jsx index ccbb565fbf..15c4220b34 100644 --- a/app/javascript/flavours/glitch/features/ui/components/bundle.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/bundle.jsx @@ -1,10 +1,10 @@ import PropTypes from 'prop-types'; -import { Component } from 'react'; +import { PureComponent } from 'react'; const emptyComponent = () => null; const noop = () => { }; -class Bundle extends Component { +class Bundle extends PureComponent { static propTypes = { fetchComponent: PropTypes.func.isRequired, @@ -26,7 +26,7 @@ class Bundle extends Component { onFetchFail: noop, }; - static cache = {}; + static cache = new Map; state = { mod: undefined, @@ -51,6 +51,7 @@ class Bundle extends Component { load = (props) => { const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props; + const cachedMod = Bundle.cache.get(fetchComponent); if (fetchComponent === undefined) { this.setState({ mod: null }); @@ -59,10 +60,8 @@ class Bundle extends Component { onFetch(); - if (Bundle.cache[fetchComponent.name]) { - const mod = Bundle.cache[fetchComponent.name]; - - this.setState({ mod: mod.default }); + if (cachedMod) { + this.setState({ mod: cachedMod.default }); onFetchSuccess(); return Promise.resolve(); } @@ -76,7 +75,7 @@ class Bundle extends Component { return fetchComponent() .then((mod) => { - Bundle.cache[fetchComponent.name] = mod; + Bundle.cache.set(fetchComponent, mod); this.setState({ mod: mod.default }); onFetchSuccess(); }) diff --git a/app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.jsx b/app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.jsx index 1d792a2cfe..67dba3ce0c 100644 --- a/app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.jsx @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import { Component } from 'react'; +import { PureComponent } from 'react'; import { defineMessages, injectIntl } from 'react-intl'; @@ -11,7 +11,7 @@ const messages = defineMessages({ close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' }, }); -class BundleModalError extends Component { +class BundleModalError extends PureComponent { static propTypes = { onRetry: PropTypes.func.isRequired, diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx b/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx index f1e7baf8dd..05a02ae6ce 100644 --- a/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx @@ -75,7 +75,7 @@ export default class ColumnsArea extends ImmutablePureComponent { this.isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl'); } - componentWillUpdate(nextProps) { + UNSAFE_componentWillUpdate(nextProps) { if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) { this.node.removeEventListener('wheel', this.handleWheel); } diff --git a/app/javascript/flavours/glitch/features/ui/components/media_modal.jsx b/app/javascript/flavours/glitch/features/ui/components/media_modal.jsx index d3d74136f1..9fc51e620c 100644 --- a/app/javascript/flavours/glitch/features/ui/components/media_modal.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/media_modal.jsx @@ -96,24 +96,10 @@ class MediaModal extends ImmutablePureComponent { componentDidMount () { window.addEventListener('keydown', this.handleKeyDown, false); + this._sendBackgroundColor(); } - componentWillUnmount () { - window.removeEventListener('keydown', this.handleKeyDown); - this.props.onChangeBackgroundColor(null); - } - - getIndex () { - return this.state.index !== null ? this.state.index : this.props.index; - } - - toggleNavigation = () => { - this.setState(prevState => ({ - navigationHidden: !prevState.navigationHidden, - })); - }; - componentDidUpdate (prevProps, prevState) { if (prevState.index !== this.state.index) { this._sendBackgroundColor(); @@ -131,6 +117,22 @@ class MediaModal extends ImmutablePureComponent { } } + componentWillUnmount () { + window.removeEventListener('keydown', this.handleKeyDown); + + this.props.onChangeBackgroundColor(null); + } + + getIndex () { + return this.state.index !== null ? this.state.index : this.props.index; + } + + toggleNavigation = () => { + this.setState(prevState => ({ + navigationHidden: !prevState.navigationHidden, + })); + }; + render () { const { media, statusId, lang, intl, onClose } = this.props; const { navigationHidden } = this.state; diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx index 7b47e08234..40fea635a6 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx @@ -48,7 +48,7 @@ class NavigationPanel extends Component { return match || location.pathname.startsWith('/public'); }; - render() { + render () { const { intl, onOpenSettings } = this.props; const { signedIn, disabledAccountId } = this.context.identity; diff --git a/app/javascript/flavours/glitch/features/ui/components/upload_area.jsx b/app/javascript/flavours/glitch/features/ui/components/upload_area.jsx index 9aa8e359f8..b2702d35ef 100644 --- a/app/javascript/flavours/glitch/features/ui/components/upload_area.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/upload_area.jsx @@ -40,14 +40,14 @@ export default class UploadArea extends PureComponent { return ( - {({ backgroundOpacity, backgroundScale }) => - (
+ {({ backgroundOpacity, backgroundScale }) => ( +
-
) - } +
+ )} ); } diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index 48bba29b8a..fa943f579f 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import { PureComponent, Component } from 'react'; +import { PureComponent } from 'react'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; @@ -68,9 +68,10 @@ import { PrivacyPolicy, } from './util/async-components'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; + // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. -import "../../components/status"; +import '../../components/status'; const messages = defineMessages({ beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' }, @@ -119,7 +120,7 @@ const keyMap = { goToBlocked: 'g b', goToMuted: 'g m', goToRequests: 'g r', - toggleSpoiler: 'x', + toggleHidden: 'x', bookmark: 'd', toggleCollapse: 'shift+x', toggleSensitive: 'h', @@ -255,7 +256,7 @@ class SwitchingColumnsArea extends PureComponent { } -class UI extends Component { +class UI extends PureComponent { static contextTypes = { identity: PropTypes.object.isRequired, @@ -270,7 +271,6 @@ class UI extends Component { hasComposingText: PropTypes.bool, hasMediaAttachments: PropTypes.bool, canUploadMore: PropTypes.bool, - match: PropTypes.object.isRequired, intl: PropTypes.object.isRequired, dropdownMenuIsOpen: PropTypes.bool, unreadNotifications: PropTypes.number, @@ -287,7 +287,7 @@ class UI extends Component { draggingOver: false, }; - handleBeforeUnload = (e) => { + handleBeforeUnload = e => { const { intl, dispatch, hasComposingText, hasMediaAttachments } = this.props; dispatch(synchronouslySubmitMarkers()); @@ -300,6 +300,14 @@ class UI extends Component { } }; + handleVisibilityChange = () => { + const visibility = !document[this.visibilityHiddenProp]; + this.props.dispatch(notificationsSetVisibility(visibility)); + if (visibility) { + this.props.dispatch(submitMarkers({ immediate: true })); + } + }; + handleDragEnter = (e) => { e.preventDefault(); @@ -311,13 +319,14 @@ class UI extends Component { this.dragTargets.push(e.target); } - if (e.dataTransfer && e.dataTransfer.types.includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) { + if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) { this.setState({ draggingOver: true }); } }; handleDragOver = (e) => { if (this.dataTransferIsText(e.dataTransfer)) return false; + e.preventDefault(); e.stopPropagation(); @@ -372,14 +381,6 @@ class UI extends Component { } }; - handleVisibilityChange = () => { - const visibility = !document[this.visibilityHiddenProp]; - this.props.dispatch(notificationsSetVisibility(visibility)); - if (visibility) { - this.props.dispatch(submitMarkers({ immediate: true })); - } - }; - handleLayoutChange = debounce(() => { this.props.dispatch(clearHeight()); // The cached heights are no longer accurate, invalidate }, 500, { diff --git a/app/javascript/flavours/glitch/styles/components/accounts.scss b/app/javascript/flavours/glitch/styles/components/accounts.scss index d29531874e..7dbefb22c3 100644 --- a/app/javascript/flavours/glitch/styles/components/accounts.scss +++ b/app/javascript/flavours/glitch/styles/components/accounts.scss @@ -85,10 +85,14 @@ display: block; position: relative; - cursor: pointer; - width: 36px; - height: 36px; - background-size: 36px 36px; + overflow: hidden; + + img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + } &-inline { display: inline-block; @@ -102,7 +106,7 @@ overflow: hidden; position: relative; - & div { + & > div { @include avatar-radius; float: left; @@ -110,6 +114,11 @@ box-sizing: border-box; } + .account__avatar { + width: 100% !important; + height: 100% !important; + } + &__label { display: block; position: absolute; @@ -125,37 +134,13 @@ } .account__avatar-overlay { - @include avatar-size(48px); - position: relative; - &-base { - @include avatar-radius; - @include avatar-size(36px); - - img { - @include avatar-radius; - - width: 100%; - height: 100%; - } - } - &-overlay { - @include avatar-radius; - @include avatar-size(24px); - position: absolute; bottom: 0; inset-inline-end: 0; z-index: 1; - - img { - @include avatar-radius; - - width: 100%; - height: 100%; - } } } diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss index 9a49477117..cb7d72e5cc 100644 --- a/app/javascript/flavours/glitch/styles/components/status.scss +++ b/app/javascript/flavours/glitch/styles/components/status.scss @@ -701,8 +701,6 @@ a.status__display-name, .status__avatar { flex: none; margin-inline-end: 10px; - height: 48px; - width: 48px; } .muted { @@ -1068,7 +1066,7 @@ a.status-card.compact:hover { width: 0; position: absolute; top: 0; - inset-inline-start: 14px + ((48px - 2px) * 0.5); + inset-inline-start: 14px + ((46px - 2px) * 0.5); &--full { top: 0; @@ -1079,7 +1077,7 @@ a.status-card.compact:hover { display: block; position: absolute; top: 10px - 4px; - height: 48px + 4px + 4px; + height: 46px + 4px + 4px; width: 2px; background: $ui-base-color; inset-inline-start: -2px; @@ -1087,8 +1085,8 @@ a.status-card.compact:hover { } &--first { - top: 10px + 48px + 4px; - height: calc(100% - (10px + 48px + 4px)); + top: 10px + 46px + 4px; + height: calc(100% - (10px + 46px + 4px)); &::before { display: none;