From 2da584b5eeb0de490e74226f95cfb590b94445b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1rbara=20de=20Castro=20Fernandes?= Date: Tue, 9 Jun 2020 17:07:00 -0300 Subject: [PATCH 01/16] Create StillImage component --- app/soapbox/components/still_image.js | 63 ++++++++++++++++++++++++++ app/styles/application.scss | 1 + app/styles/components/still-image.scss | 28 ++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 app/soapbox/components/still_image.js create mode 100644 app/styles/components/still-image.scss diff --git a/app/soapbox/components/still_image.js b/app/soapbox/components/still_image.js new file mode 100644 index 000000000..45ce770c7 --- /dev/null +++ b/app/soapbox/components/still_image.js @@ -0,0 +1,63 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import { getSettings } from 'soapbox/actions/settings'; +import classNames from 'classnames'; + +const mapStateToProps = state => ({ + autoPlayGif: getSettings(state).get('autoPlayGif'), +}); + +export default @connect(mapStateToProps) +class StillImage extends React.PureComponent { + + static propTypes = { + alt: PropTypes.string, + autoPlayGif: PropTypes.bool.isRequired, + className: PropTypes.node, + src: PropTypes.string.isRequired, + style: PropTypes.object, + }; + + static defaultProps = { + alt: '', + className: '', + style: {}, + } + + hoverToPlay() { + const { autoPlayGif, src } = this.props; + return !autoPlayGif && src.endsWith('.gif'); + } + + setCanvasRef = c => { + this.canvas = c; + } + + setImageRef = i => { + this.img = i; + } + + handleImageLoad = () => { + if (this.hoverToPlay()) { + const img = this.img; + const canvas = this.canvas; + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + canvas.getContext('2d').drawImage(img, 0, 0); + } + } + + render() { + const { alt, className, src, style } = this.props; + const hoverToPlay = this.hoverToPlay(); + + return ( +
+ {alt} + {hoverToPlay && } +
+ ); + } + +} diff --git a/app/styles/application.scss b/app/styles/application.scss index 331e38f92..62682fc9c 100644 --- a/app/styles/application.scss +++ b/app/styles/application.scss @@ -58,6 +58,7 @@ @import 'components/navigation-bar'; @import 'components/promo-panel'; @import 'components/drawer'; +@import 'components/still-image'; @import 'components/timeline-queue-header'; @import 'components/badge'; @import 'components/trends'; diff --git a/app/styles/components/still-image.scss b/app/styles/components/still-image.scss new file mode 100644 index 000000000..0b86d6ada --- /dev/null +++ b/app/styles/components/still-image.scss @@ -0,0 +1,28 @@ +.still-image { + position: relative; + overflow: hidden; + + img, + canvas { + width: 100%; + height: 100%; + display: block; + object-fit: cover; + font-family: inherit; + } + + &--play-on-hover { + img { + position: absolute; + visibility: hidden; + } + + &:hover img { + visibility: visible; + } + + &:hover canvas { + visibility: hidden; + } + } +} From e19f350111deaf40e37a8665fcde8204d231a011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1rbara=20de=20Castro=20Fernandes?= Date: Tue, 9 Jun 2020 18:24:31 -0300 Subject: [PATCH 02/16] Use StillImage on media items and fix autoPlayGif on account gallery --- app/soapbox/components/media_gallery.js | 45 ++---------- .../account_gallery/components/media_item.js | 5 +- app/styles/components/media-gallery.scss | 70 +++++++++---------- 3 files changed, 41 insertions(+), 79 deletions(-) diff --git a/app/soapbox/components/media_gallery.js b/app/soapbox/components/media_gallery.js index 4ab6e53e0..92886b715 100644 --- a/app/soapbox/components/media_gallery.js +++ b/app/soapbox/components/media_gallery.js @@ -11,6 +11,7 @@ import { decode } from 'blurhash'; import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../utils/media_aspect_ratio'; import { Map as ImmutableMap } from 'immutable'; import { getSettings } from 'soapbox/actions/settings'; +import StillImage from 'soapbox/components/still_image'; const messages = defineMessages({ toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' }, @@ -60,8 +61,7 @@ class Item extends React.PureComponent { hoverToPlay() { const { attachment, autoPlayGif } = this.props; - return !autoPlayGif && - (attachment.get('type') === 'gifv' || attachment.getIn(['pleroma', 'mime_type']) === 'image/gif'); + return !autoPlayGif && attachment.get('type') === 'gifv'; } handleClick = (e) => { @@ -72,7 +72,7 @@ class Item extends React.PureComponent { e.preventDefault(); } else { if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { - if (!this.canvas && this.hoverToPlay()) { + if (this.hoverToPlay()) { e.target.pause(); e.target.currentTime = 0; } @@ -112,23 +112,12 @@ class Item extends React.PureComponent { this.canvas = c; } - setImageRef = i => { - this.image = i; - } - handleImageLoad = () => { this.setState({ loaded: true }); - if (this.hoverToPlay()) { - const image = this.image; - const canvas = this.canvas; - canvas.width = image.naturalWidth; - canvas.height = image.naturalHeight; - canvas.getContext('2d').drawImage(image, 0, 0); - } } render() { - const { attachment, standalone, displayWidth, visible, dimensions, autoPlayGif } = this.props; + const { attachment, standalone, visible, dimensions, autoPlayGif } = this.props; let width = 100; let height = '100%'; @@ -162,39 +151,17 @@ class Item extends React.PureComponent { ); } else if (attachment.get('type') === 'image') { const previewUrl = attachment.get('preview_url'); - const previewWidth = attachment.getIn(['meta', 'small', 'width']); const originalUrl = attachment.get('url'); - const originalWidth = attachment.getIn(['meta', 'original', 'width']); - - const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number'; - - const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null; - const sizes = hasSize && (displayWidth > 0) ? `${displayWidth * (width / 100)}px` : null; - - const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0; - const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0; - const x = ((focusX / 2) + .5) * 100; - const y = ((focusY / -2) + .5) * 100; thumbnail = ( - {attachment.get('description')} - {this.hoverToPlay() && } + ); } else if (attachment.get('type') === 'gifv') { diff --git a/app/soapbox/features/account_gallery/components/media_item.js b/app/soapbox/features/account_gallery/components/media_item.js index 670e465a8..05f8867d4 100644 --- a/app/soapbox/features/account_gallery/components/media_item.js +++ b/app/soapbox/features/account_gallery/components/media_item.js @@ -8,6 +8,7 @@ import classNames from 'classnames'; import { decode } from 'blurhash'; import { isIOS } from 'soapbox/is_mobile'; import { getSettings } from 'soapbox/actions/settings'; +import StillImage from 'soapbox/components/still_image'; const mapStateToProps = state => ({ autoPlayGif: getSettings(state).get('autoPlayGif'), @@ -113,12 +114,10 @@ class MediaItem extends ImmutablePureComponent { const y = ((focusY / -2) + .5) * 100; thumbnail = ( - {attachment.get('description')} ); } else if (['gifv', 'video'].indexOf(attachment.get('type')) !== -1) { diff --git a/app/styles/components/media-gallery.scss b/app/styles/components/media-gallery.scss index 05ba65a41..431610bb6 100644 --- a/app/styles/components/media-gallery.scss +++ b/app/styles/components/media-gallery.scss @@ -28,47 +28,26 @@ z-index: 1; &, - img, - canvas { + .still-image { height: 100%; width: 100%; } - img, - canvas { - object-fit: cover; - } - - &--play-on-hover { - &::before { - content: 'GIF'; - position: absolute; - color: var(--primary-text-color); - background: var(--foreground-color); - bottom: 6px; - left: 6px; - padding: 2px 6px; - border-radius: 2px; - font-size: 11px; - font-weight: 600; - pointer-events: none; - opacity: 0.9; - transition: opacity 0.1s ease; - line-height: 18px; - } - - img { - position: absolute; - } - - img, - &:hover::before { - visibility: hidden; - } - - &:hover img { - visibility: visible; - } + .still-image--play-on-hover::before { + content: 'GIF'; + position: absolute; + color: var(--primary-text-color); + background: var(--foreground-color); + bottom: 6px; + left: 6px; + padding: 2px 6px; + border-radius: 2px; + font-size: 11px; + font-weight: 600; + pointer-events: none; + opacity: 0.9; + transition: opacity 0.1s ease; + line-height: 18px; } } @@ -82,6 +61,23 @@ z-index: 0; background: var(--background-color); + .still-image--play-on-hover::before { + content: 'GIF'; + position: absolute; + color: var(--primary-text-color); + background: var(--foreground-color); + bottom: 6px; + left: 6px; + padding: 2px 6px; + border-radius: 2px; + font-size: 11px; + font-weight: 600; + pointer-events: none; + opacity: 0.9; + transition: opacity 0.1s ease; + line-height: 18px; + } + &--hidden { display: none; } From 52f3f8ce8d91abecc6c298e252cc4bf3536413d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1rbara=20de=20Castro=20Fernandes?= Date: Wed, 10 Jun 2020 10:30:33 -0300 Subject: [PATCH 03/16] Handle autoPlayGif for avatars and banners --- app/soapbox/components/avatar.js | 53 +++---------------- app/soapbox/components/avatar_composite.js | 16 ++---- app/soapbox/components/avatar_overlay.js | 27 ++-------- app/soapbox/components/still_image.js | 2 +- .../features/account/components/header.js | 11 ++-- .../components/profile_preview.js | 5 +- .../features/ui/components/user_panel.js | 7 ++- app/styles/accounts.scss | 4 +- app/styles/components/account-header.scss | 35 +++++++++++- 9 files changed, 63 insertions(+), 97 deletions(-) diff --git a/app/soapbox/components/avatar.js b/app/soapbox/components/avatar.js index a4358332b..e909c44df 100644 --- a/app/soapbox/components/avatar.js +++ b/app/soapbox/components/avatar.js @@ -1,54 +1,25 @@ import React from 'react'; -import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { getSettings } from 'soapbox/actions/settings'; +import classNames from 'classnames'; +import StillImage from 'soapbox/components/still_image'; -const mapStateToProps = state => ({ - animate: getSettings(state).get('autoPlayGif'), -}); - -export class Avatar extends React.PureComponent { +export default class Avatar extends React.PureComponent { static propTypes = { account: ImmutablePropTypes.map, size: PropTypes.number, style: PropTypes.object, inline: PropTypes.bool, - animate: PropTypes.bool, }; static defaultProps = { inline: false, }; - state = { - hovering: false, - }; - - handleMouseEnter = () => { - if (this.props.animate) return; - this.setState({ hovering: true }); - } - - handleMouseLeave = () => { - if (this.props.animate) return; - this.setState({ hovering: false }); - } - render() { - const { account, size, animate, inline } = this.props; + const { account, size, inline } = this.props; if (!account) return null; - const { hovering } = this.state; - - const src = account.get('avatar'); - const staticSrc = account.get('avatar_static'); - - let className = 'account__avatar'; - - if (inline) { - className = className + ' account__avatar-inline'; - } // : TODO : remove inline and change all avatars to be sized using css const style = !size ? {} : { @@ -56,22 +27,14 @@ export class Avatar extends React.PureComponent { height: `${size}px`, }; - if (hovering || animate) { - style.backgroundImage = `url(${src})`; - } else { - style.backgroundImage = `url(${staticSrc})`; - } - return ( -
); } } - -export default connect(mapStateToProps)(Avatar); diff --git a/app/soapbox/components/avatar_composite.js b/app/soapbox/components/avatar_composite.js index 06607cacd..c6d1eb8cc 100644 --- a/app/soapbox/components/avatar_composite.js +++ b/app/soapbox/components/avatar_composite.js @@ -1,24 +1,16 @@ import React from 'react'; -import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { getSettings } from 'soapbox/actions/settings'; +import StillImage from 'soapbox/components/still_image'; -const mapStateToProps = state => ({ - animate: getSettings(state).get('autoPlayGif'), -}); - -export default @connect(mapStateToProps) -class AvatarComposite extends React.PureComponent { +export default class AvatarComposite extends React.PureComponent { static propTypes = { accounts: ImmutablePropTypes.list.isRequired, - animate: PropTypes.bool, size: PropTypes.number.isRequired, }; renderItem(account, size, index) { - const { animate } = this.props; let width = 50; let height = 100; @@ -76,12 +68,10 @@ class AvatarComposite extends React.PureComponent { bottom: bottom, width: `${width}%`, height: `${height}%`, - backgroundSize: 'cover', - backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`, }; return ( -
+ ); } diff --git a/app/soapbox/components/avatar_overlay.js b/app/soapbox/components/avatar_overlay.js index a72c4e614..802fe423a 100644 --- a/app/soapbox/components/avatar_overlay.js +++ b/app/soapbox/components/avatar_overlay.js @@ -1,40 +1,23 @@ import React from 'react'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { getSettings } from 'soapbox/actions/settings'; +import StillImage from 'soapbox/components/still_image'; -const mapStateToProps = state => ({ - animate: getSettings(state).get('autoPlayGif'), -}); - -export class AvatarOverlay extends React.PureComponent { +export default class AvatarOverlay extends React.PureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, friend: ImmutablePropTypes.map.isRequired, - animate: PropTypes.bool, }; 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')})`, - }; + const { account, friend } = this.props; return (
-
-
+ +
); } } - -export default connect(mapStateToProps)(AvatarOverlay); diff --git a/app/soapbox/components/still_image.js b/app/soapbox/components/still_image.js index 45ce770c7..ef02b13ef 100644 --- a/app/soapbox/components/still_image.js +++ b/app/soapbox/components/still_image.js @@ -27,7 +27,7 @@ class StillImage extends React.PureComponent { hoverToPlay() { const { autoPlayGif, src } = this.props; - return !autoPlayGif && src.endsWith('.gif'); + return !autoPlayGif && (src.endsWith('.gif') || src.startsWith('blob:')); } setCanvasRef = c => { diff --git a/app/soapbox/features/account/components/header.js b/app/soapbox/features/account/components/header.js index 83c87e0bf..908c71a50 100644 --- a/app/soapbox/features/account/components/header.js +++ b/app/soapbox/features/account/components/header.js @@ -16,7 +16,7 @@ import { NavLink } from 'react-router-dom'; import DropdownMenuContainer from 'soapbox/containers/dropdown_menu_container'; import ProfileInfoPanel from '../../ui/components/profile_info_panel'; import { debounce } from 'lodash'; -import { getSettings } from 'soapbox/actions/settings'; +import StillImage from 'soapbox/components/still_image'; const messages = defineMessages({ unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, @@ -54,7 +54,6 @@ const mapStateToProps = state => { return { me, isStaff: isStaff(state.getIn(['accounts', me])), - autoPlayGif: getSettings(state).get('autoPlayGif'), version: parseVersion(state.getIn(['instance', 'version'])), }; }; @@ -70,7 +69,6 @@ class Header extends ImmutablePureComponent { onBlock: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, username: PropTypes.string, - autoPlayGif: PropTypes.bool, isStaff: PropTypes.bool.isRequired, version: PropTypes.object, }; @@ -226,7 +224,7 @@ class Header extends ImmutablePureComponent { }; render() { - const { account, intl, username, me, autoPlayGif } = this.props; + const { account, intl, username, me } = this.props; const { isSmallScreen } = this.state; if (!account) { @@ -252,8 +250,7 @@ class Header extends ImmutablePureComponent { const actionBtn = this.getActionBtn(); const menu = this.makeMenu(); - const headerImgSrc = autoPlayGif ? account.get('header') : account.get('header_static'); - const headerMissing = (headerImgSrc.indexOf('/headers/original/missing.png') > -1); + const headerMissing = (account.get('header').indexOf('/headers/original/missing.png') > -1); const avatarSize = isSmallScreen ? 90 : 200; @@ -264,7 +261,7 @@ class Header extends ImmutablePureComponent { {info}
- +
diff --git a/app/soapbox/features/edit_profile/components/profile_preview.js b/app/soapbox/features/edit_profile/components/profile_preview.js index 0e8923207..6b4166d22 100644 --- a/app/soapbox/features/edit_profile/components/profile_preview.js +++ b/app/soapbox/features/edit_profile/components/profile_preview.js @@ -1,16 +1,17 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { acctFull } from 'soapbox/utils/accounts'; +import StillImage from 'soapbox/components/still_image'; const ProfilePreview = ({ account }) => (
- +
- +
{account.get('username')} diff --git a/app/soapbox/features/ui/components/user_panel.js b/app/soapbox/features/ui/components/user_panel.js index d434ff4cc..83dfd2099 100644 --- a/app/soapbox/features/ui/components/user_panel.js +++ b/app/soapbox/features/ui/components/user_panel.js @@ -9,7 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import Avatar from 'soapbox/components/avatar'; import { shortNumberFormat } from 'soapbox/utils/numbers'; import { acctFull } from 'soapbox/utils/accounts'; -import { getSettings } from 'soapbox/actions/settings'; +import StillImage from 'soapbox/components/still_image'; class UserPanel extends ImmutablePureComponent { @@ -20,7 +20,7 @@ class UserPanel extends ImmutablePureComponent { } render() { - const { account, intl, domain, autoPlayGif } = this.props; + const { account, intl, domain } = this.props; if (!account) return null; const displayNameHtml = { __html: account.get('display_name_html') }; const acct = account.get('acct').indexOf('@') === -1 && domain ? `${account.get('acct')}@${domain}` : account.get('acct'); @@ -30,7 +30,7 @@ class UserPanel extends ImmutablePureComponent {
- +
@@ -91,7 +91,6 @@ const mapStateToProps = state => { return { account: getAccount(state, me), - autoPlayGif: getSettings(state).get('autoPlayGif'), }; }; diff --git a/app/styles/accounts.scss b/app/styles/accounts.scss index 02dec30f8..da3025186 100644 --- a/app/styles/accounts.scss +++ b/app/styles/accounts.scss @@ -24,7 +24,7 @@ background: var(--background-color); border-radius: 4px 4px 0 0; - img { + .still-image { display: block; width: 100%; height: 100%; @@ -61,7 +61,7 @@ height: 48px; padding-top: 2px; - img { + .still-image { width: 100%; height: 100%; display: block; diff --git a/app/styles/components/account-header.scss b/app/styles/components/account-header.scss index 7fff5095d..78a9e7e78 100644 --- a/app/styles/components/account-header.scss +++ b/app/styles/components/account-header.scss @@ -21,7 +21,6 @@ background: var(--accent-color--med); @media screen and (max-width: 895px) {height: 225px;} &--none {height: 125px;} - img { object-fit: cover; display: block; @@ -29,6 +28,23 @@ height: 100%; margin: 0; } + + .still-image--play-on-hover::before { + content: 'GIF'; + position: absolute; + color: var(--primary-text-color); + background: var(--foreground-color); + top: 6px; + left: 6px; + padding: 2px 6px; + border-radius: 2px; + font-size: 11px; + font-weight: 600; + pointer-events: none; + opacity: 0.9; + transition: opacity 0.1s ease; + line-height: 18px; + } } &__bar { @@ -58,6 +74,23 @@ background-size: 200px 200px; } + .still-image--play-on-hover::before { + content: 'GIF'; + position: absolute; + color: var(--primary-text-color); + background: var(--foreground-color); + bottom: 15%; + left: 15%; + padding: 1px 4px; + border-radius: 2px; + font-size: 8px; + font-weight: 600; + pointer-events: none; + opacity: 0.9; + transition: opacity 0.1s ease; + line-height: 13px; + } + @media screen and (max-width: 895px) { top: -45px; left: 10px; From 853a68cd5479ffc5f7a14a93a065db8f08bfe424 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 10 Jun 2020 19:10:13 -0500 Subject: [PATCH 04/16] Fix avatar tests --- .../__snapshots__/avatar-test.js.snap | 26 +++++++++------ .../__snapshots__/avatar_overlay-test.js.snap | 32 +++++++++++-------- .../components/__tests__/avatar-test.js | 8 ++--- .../__tests__/avatar_overlay-test.js | 6 ++-- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/app/soapbox/components/__tests__/__snapshots__/avatar-test.js.snap b/app/soapbox/components/__tests__/__snapshots__/avatar-test.js.snap index c3139fe69..2938fb3ed 100644 --- a/app/soapbox/components/__tests__/__snapshots__/avatar-test.js.snap +++ b/app/soapbox/components/__tests__/__snapshots__/avatar-test.js.snap @@ -2,30 +2,36 @@ exports[` Autoplay renders an animated avatar 1`] = `
+> + +
`; exports[` Still renders a still avatar 1`] = `
+> + +
`; diff --git a/app/soapbox/components/__tests__/__snapshots__/avatar_overlay-test.js.snap b/app/soapbox/components/__tests__/__snapshots__/avatar_overlay-test.js.snap index d59fee42f..6481d1b69 100644 --- a/app/soapbox/components/__tests__/__snapshots__/avatar_overlay-test.js.snap +++ b/app/soapbox/components/__tests__/__snapshots__/avatar_overlay-test.js.snap @@ -5,20 +5,24 @@ exports[`
+ className="account__avatar-overlay-base still-image" + style={Object {}} + > + +
+ className="account__avatar-overlay-overlay still-image" + style={Object {}} + > + +
`; diff --git a/app/soapbox/components/__tests__/avatar-test.js b/app/soapbox/components/__tests__/avatar-test.js index f7f430c7a..297b0b413 100644 --- a/app/soapbox/components/__tests__/avatar-test.js +++ b/app/soapbox/components/__tests__/avatar-test.js @@ -1,7 +1,7 @@ import React from 'react'; -import renderer from 'react-test-renderer'; import { fromJS } from 'immutable'; -import { Avatar } from '../avatar'; +import { createComponent } from 'soapbox/test_helpers'; +import Avatar from '../avatar'; describe('', () => { const account = fromJS({ @@ -16,7 +16,7 @@ describe('', () => { describe('Autoplay', () => { it('renders an animated avatar', () => { - const component = renderer.create(); + const component = createComponent(); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); @@ -25,7 +25,7 @@ describe('', () => { describe('Still', () => { it('renders a still avatar', () => { - const component = renderer.create(); + const component = createComponent(); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/app/soapbox/components/__tests__/avatar_overlay-test.js b/app/soapbox/components/__tests__/avatar_overlay-test.js index ea75dab57..c469dcc75 100644 --- a/app/soapbox/components/__tests__/avatar_overlay-test.js +++ b/app/soapbox/components/__tests__/avatar_overlay-test.js @@ -1,7 +1,7 @@ import React from 'react'; -import renderer from 'react-test-renderer'; import { fromJS } from 'immutable'; -import { AvatarOverlay } from '../avatar_overlay'; +import { createComponent } from 'soapbox/test_helpers'; +import AvatarOverlay from '../avatar_overlay'; describe(' { const account = fromJS({ @@ -21,7 +21,7 @@ describe(' { }); it('renders a overlay avatar', () => { - const component = renderer.create(); + const component = createComponent(); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); From 24b72b5828c249ceb4076708eb03dd17be08bdaa Mon Sep 17 00:00:00 2001 From: crockwave Date: Wed, 17 Jun 2020 20:42:30 -0500 Subject: [PATCH 05/16] Replace componentWillMount with componentDidMount and as required, construct(). Fixes #179 --- app/soapbox/features/about/index.js | 2 +- .../features/account/components/header.js | 2 +- .../features/account_timeline/index.js | 2 +- .../features/auth_login/components/captcha.js | 2 +- app/soapbox/features/blocks/index.js | 2 +- .../compose/components/privacy_dropdown.js | 23 ++++++++++--------- app/soapbox/features/domain_blocks/index.js | 2 +- app/soapbox/features/edit_profile/index.js | 9 +++----- .../features/favourited_statuses/index.js | 2 +- app/soapbox/features/favourites/index.js | 2 +- app/soapbox/features/filters/index.js | 2 +- app/soapbox/features/follow_requests/index.js | 2 +- app/soapbox/features/followers/index.js | 2 +- app/soapbox/features/following/index.js | 2 +- app/soapbox/features/groups/create/index.js | 3 ++- app/soapbox/features/groups/edit/index.js | 3 ++- app/soapbox/features/groups/index/index.js | 2 +- app/soapbox/features/groups/members/index.js | 2 +- .../features/groups/removed_accounts/index.js | 2 +- app/soapbox/features/introduction/index.js | 3 ++- app/soapbox/features/lists/index.js | 2 +- app/soapbox/features/mutes/index.js | 2 +- app/soapbox/features/pinned_statuses/index.js | 2 +- app/soapbox/features/reblogs/index.js | 2 +- app/soapbox/features/status/index.js | 5 +--- app/soapbox/features/ui/components/bundle.js | 2 +- .../ui/components/focal_point_modal.js | 2 +- app/soapbox/features/ui/index.js | 11 +++------ app/soapbox/pages/group_page.js | 2 +- 29 files changed, 47 insertions(+), 54 deletions(-) diff --git a/app/soapbox/features/about/index.js b/app/soapbox/features/about/index.js index 55d716878..df7b41aa2 100644 --- a/app/soapbox/features/about/index.js +++ b/app/soapbox/features/about/index.js @@ -20,7 +20,7 @@ class AboutPage extends ImmutablePureComponent { }); } - componentWillMount() { + componentDidMount() { this.loadPageHtml(); } diff --git a/app/soapbox/features/account/components/header.js b/app/soapbox/features/account/components/header.js index 83c87e0bf..c0d30bb65 100644 --- a/app/soapbox/features/account/components/header.js +++ b/app/soapbox/features/account/components/header.js @@ -91,7 +91,7 @@ class Header extends ImmutablePureComponent { return !location.pathname.match(/\/(followers|following|favorites|pins)\/?$/); } - componentWillMount() { + componentDidMount() { window.addEventListener('resize', this.handleResize, { passive: true }); } diff --git a/app/soapbox/features/account_timeline/index.js b/app/soapbox/features/account_timeline/index.js index 4bd7090c0..99ef9af94 100644 --- a/app/soapbox/features/account_timeline/index.js +++ b/app/soapbox/features/account_timeline/index.js @@ -64,7 +64,7 @@ class AccountTimeline extends ImmutablePureComponent { unavailable: PropTypes.bool, }; - componentWillMount() { + componentDidMount() { const { params: { username }, accountId, withReplies, me } = this.props; if (accountId && accountId !== -1) { diff --git a/app/soapbox/features/auth_login/components/captcha.js b/app/soapbox/features/auth_login/components/captcha.js index 381c5de32..3921f3d6e 100644 --- a/app/soapbox/features/auth_login/components/captcha.js +++ b/app/soapbox/features/auth_login/components/captcha.js @@ -61,7 +61,7 @@ class CaptchaField extends React.Component { }); } - componentWillMount() { + componentDidMount() { this.fetchCaptcha(); this.startRefresh(); // Refresh periodically } diff --git a/app/soapbox/features/blocks/index.js b/app/soapbox/features/blocks/index.js index 0a0560c5b..ef406e3be 100644 --- a/app/soapbox/features/blocks/index.js +++ b/app/soapbox/features/blocks/index.js @@ -32,7 +32,7 @@ class Blocks extends ImmutablePureComponent { intl: PropTypes.object.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchBlocks()); } diff --git a/app/soapbox/features/compose/components/privacy_dropdown.js b/app/soapbox/features/compose/components/privacy_dropdown.js index dd13033c9..d980c715b 100644 --- a/app/soapbox/features/compose/components/privacy_dropdown.js +++ b/app/soapbox/features/compose/components/privacy_dropdown.js @@ -168,6 +168,18 @@ class PrivacyDropdown extends React.PureComponent { placement: 'bottom', }; + constructor(props) { + super(props); + const { intl: { formatMessage } } = this.props; + + this.options = [ + { icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) }, + { icon: 'unlock', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) }, + { icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) }, + { icon: 'envelope', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) }, + ]; + } + handleToggle = ({ target }) => { if (this.props.isUserTouching()) { if (this.state.open) { @@ -210,17 +222,6 @@ class PrivacyDropdown extends React.PureComponent { this.props.onChange(value); } - componentWillMount() { - const { intl: { formatMessage } } = this.props; - - this.options = [ - { icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) }, - { icon: 'unlock', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) }, - { icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) }, - { icon: 'envelope', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) }, - ]; - } - render() { const { value, intl } = this.props; const { open, placement } = this.state; diff --git a/app/soapbox/features/domain_blocks/index.js b/app/soapbox/features/domain_blocks/index.js index ba2fe8bdf..9c1e313cc 100644 --- a/app/soapbox/features/domain_blocks/index.js +++ b/app/soapbox/features/domain_blocks/index.js @@ -33,7 +33,7 @@ class Blocks extends ImmutablePureComponent { intl: PropTypes.object.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchDomainBlocks()); } diff --git a/app/soapbox/features/edit_profile/index.js b/app/soapbox/features/edit_profile/index.js index bd908afd4..dde0d5e5c 100644 --- a/app/soapbox/features/edit_profile/index.js +++ b/app/soapbox/features/edit_profile/index.js @@ -117,18 +117,15 @@ class EditProfile extends ImmutablePureComponent { event.preventDefault(); } - setInitialState = () => { + constructor(props) { + super(props); const initialState = this.props.account.withMutations(map => { map.merge(map.get('source')); map.delete('source'); map.set('fields', normalizeFields(map.get('fields'))); unescapeParams(map, ['display_name', 'note']); }); - this.setState(initialState.toObject()); - } - - componentWillMount() { - this.setInitialState(); + this.state = initialState.toObject(); } handleCheckboxChange = e => { diff --git a/app/soapbox/features/favourited_statuses/index.js b/app/soapbox/features/favourited_statuses/index.js index 610dbf2bc..9e831e03b 100644 --- a/app/soapbox/features/favourited_statuses/index.js +++ b/app/soapbox/features/favourited_statuses/index.js @@ -34,7 +34,7 @@ class Favourites extends ImmutablePureComponent { isMyAccount: PropTypes.bool.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchFavouritedStatuses()); } diff --git a/app/soapbox/features/favourites/index.js b/app/soapbox/features/favourites/index.js index 84cb94c1a..db5cbef49 100644 --- a/app/soapbox/features/favourites/index.js +++ b/app/soapbox/features/favourites/index.js @@ -23,7 +23,7 @@ class Favourites extends ImmutablePureComponent { accountIds: ImmutablePropTypes.list, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchFavourites(this.props.params.statusId)); } diff --git a/app/soapbox/features/filters/index.js b/app/soapbox/features/filters/index.js index 7e6048532..7ae71daac 100644 --- a/app/soapbox/features/filters/index.js +++ b/app/soapbox/features/filters/index.js @@ -24,7 +24,7 @@ class Filters extends ImmutablePureComponent { intl: PropTypes.object.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchFilters()); } diff --git a/app/soapbox/features/follow_requests/index.js b/app/soapbox/features/follow_requests/index.js index d40e947bf..e956e5c34 100644 --- a/app/soapbox/features/follow_requests/index.js +++ b/app/soapbox/features/follow_requests/index.js @@ -32,7 +32,7 @@ class FollowRequests extends ImmutablePureComponent { intl: PropTypes.object.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchFollowRequests()); } diff --git a/app/soapbox/features/followers/index.js b/app/soapbox/features/followers/index.js index 86709545e..87a60272d 100644 --- a/app/soapbox/features/followers/index.js +++ b/app/soapbox/features/followers/index.js @@ -58,7 +58,7 @@ class Followers extends ImmutablePureComponent { unavailable: PropTypes.bool, }; - componentWillMount() { + componentDidMount() { const { params: { username }, accountId } = this.props; if (accountId && accountId !== -1) { diff --git a/app/soapbox/features/following/index.js b/app/soapbox/features/following/index.js index db42b53b3..f3d404a23 100644 --- a/app/soapbox/features/following/index.js +++ b/app/soapbox/features/following/index.js @@ -58,7 +58,7 @@ class Following extends ImmutablePureComponent { diffCount: PropTypes.number, }; - componentWillMount() { + componentDidMount() { const { params: { username }, accountId } = this.props; if (accountId && accountId !== -1) { diff --git a/app/soapbox/features/groups/create/index.js b/app/soapbox/features/groups/create/index.js index 83ac82583..5bf9b50f4 100644 --- a/app/soapbox/features/groups/create/index.js +++ b/app/soapbox/features/groups/create/index.js @@ -49,7 +49,8 @@ class Create extends React.PureComponent { onCoverImageChange: PropTypes.func.isRequired, }; - componentWillMount() { + constructor(props) { + super(props); this.props.reset(); } diff --git a/app/soapbox/features/groups/edit/index.js b/app/soapbox/features/groups/edit/index.js index f63447b2f..a11d64a6f 100644 --- a/app/soapbox/features/groups/edit/index.js +++ b/app/soapbox/features/groups/edit/index.js @@ -55,7 +55,8 @@ class Edit extends React.PureComponent { setUp: PropTypes.func.isRequired, }; - componentWillMount(nextProps) { + constructor(nextProps) { + super(nextProps); if (this.props.group) { this.props.setUp(this.props.group); } diff --git a/app/soapbox/features/groups/index/index.js b/app/soapbox/features/groups/index/index.js index f99c5533e..36269b08a 100644 --- a/app/soapbox/features/groups/index/index.js +++ b/app/soapbox/features/groups/index/index.js @@ -36,7 +36,7 @@ class Groups extends ImmutablePureComponent { intl: PropTypes.object.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchGroups(this.props.activeTab)); } diff --git a/app/soapbox/features/groups/members/index.js b/app/soapbox/features/groups/members/index.js index adf1984ca..0fa713906 100644 --- a/app/soapbox/features/groups/members/index.js +++ b/app/soapbox/features/groups/members/index.js @@ -30,7 +30,7 @@ class GroupMembers extends ImmutablePureComponent { hasMore: PropTypes.bool, }; - componentWillMount() { + componentDidMount() { const { params: { id } } = this.props; this.props.dispatch(fetchMembers(id)); diff --git a/app/soapbox/features/groups/removed_accounts/index.js b/app/soapbox/features/groups/removed_accounts/index.js index c3431fe2f..3f49c3546 100644 --- a/app/soapbox/features/groups/removed_accounts/index.js +++ b/app/soapbox/features/groups/removed_accounts/index.js @@ -37,7 +37,7 @@ class GroupRemovedAccounts extends ImmutablePureComponent { hasMore: PropTypes.bool, }; - componentWillMount() { + componentDidMount() { const { params: { id } } = this.props; this.props.dispatch(fetchRemovedAccounts(id)); diff --git a/app/soapbox/features/introduction/index.js b/app/soapbox/features/introduction/index.js index 42645ecf3..2ac40a865 100644 --- a/app/soapbox/features/introduction/index.js +++ b/app/soapbox/features/introduction/index.js @@ -84,7 +84,8 @@ class Introduction extends React.PureComponent { currentIndex: 0, }; - componentWillMount() { + constructor(props) { + super(props); this.pages = [ , , diff --git a/app/soapbox/features/lists/index.js b/app/soapbox/features/lists/index.js index ffa7f9528..16166047f 100644 --- a/app/soapbox/features/lists/index.js +++ b/app/soapbox/features/lists/index.js @@ -42,7 +42,7 @@ class Lists extends ImmutablePureComponent { intl: PropTypes.object.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchLists()); } diff --git a/app/soapbox/features/mutes/index.js b/app/soapbox/features/mutes/index.js index f6814af15..8b7368845 100644 --- a/app/soapbox/features/mutes/index.js +++ b/app/soapbox/features/mutes/index.js @@ -32,7 +32,7 @@ class Mutes extends ImmutablePureComponent { intl: PropTypes.object.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchMutes()); } diff --git a/app/soapbox/features/pinned_statuses/index.js b/app/soapbox/features/pinned_statuses/index.js index 0a6bab195..5eef23e00 100644 --- a/app/soapbox/features/pinned_statuses/index.js +++ b/app/soapbox/features/pinned_statuses/index.js @@ -31,7 +31,7 @@ class PinnedStatuses extends ImmutablePureComponent { isMyAccount: PropTypes.bool.isRequired, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchPinnedStatuses()); } diff --git a/app/soapbox/features/reblogs/index.js b/app/soapbox/features/reblogs/index.js index d71c4330b..5e65e6f30 100644 --- a/app/soapbox/features/reblogs/index.js +++ b/app/soapbox/features/reblogs/index.js @@ -36,7 +36,7 @@ class Reblogs extends ImmutablePureComponent { status: ImmutablePropTypes.map, }; - componentWillMount() { + componentDidMount() { this.props.dispatch(fetchReblogs(this.props.params.statusId)); this.props.dispatch(fetchStatus(this.props.params.statusId)); } diff --git a/app/soapbox/features/status/index.js b/app/soapbox/features/status/index.js index be2227311..fbdadf191 100644 --- a/app/soapbox/features/status/index.js +++ b/app/soapbox/features/status/index.js @@ -139,11 +139,8 @@ class Status extends ImmutablePureComponent { loadedStatusId: undefined, }; - componentWillMount() { - this.props.dispatch(fetchStatus(this.props.params.statusId)); - } - componentDidMount() { + this.props.dispatch(fetchStatus(this.props.params.statusId)); attachFullscreenListener(this.onFullScreenChange); } diff --git a/app/soapbox/features/ui/components/bundle.js b/app/soapbox/features/ui/components/bundle.js index a3c7ab77e..7089597b6 100644 --- a/app/soapbox/features/ui/components/bundle.js +++ b/app/soapbox/features/ui/components/bundle.js @@ -33,7 +33,7 @@ class Bundle extends React.PureComponent { forceRender: false, } - componentWillMount() { + componentDidMount() { this.load(this.props); } diff --git a/app/soapbox/features/ui/components/focal_point_modal.js b/app/soapbox/features/ui/components/focal_point_modal.js index 9ec15768a..1b9246390 100644 --- a/app/soapbox/features/ui/components/focal_point_modal.js +++ b/app/soapbox/features/ui/components/focal_point_modal.js @@ -34,7 +34,7 @@ class FocalPointModal extends ImmutablePureComponent { dragging: false, }; - componentWillMount() { + componentDidMount() { this.updatePositionFromMedia(this.props.media); } diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index f98d09088..11e0669a9 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -167,7 +167,7 @@ class SwitchingColumnsArea extends React.PureComponent { mobile: isMobile(window.innerWidth), }; - componentWillMount() { + componentDidMount() { window.addEventListener('resize', this.handleResize, { passive: true }); } @@ -394,9 +394,9 @@ class UI extends React.PureComponent { } } - - componentWillMount() { + componentDidMount() { const { me } = this.props; + if (!me) return; window.addEventListener('beforeunload', this.handleBeforeUnload, false); document.addEventListener('dragenter', this.handleDragEnter, false); @@ -420,11 +420,6 @@ class UI extends React.PureComponent { setTimeout(() => this.props.dispatch(fetchFilters()), 500); } - } - - componentDidMount() { - const { me } = this.props; - if (!me) return; this.connectStreaming(); } diff --git a/app/soapbox/pages/group_page.js b/app/soapbox/pages/group_page.js index 423f0b54e..9458638b9 100644 --- a/app/soapbox/pages/group_page.js +++ b/app/soapbox/pages/group_page.js @@ -25,7 +25,7 @@ class GroupPage extends ImmutablePureComponent { dispatch: PropTypes.func.isRequired, }; - componentWillMount() { + componentDidMount() { const { params: { id }, dispatch } = this.props; dispatch(fetchGroup(id)); From 10ebea7489f5ef4795d36b2ad4c6f930677be532 Mon Sep 17 00:00:00 2001 From: crockwave Date: Thu, 18 Jun 2020 16:04:31 -0500 Subject: [PATCH 06/16] Replaced componentWillUpdate to componentDidUpdate. Fixes #185 --- app/soapbox/components/relative_timestamp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/components/relative_timestamp.js b/app/soapbox/components/relative_timestamp.js index e66c031c1..b67426c40 100644 --- a/app/soapbox/components/relative_timestamp.js +++ b/app/soapbox/components/relative_timestamp.js @@ -147,7 +147,7 @@ class RelativeTimestamp extends React.Component { this._scheduleNextUpdate(this.props, this.state); } - componentWillUpdate(nextProps, nextState) { + componentDidUpdate(nextProps, nextState) { this._scheduleNextUpdate(nextProps, nextState); } From 8a43cb3709104c9cab67b16eb2d48fb3e3dec346 Mon Sep 17 00:00:00 2001 From: crockwave Date: Thu, 18 Jun 2020 20:30:46 -0500 Subject: [PATCH 07/16] partial completion of replacing componentWillReceiveProps --- app/soapbox/components/autosuggest_input.js | 9 ++++++--- app/soapbox/components/autosuggest_textarea.js | 9 ++++++--- app/soapbox/components/media_gallery.js | 18 ++++++++++++------ app/soapbox/components/modal_root.js | 7 ++----- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/app/soapbox/components/autosuggest_input.js b/app/soapbox/components/autosuggest_input.js index 3136325a0..8b4180b5b 100644 --- a/app/soapbox/components/autosuggest_input.js +++ b/app/soapbox/components/autosuggest_input.js @@ -153,10 +153,13 @@ export default class AutosuggestInput extends ImmutablePureComponent { this.input.focus(); } - componentWillReceiveProps(nextProps) { - if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) { - this.setState({ suggestionsHidden: false }); + static getDerivedStateFromProps(nextProps, state) { + if (nextProps.suggestions && nextProps.suggestions.size > 0 && state.suggestionsHidden && state.focused) { + return { + suggestionsHidden: false, + }; } + return null; } setInput = (c) => { diff --git a/app/soapbox/components/autosuggest_textarea.js b/app/soapbox/components/autosuggest_textarea.js index cae6116d4..4f686ac37 100644 --- a/app/soapbox/components/autosuggest_textarea.js +++ b/app/soapbox/components/autosuggest_textarea.js @@ -159,10 +159,13 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { this.textarea.focus(); } - componentWillReceiveProps(nextProps) { - if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) { - this.setState({ suggestionsHidden: false }); + static getDerivedStateFromProps(nextProps, state) { + if (nextProps.suggestions && nextProps.suggestions.size > 0 && state.suggestionsHidden && state.focused) { + return { + suggestionsHidden: false, + }; } + return null; } setTextarea = (c) => { diff --git a/app/soapbox/components/media_gallery.js b/app/soapbox/components/media_gallery.js index 4ab6e53e0..66719d2b9 100644 --- a/app/soapbox/components/media_gallery.js +++ b/app/soapbox/components/media_gallery.js @@ -267,15 +267,21 @@ class MediaGallery extends React.PureComponent { state = { visible: this.props.visible !== undefined ? this.props.visible : (this.props.displayMedia !== 'hide_all' && !this.props.sensitive || this.props.displayMedia === 'show_all'), width: this.props.defaultWidth, + media: this.props.media, + displayMedia: this.props.displayMedia, }; - componentWillReceiveProps(nextProps) { - const { displayMedia } = this.props; - if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) { - this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' }); - } else if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) { - this.setState({ visible: nextProps.visible }); + static getDerivedStateFromProps(nextProps, state) { + if (!is(nextProps.media, state.media) && nextProps.visible === undefined) { + return { + visible: state.displayMedia !== 'hide_all' && !nextProps.sensitive || state.displayMedia === 'show_all', + }; + } else if (!is(nextProps.visible, state.visible) && nextProps.visible !== undefined) { + return { + visible: nextProps.visible, + }; } + return null; } handleOpen = () => { diff --git a/app/soapbox/components/modal_root.js b/app/soapbox/components/modal_root.js index 17cdaa64a..9857ad616 100644 --- a/app/soapbox/components/modal_root.js +++ b/app/soapbox/components/modal_root.js @@ -78,21 +78,18 @@ class ModalRoot extends React.PureComponent { window.addEventListener('keyup', this.handleKeyUp, false); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps, prevProps) { if (!!nextProps.children && !this.props.children) { this.activeElement = document.activeElement; - this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true)); } else if (!nextProps.children) { this.setState({ revealed: false }); } if (!nextProps.children && !!this.props.children) { + this.activeElement = document.activeElement; this.activeElement.focus(); this.activeElement = null; } - } - - componentDidUpdate(prevProps) { if (!this.props.children && !!prevProps.children) { this.getSiblings().forEach(sibling => sibling.removeAttribute('inert')); } From 3d29f7b6e2d559126017bdeb97bbf43bc0aab9fd Mon Sep 17 00:00:00 2001 From: crockwave Date: Mon, 22 Jun 2020 20:26:20 -0500 Subject: [PATCH 08/16] partial mods to replace deprecated componentWillReceiveProps --- app/soapbox/components/relative_timestamp.js | 2 +- app/soapbox/features/account_gallery/index.js | 2 +- app/soapbox/features/account_timeline/index.js | 2 +- .../features/compose/components/emoji_picker_dropdown.js | 2 +- app/soapbox/features/favourites/index.js | 2 +- app/soapbox/features/followers/index.js | 2 +- app/soapbox/features/following/index.js | 2 +- app/soapbox/features/groups/edit/index.js | 2 +- app/soapbox/features/groups/members/index.js | 2 +- app/soapbox/features/groups/removed_accounts/index.js | 2 +- app/soapbox/features/hashtag_timeline/index.js | 2 +- app/soapbox/features/list_timeline/index.js | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/soapbox/components/relative_timestamp.js b/app/soapbox/components/relative_timestamp.js index e66c031c1..b089c6dfc 100644 --- a/app/soapbox/components/relative_timestamp.js +++ b/app/soapbox/components/relative_timestamp.js @@ -137,7 +137,7 @@ class RelativeTimestamp extends React.Component { this.state.now !== nextState.now; } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (this.props.timestamp !== nextProps.timestamp) { this.setState({ now: Date.now() }); } diff --git a/app/soapbox/features/account_gallery/index.js b/app/soapbox/features/account_gallery/index.js index de2213b07..0b55a0cb0 100644 --- a/app/soapbox/features/account_gallery/index.js +++ b/app/soapbox/features/account_gallery/index.js @@ -97,7 +97,7 @@ class AccountGallery extends ImmutablePureComponent { } } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) { this.props.dispatch(fetchAccount(nextProps.params.accountId)); this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId)); diff --git a/app/soapbox/features/account_timeline/index.js b/app/soapbox/features/account_timeline/index.js index 4bd7090c0..131da0579 100644 --- a/app/soapbox/features/account_timeline/index.js +++ b/app/soapbox/features/account_timeline/index.js @@ -81,7 +81,7 @@ class AccountTimeline extends ImmutablePureComponent { } } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { const { me } = nextProps; if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId) || nextProps.withReplies !== this.props.withReplies) { this.props.dispatch(fetchAccount(nextProps.accountId)); diff --git a/app/soapbox/features/compose/components/emoji_picker_dropdown.js b/app/soapbox/features/compose/components/emoji_picker_dropdown.js index f88df3a49..dc3ec4c7f 100644 --- a/app/soapbox/features/compose/components/emoji_picker_dropdown.js +++ b/app/soapbox/features/compose/components/emoji_picker_dropdown.js @@ -56,7 +56,7 @@ class ModifierPickerMenu extends React.PureComponent { this.props.onSelect(e.currentTarget.getAttribute('data-index') * 1); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.active) { this.attachListeners(); } else { diff --git a/app/soapbox/features/favourites/index.js b/app/soapbox/features/favourites/index.js index 84cb94c1a..0097315bb 100644 --- a/app/soapbox/features/favourites/index.js +++ b/app/soapbox/features/favourites/index.js @@ -27,7 +27,7 @@ class Favourites extends ImmutablePureComponent { this.props.dispatch(fetchFavourites(this.props.params.statusId)); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { this.props.dispatch(fetchFavourites(nextProps.params.statusId)); } diff --git a/app/soapbox/features/followers/index.js b/app/soapbox/features/followers/index.js index 86709545e..48eeabb0b 100644 --- a/app/soapbox/features/followers/index.js +++ b/app/soapbox/features/followers/index.js @@ -69,7 +69,7 @@ class Followers extends ImmutablePureComponent { } } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) { this.props.dispatch(fetchAccount(nextProps.accountId)); this.props.dispatch(fetchFollowers(nextProps.accountId)); diff --git a/app/soapbox/features/following/index.js b/app/soapbox/features/following/index.js index db42b53b3..8cfdbecf6 100644 --- a/app/soapbox/features/following/index.js +++ b/app/soapbox/features/following/index.js @@ -69,7 +69,7 @@ class Following extends ImmutablePureComponent { } } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.accountId && nextProps.accountId !== -1 && (nextProps.accountId !== this.props.accountId && nextProps.accountId)) { this.props.dispatch(fetchAccount(nextProps.accountId)); this.props.dispatch(fetchFollowing(nextProps.accountId)); diff --git a/app/soapbox/features/groups/edit/index.js b/app/soapbox/features/groups/edit/index.js index f63447b2f..e431faa42 100644 --- a/app/soapbox/features/groups/edit/index.js +++ b/app/soapbox/features/groups/edit/index.js @@ -61,7 +61,7 @@ class Edit extends React.PureComponent { } } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (!this.props.group && nextProps.group) { this.props.setUp(nextProps.group); } diff --git a/app/soapbox/features/groups/members/index.js b/app/soapbox/features/groups/members/index.js index adf1984ca..3e7f6128d 100644 --- a/app/soapbox/features/groups/members/index.js +++ b/app/soapbox/features/groups/members/index.js @@ -36,7 +36,7 @@ class GroupMembers extends ImmutablePureComponent { this.props.dispatch(fetchMembers(id)); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.params.id !== this.props.params.id) { this.props.dispatch(fetchMembers(nextProps.params.id)); } diff --git a/app/soapbox/features/groups/removed_accounts/index.js b/app/soapbox/features/groups/removed_accounts/index.js index c3431fe2f..a375ca90c 100644 --- a/app/soapbox/features/groups/removed_accounts/index.js +++ b/app/soapbox/features/groups/removed_accounts/index.js @@ -43,7 +43,7 @@ class GroupRemovedAccounts extends ImmutablePureComponent { this.props.dispatch(fetchRemovedAccounts(id)); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.params.id !== this.props.params.id) { this.props.dispatch(fetchRemovedAccounts(nextProps.params.id)); } diff --git a/app/soapbox/features/hashtag_timeline/index.js b/app/soapbox/features/hashtag_timeline/index.js index c73c7ce35..c841aeea4 100644 --- a/app/soapbox/features/hashtag_timeline/index.js +++ b/app/soapbox/features/hashtag_timeline/index.js @@ -81,7 +81,7 @@ class HashtagTimeline extends React.PureComponent { dispatch(expandHashtagTimeline(id, { tags })); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { const { dispatch, params } = this.props; const { id, tags } = nextProps.params; diff --git a/app/soapbox/features/list_timeline/index.js b/app/soapbox/features/list_timeline/index.js index 349052ab3..a606d90dc 100644 --- a/app/soapbox/features/list_timeline/index.js +++ b/app/soapbox/features/list_timeline/index.js @@ -50,7 +50,7 @@ class ListTimeline extends React.PureComponent { this.handleDisconnect(); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.params.id !== this.props.params.id) { this.handleDisconnect(); this.handleConnect(nextProps.params.id); From 81f9a1da3a4fcc77136dad20215a9a5471f391f7 Mon Sep 17 00:00:00 2001 From: crockwave Date: Wed, 24 Jun 2020 16:02:14 -0500 Subject: [PATCH 09/16] Final commit on replacing componentWillReceiveProps deprecated method --- app/soapbox/features/reblogs/index.js | 2 +- .../features/search/components/header.js | 2 +- .../features/status/components/card.js | 2 +- app/soapbox/features/status/index.js | 22 +++++++++---------- app/soapbox/features/ui/components/bundle.js | 2 +- .../ui/components/focal_point_modal.js | 2 +- .../features/ui/components/image_loader.js | 2 +- .../features/ui/components/report_modal.js | 2 +- app/soapbox/features/video/index.js | 2 +- 9 files changed, 18 insertions(+), 20 deletions(-) diff --git a/app/soapbox/features/reblogs/index.js b/app/soapbox/features/reblogs/index.js index d71c4330b..0c7c667aa 100644 --- a/app/soapbox/features/reblogs/index.js +++ b/app/soapbox/features/reblogs/index.js @@ -41,7 +41,7 @@ class Reblogs extends ImmutablePureComponent { this.props.dispatch(fetchStatus(this.props.params.statusId)); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { this.props.dispatch(fetchReblogs(nextProps.params.statusId)); this.props.dispatch(fetchStatus(nextProps.params.statusId)); diff --git a/app/soapbox/features/search/components/header.js b/app/soapbox/features/search/components/header.js index 02f7cd36e..82ccb46c6 100644 --- a/app/soapbox/features/search/components/header.js +++ b/app/soapbox/features/search/components/header.js @@ -21,7 +21,7 @@ class Header extends ImmutablePureComponent { submittedValue: '', }; - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (nextProps.submitted) { const submittedValue = nextProps.value; this.setState({ submittedValue }); diff --git a/app/soapbox/features/status/components/card.js b/app/soapbox/features/status/components/card.js index af2308461..bbca9db93 100644 --- a/app/soapbox/features/status/components/card.js +++ b/app/soapbox/features/status/components/card.js @@ -75,7 +75,7 @@ export default class Card extends React.PureComponent { embedded: false, }; - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (!Immutable.is(this.props.card, nextProps.card)) { this.setState({ embedded: false }); } diff --git a/app/soapbox/features/status/index.js b/app/soapbox/features/status/index.js index be2227311..b68c6f6af 100644 --- a/app/soapbox/features/status/index.js +++ b/app/soapbox/features/status/index.js @@ -147,17 +147,6 @@ class Status extends ImmutablePureComponent { attachFullscreenListener(this.onFullScreenChange); } - componentWillReceiveProps(nextProps) { - if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this._scrolledIntoView = false; - this.props.dispatch(fetchStatus(nextProps.params.statusId)); - } - - if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) { - this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') }); - } - } - handleToggleMediaVisibility = () => { this.setState({ showMedia: !this.state.showMedia }); } @@ -404,7 +393,16 @@ class Status extends ImmutablePureComponent { this.node = c; } - componentDidUpdate() { + componentDidUpdate(nextProps) { + if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { + this._scrolledIntoView = false; + this.props.dispatch(fetchStatus(nextProps.params.statusId)); + } + + if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) { + this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') }); + } + if (this._scrolledIntoView) { return; } diff --git a/app/soapbox/features/ui/components/bundle.js b/app/soapbox/features/ui/components/bundle.js index a3c7ab77e..532bff159 100644 --- a/app/soapbox/features/ui/components/bundle.js +++ b/app/soapbox/features/ui/components/bundle.js @@ -37,7 +37,7 @@ class Bundle extends React.PureComponent { this.load(this.props); } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { if (nextProps.fetchComponent !== this.props.fetchComponent) { this.load(nextProps); } diff --git a/app/soapbox/features/ui/components/focal_point_modal.js b/app/soapbox/features/ui/components/focal_point_modal.js index 9ec15768a..9119a192e 100644 --- a/app/soapbox/features/ui/components/focal_point_modal.js +++ b/app/soapbox/features/ui/components/focal_point_modal.js @@ -38,7 +38,7 @@ class FocalPointModal extends ImmutablePureComponent { this.updatePositionFromMedia(this.props.media); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (this.props.media.get('id') !== nextProps.media.get('id')) { this.updatePositionFromMedia(nextProps.media); } diff --git a/app/soapbox/features/ui/components/image_loader.js b/app/soapbox/features/ui/components/image_loader.js index 02a0441fa..939886f17 100644 --- a/app/soapbox/features/ui/components/image_loader.js +++ b/app/soapbox/features/ui/components/image_loader.js @@ -42,7 +42,7 @@ export default class ImageLoader extends React.PureComponent { this.loadImage(this.props); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (this.props.src !== nextProps.src) { this.loadImage(nextProps); } diff --git a/app/soapbox/features/ui/components/report_modal.js b/app/soapbox/features/ui/components/report_modal.js index fcd31afb1..dbe48f27f 100644 --- a/app/soapbox/features/ui/components/report_modal.js +++ b/app/soapbox/features/ui/components/report_modal.js @@ -83,7 +83,7 @@ class ReportModal extends ImmutablePureComponent { this.props.dispatch(expandAccountTimeline(this.props.account.get('id'), { withReplies: true })); } - componentWillReceiveProps(nextProps) { + componentDidUpdate(nextProps) { if (this.props.account !== nextProps.account && nextProps.account) { this.props.dispatch(expandAccountTimeline(nextProps.account.get('id'), { withReplies: true })); } diff --git a/app/soapbox/features/video/index.js b/app/soapbox/features/video/index.js index d49e08e65..490748274 100644 --- a/app/soapbox/features/video/index.js +++ b/app/soapbox/features/video/index.js @@ -291,7 +291,7 @@ class Video extends React.PureComponent { document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true); } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) { this.setState({ revealed: nextProps.visible }); } From b4aadc9337923e88dab58a21c62d02c2bd6dee03 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 27 Jun 2020 02:19:35 +0000 Subject: [PATCH 10/16] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91821cf8b..954562943 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Try again. You can also run Soapbox FE locally with a live production server as the backend. -> **Note:** Whether or not this works depends on your production server. It does not seem to work with Cloudflare. +> **Note:** Whether or not this works depends on your production server. It does not seem to work with Cloudflare or VanwaNet. To do so, just copy the env file: From 0c66cf43e1be08bacfb6862fc4386543d152710d Mon Sep 17 00:00:00 2001 From: Sean King Date: Fri, 26 Jun 2020 22:24:23 -0600 Subject: [PATCH 11/16] Switch to Fork Awesome --- app/soapbox/common.js | 2 +- package.json | 2 +- yarn.lock | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/soapbox/common.js b/app/soapbox/common.js index 22146c0e0..6947fd07c 100644 --- a/app/soapbox/common.js +++ b/app/soapbox/common.js @@ -3,7 +3,7 @@ import Rails from 'rails-ujs'; export function start() { - require('font-awesome/css/font-awesome.css'); + require('fork-awesome/css/fork-awesome.css'); require.context('../images/', true); try { diff --git a/package.json b/package.json index b3892176c..9682ef309 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "exif-js": "^2.3.0", "express": "^4.17.1", "file-loader": "^4.0.0", - "font-awesome": "^4.7.0", + "fork-awesome": "^1.1.7", "glob": "^7.1.1", "html-webpack-harddisk-plugin": "^1.0.1", "html-webpack-plugin": "^4.3.0", diff --git a/yarn.lock b/yarn.lock index aee589968..c6908bd86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5166,11 +5166,6 @@ follow-redirects@^1.0.0: dependencies: debug "=3.1.0" -font-awesome@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" - integrity sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM= - for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -5181,6 +5176,11 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +fork-awesome@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/fork-awesome/-/fork-awesome-1.1.7.tgz#1427da1cac3d1713046ee88427e5fcecb9501d21" + integrity sha512-IHI7XCSXrKfUIWslse8c/PaaVDT1oBaYge+ju40ihL2ooiQeBpTr4wvIXhgTd2NuhntlvX+M5jYHAPTzNlmv0g== + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" From 9166b95dfbe70a1d34f4fe1574c41a368a73803b Mon Sep 17 00:00:00 2001 From: Sean King Date: Fri, 26 Jun 2020 22:47:48 -0600 Subject: [PATCH 12/16] Final fixes to the switch from Font Awesome to Fork Awesome --- app/soapbox/components/home_column_header.js | 2 +- app/soapbox/components/icon.js | 2 +- app/styles/fonts.scss | 4 ++-- app/styles/ui.scss | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/soapbox/components/home_column_header.js b/app/soapbox/components/home_column_header.js index aca89888a..80cccb808 100644 --- a/app/soapbox/components/home_column_header.js +++ b/app/soapbox/components/home_column_header.js @@ -143,7 +143,7 @@ class ColumnHeader extends React.PureComponent { - + {siteTitle} diff --git a/app/soapbox/components/icon.js b/app/soapbox/components/icon.js index 9bf39abf9..8081aac12 100644 --- a/app/soapbox/components/icon.js +++ b/app/soapbox/components/icon.js @@ -12,7 +12,7 @@ export default class Icon extends React.PureComponent { render() { const { id, className, fixedWidth, ...other } = this.props; - // Use the font awesome retweet icon, but change its alt + // Use the Fork Awesome retweet icon, but change its alt // tag. There is a common adblocker rule which hides elements with // alt='retweet' unless the domain is twitter.com. This should // change what screenreaders call it as well. diff --git a/app/styles/fonts.scss b/app/styles/fonts.scss index 2ba14213e..354b9b9a9 100644 --- a/app/styles/fonts.scss +++ b/app/styles/fonts.scss @@ -159,8 +159,8 @@ font-style: normal; } -.fa-site-icon::before { - font-family: 'FontAwesome'; +.fa-users::before { + font-family: 'ForkAwesome'; content: '\f0c0'; } diff --git a/app/styles/ui.scss b/app/styles/ui.scss index b613802ed..7eab8edb9 100644 --- a/app/styles/ui.scss +++ b/app/styles/ui.scss @@ -739,7 +739,7 @@ display: block; position: absolute; content: '\f00c'; - font: normal normal normal 14px/1 FontAwesome; + font: normal normal normal 14px/1 ForkAwesome; font-size: inherit; text-rendering: auto; -webkit-font-smoothing: antialiased; From 73b275b8da5c32b4dde5f7b4cb55281b6b3b035c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 30 Jun 2020 17:33:21 -0500 Subject: [PATCH 13/16] Patron updates --- .env.example | 1 - README.md | 2 +- app/soapbox/actions/patron.js | 5 ++-- app/soapbox/components/sidebar_menu.js | 12 ++++++---- .../features/ui/components/funding_panel.js | 24 ++++++++++++++----- app/soapbox/pages/home_page.js | 2 +- static/instance/soapbox.example.json | 5 +++- webpack/development.js | 6 ----- 8 files changed, 34 insertions(+), 23 deletions(-) diff --git a/.env.example b/.env.example index 3004daa76..b116e6d40 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,3 @@ NODE_ENV=development # BACKEND_URL="https://example.com" -# PATRON_URL="https://patron.example.com" # PROXY_HTTPS_INSECURE=false diff --git a/README.md b/README.md index 954562943..30fa61a1d 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ For https, be sure to also set `PROXY_HTTPS_INSECURE=true`. Allows using an HTTPS backend if set to `true`. -This is needed if `BACKEND_URL` or `PATRON_URL` are set to an `https://` value. +This is needed if `BACKEND_URL` is set to an `https://` value. [More info](https://stackoverflow.com/a/48624590/8811886). **Default:** `false` diff --git a/app/soapbox/actions/patron.js b/app/soapbox/actions/patron.js index 6b4885c9a..3f32e2ad8 100644 --- a/app/soapbox/actions/patron.js +++ b/app/soapbox/actions/patron.js @@ -1,11 +1,12 @@ -import api from '../api'; +import axios from 'axios'; export const PATRON_FUNDING_IMPORT = 'PATRON_FUNDING_IMPORT'; export const PATRON_FUNDING_FETCH_FAIL = 'PATRON_FUNDING_FETCH_FAIL'; export function fetchFunding() { return (dispatch, getState) => { - api(getState).get('/patron/v1/funding').then(response => { + const baseUrl = getState().getIn(['soapbox', 'extensions', 'patron', 'baseUrl']); + axios.get(`${baseUrl}/api/patron/v1/instance`).then(response => { dispatch(importFetchedFunding(response.data)); }).catch(error => { dispatch(fetchFundingFail(error)); diff --git a/app/soapbox/components/sidebar_menu.js b/app/soapbox/components/sidebar_menu.js index 844a1bc4d..6c9a6e7ad 100644 --- a/app/soapbox/components/sidebar_menu.js +++ b/app/soapbox/components/sidebar_menu.js @@ -15,6 +15,7 @@ import { shortNumberFormat } from '../utils/numbers'; import { isStaff } from '../utils/accounts'; import { makeGetAccount } from '../selectors'; import { logOut } from 'soapbox/actions/auth'; +import { Map as ImmutableMap } from 'immutable'; const messages = defineMessages({ followers: { id: 'account.followers', defaultMessage: 'Followers' }, @@ -39,11 +40,12 @@ const messages = defineMessages({ const mapStateToProps = state => { const me = state.get('me'); const getAccount = makeGetAccount(); + const patron = state.getIn(['soapbox', 'extensions', 'patron'], ImmutableMap()); return { account: getAccount(state, me), sidebarOpen: state.get('sidebar').sidebarOpen, - hasPatron: state.getIn(['soapbox', 'extensions', 'patron']), + patronUrl: patron.get('enabled') && patron.get('baseUrl'), isStaff: isStaff(state.getIn(['accounts', me])), }; }; @@ -75,7 +77,7 @@ class SidebarMenu extends ImmutablePureComponent { } render() { - const { sidebarOpen, onClose, intl, account, onClickLogOut, hasPatron, isStaff } = this.props; + const { sidebarOpen, onClose, intl, account, onClickLogOut, patronUrl, isStaff } = this.props; if (!account) return null; const acct = account.get('acct'); @@ -127,11 +129,11 @@ class SidebarMenu extends ImmutablePureComponent { {intl.formatMessage(messages.messages)} - {hasPatron ? - + {patronUrl ? +
{intl.formatMessage(messages.donate)} - + : ''} diff --git a/app/soapbox/features/ui/components/funding_panel.js b/app/soapbox/features/ui/components/funding_panel.js index 7650c8cdf..df96512e2 100644 --- a/app/soapbox/features/ui/components/funding_panel.js +++ b/app/soapbox/features/ui/components/funding_panel.js @@ -5,6 +5,16 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import ProgressBar from '../../../components/progress_bar'; import { fetchFunding } from 'soapbox/actions/patron'; +const moneyFormat = amount => ( + new Intl + .NumberFormat('en-US', { + style: 'currency', + currency: 'usd', + notation: 'compact', + }) + .format(amount/100) +); + class FundingPanel extends ImmutablePureComponent { componentDidMount() { @@ -12,21 +22,22 @@ class FundingPanel extends ImmutablePureComponent { } render() { - const { funding } = this.props; + const { funding, patronUrl } = this.props; if (!funding) { return null; } + const amount = funding.getIn(['funding', 'amount']); const goal = funding.getIn(['goals', '0', 'amount']); const goal_text = funding.getIn(['goals', '0', 'text']); - const goal_reached = funding.get('amount') >= goal; + const goal_reached = amount >= goal; let ratio_text; if (goal_reached) { - ratio_text = <>${Math.floor(goal/100)} per month— reached!; + ratio_text = <>{moneyFormat(goal)} per month— reached!; } else { - ratio_text = <>${Math.floor(funding.get('amount')/100)} out of ${Math.floor(goal/100)} per month; + ratio_text = <>{moneyFormat(amount)} out of {moneyFormat(goal)} per month; } return ( @@ -41,11 +52,11 @@ class FundingPanel extends ImmutablePureComponent {
{ratio_text}
- +
{goal_text}
- Donate + {patronUrl && Donate}
); @@ -56,6 +67,7 @@ class FundingPanel extends ImmutablePureComponent { const mapStateToProps = state => { return { funding: state.getIn(['patron', 'funding']), + patronUrl: state.getIn(['soapbox', 'extensions', 'patron', 'baseUrl']), }; }; diff --git a/app/soapbox/pages/home_page.js b/app/soapbox/pages/home_page.js index 6a6a7f6fe..f06bd90eb 100644 --- a/app/soapbox/pages/home_page.js +++ b/app/soapbox/pages/home_page.js @@ -16,7 +16,7 @@ const mapStateToProps = state => { const me = state.get('me'); return { account: state.getIn(['accounts', me]), - hasPatron: state.getIn(['soapbox', 'extensions', 'patron']), + hasPatron: state.getIn(['soapbox', 'extensions', 'patron', 'enabled']), features: getFeatures(state.get('instance')), }; }; diff --git a/static/instance/soapbox.example.json b/static/instance/soapbox.example.json index 9083c8e7f..540fc51ed 100644 --- a/static/instance/soapbox.example.json +++ b/static/instance/soapbox.example.json @@ -13,7 +13,10 @@ }] }, "extensions": { - "patron": false + "patron": { + "enabled": false, + "baseUrl": "https://patron.example.com" + } }, "defaultSettings": { "autoPlayGif": false, diff --git a/webpack/development.js b/webpack/development.js index a16df0fbf..98ce3e718 100644 --- a/webpack/development.js +++ b/webpack/development.js @@ -9,7 +9,6 @@ const { settings, output } = require('./configuration'); const watchOptions = {}; const backendUrl = process.env.BACKEND_URL || 'http://localhost:4000'; -const patronUrl = process.env.PATRON_URL || 'http://localhost:5000'; const secureProxy = !(process.env.PROXY_HTTPS_INSECURE === 'true'); const backendEndpoints = [ @@ -22,7 +21,6 @@ const backendEndpoints = [ '/.well-known/webfinger', '/static', '/emoji', - '/patron', ]; const makeProxyConfig = () => { @@ -33,10 +31,6 @@ const makeProxyConfig = () => { secure: secureProxy, }; }); - proxyConfig['/patron'] = { - target: patronUrl, - secure: secureProxy, - }; return proxyConfig; }; From 8144c7d508ddc058b5197a3c6789c69f01a95efb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 1 Jul 2020 15:06:31 -0500 Subject: [PATCH 14/16] UserPanel header size fix, fixes #222 --- app/styles/components/user-panel.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/styles/components/user-panel.scss b/app/styles/components/user-panel.scss index 3850df929..cf49a3d68 100644 --- a/app/styles/components/user-panel.scss +++ b/app/styles/components/user-panel.scss @@ -11,6 +11,10 @@ width: 100%; background: var(--brand-color--faint); + .still-image { + height: 100%; + } + img { display: block; height: 100%; From 2d194d75aa6d8259f7881fb456dc926d063efa77 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 1 Jul 2020 15:08:18 -0500 Subject: [PATCH 15/16] Patron: Expect proxied endpoints --- app/soapbox/actions/patron.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/soapbox/actions/patron.js b/app/soapbox/actions/patron.js index 3f32e2ad8..ec9a35241 100644 --- a/app/soapbox/actions/patron.js +++ b/app/soapbox/actions/patron.js @@ -1,12 +1,11 @@ -import axios from 'axios'; +import api from '../api'; export const PATRON_FUNDING_IMPORT = 'PATRON_FUNDING_IMPORT'; export const PATRON_FUNDING_FETCH_FAIL = 'PATRON_FUNDING_FETCH_FAIL'; export function fetchFunding() { return (dispatch, getState) => { - const baseUrl = getState().getIn(['soapbox', 'extensions', 'patron', 'baseUrl']); - axios.get(`${baseUrl}/api/patron/v1/instance`).then(response => { + api(getState).get('/api/patron/v1/instance').then(response => { dispatch(importFetchedFunding(response.data)); }).catch(error => { dispatch(fetchFundingFail(error)); From e965c74760a6f9b6065d53d1b4bb0618b056c17b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 1 Jul 2020 17:10:46 -0500 Subject: [PATCH 16/16] Use object-fit: contain for sane image thumbnails, fixes #212 --- app/styles/components/compose-form.scss | 2 +- app/styles/components/media-gallery.scss | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/styles/components/compose-form.scss b/app/styles/components/compose-form.scss index 0b144c700..b2d61bc22 100644 --- a/app/styles/components/compose-form.scss +++ b/app/styles/components/compose-form.scss @@ -284,7 +284,7 @@ .compose-form__upload-thumbnail { border-radius: 4px; background-position: center; - background-size: cover; + background-size: contain; background-repeat: no-repeat; height: 140px; width: 100%; diff --git a/app/styles/components/media-gallery.scss b/app/styles/components/media-gallery.scss index 431610bb6..c98d212b3 100644 --- a/app/styles/components/media-gallery.scss +++ b/app/styles/components/media-gallery.scss @@ -31,6 +31,10 @@ .still-image { height: 100%; width: 100%; + + img { + object-fit: contain; + } } .still-image--play-on-hover::before {