diff --git a/app/soapbox/components/__tests__/avatar.test.js b/app/soapbox/components/__tests__/avatar.test.js index 6b50083ed..55abca520 100644 --- a/app/soapbox/components/__tests__/avatar.test.js +++ b/app/soapbox/components/__tests__/avatar.test.js @@ -1,11 +1,12 @@ -import { fromJS } from 'immutable'; import React from 'react'; +import { normalizeAccount } from 'soapbox/normalizers'; + import { render, screen } from '../../jest/test-helpers'; import Avatar from '../avatar'; describe('', () => { - const account = fromJS({ + const account = normalizeAccount({ username: 'alice', acct: 'alice', display_name: 'Alice', diff --git a/app/soapbox/components/avatar.js b/app/soapbox/components/avatar.js deleted file mode 100644 index d5000264d..000000000 --- a/app/soapbox/components/avatar.js +++ /dev/null @@ -1,39 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; - -import StillImage from 'soapbox/components/still_image'; - -export default class Avatar extends React.PureComponent { - - static propTypes = { - account: ImmutablePropTypes.record, - size: PropTypes.number, - style: PropTypes.object, - className: PropTypes.string, - }; - - render() { - const { account, size, className } = this.props; - if (!account) return null; - - // : TODO : remove inline and change all avatars to be sized using css - const style = !size ? {} : { - width: `${size}px`, - height: `${size}px`, - }; - - return ( - - ); - } - -} diff --git a/app/soapbox/components/avatar.tsx b/app/soapbox/components/avatar.tsx new file mode 100644 index 000000000..9d4fbaa7c --- /dev/null +++ b/app/soapbox/components/avatar.tsx @@ -0,0 +1,38 @@ +import classNames from 'classnames'; +import React from 'react'; + +import StillImage from 'soapbox/components/still_image'; + +import type { Account } from 'soapbox/types/entities'; + +interface IAvatar { + account?: Account | null, + size?: number, + className?: string, +} + +/** + * Legacy avatar component. + * @see soapbox/components/ui/avatar/avatar.tsx + * @deprecated + */ +const Avatar: React.FC = ({ account, size, className }) => { + if (!account) return null; + + // : TODO : remove inline and change all avatars to be sized using css + const style: React.CSSProperties = !size ? {} : { + width: `${size}px`, + height: `${size}px`, + }; + + return ( + + ); +}; + +export default Avatar; diff --git a/app/soapbox/components/still_image.js b/app/soapbox/components/still_image.js deleted file mode 100644 index 323960a37..000000000 --- a/app/soapbox/components/still_image.js +++ /dev/null @@ -1,64 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; - -import { getSettings } from 'soapbox/actions/settings'; - -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 src && !autoPlayGif && (src.endsWith('.gif') || src.startsWith('blob:')); - } - - 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/soapbox/components/still_image.tsx b/app/soapbox/components/still_image.tsx new file mode 100644 index 000000000..d2ee256a3 --- /dev/null +++ b/app/soapbox/components/still_image.tsx @@ -0,0 +1,46 @@ +import classNames from 'classnames'; +import React, { useRef } from 'react'; + +import { useSettings } from 'soapbox/hooks'; + +interface IStillImage { + /** Image alt text. */ + alt?: string, + /** Extra class names for the outer
container. */ + className?: string, + /** URL to the image */ + src: string, + /** Extra CSS styles on the outer
element. */ + style?: React.CSSProperties, +} + +/** Renders images on a canvas, only playing GIFs if autoPlayGif is enabled. */ +const StillImage: React.FC = ({ alt, className, src, style }) => { + const settings = useSettings(); + const autoPlayGif = settings.get('autoPlayGif'); + + const canvas = useRef(null); + const img = useRef(null); + + const hoverToPlay = ( + src && !autoPlayGif && (src.endsWith('.gif') || src.startsWith('blob:')) + ); + + const handleImageLoad = () => { + if (hoverToPlay && canvas.current && img.current) { + canvas.current.width = img.current.naturalWidth; + canvas.current.height = img.current.naturalHeight; + canvas.current.getContext('2d')?.drawImage(img.current, 0, 0); + } + }; + + + return ( +
+ {alt} + {hoverToPlay && } +
+ ); +}; + +export default StillImage; diff --git a/app/soapbox/components/ui/avatar/avatar.tsx b/app/soapbox/components/ui/avatar/avatar.tsx index 6818509e8..1b02c6d25 100644 --- a/app/soapbox/components/ui/avatar/avatar.tsx +++ b/app/soapbox/components/ui/avatar/avatar.tsx @@ -25,7 +25,7 @@ const Avatar = (props: IAvatar) => { return (