diff --git a/app/index.ejs b/app/index.ejs index 17cf261c3..0f3ec664c 100644 --- a/app/index.ejs +++ b/app/index.ejs @@ -9,33 +9,6 @@ - - - - - - - - - - -
diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx index 47dc5d769..cc4efe3fb 100644 --- a/app/soapbox/components/account.tsx +++ b/app/soapbox/components/account.tsx @@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'; import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; import VerificationBadge from 'soapbox/components/verification_badge'; import ActionButton from 'soapbox/features/ui/components/action_button'; -import { useAppSelector } from 'soapbox/hooks'; +import { useAppSelector, useOnScreen } from 'soapbox/hooks'; import { getAcct } from 'soapbox/utils/accounts'; import { displayFqn } from 'soapbox/utils/state'; @@ -52,8 +52,9 @@ const Account = ({ timestampUrl, withRelationship = true, }: IAccount) => { - const overflowRef = React.useRef(null); - const actionRef = React.useRef(null); + const overflowRef = React.useRef(null); + const actionRef = React.useRef(null); + const isOnScreen = useOnScreen(overflowRef); const [style, setStyle] = React.useState({ visibility: 'hidden' }); @@ -93,18 +94,19 @@ const Account = ({ }; React.useEffect(() => { - const style: React.CSSProperties = {}; + if (isOnScreen) { + const style: React.CSSProperties = {}; + const actionWidth = actionRef.current?.clientWidth; - const actionWidth = actionRef.current?.clientWidth; + if (overflowRef.current) { + style.maxWidth = overflowRef.current.clientWidth - 30 - avatarSize - actionWidth; + } else { + style.visibility = 'hidden'; + } - if (overflowRef.current) { - style.maxWidth = overflowRef.current.clientWidth - 30 - avatarSize - actionWidth; - } else { - style.visibility = 'hidden'; + setStyle(style); } - - setStyle(style); - }, [overflowRef, actionRef]); + }, [isOnScreen, overflowRef, actionRef]); if (!account) { return null; @@ -161,8 +163,8 @@ const Account = ({ - - @{username} + + @{username} {(timestamp) ? ( <> diff --git a/app/soapbox/components/avatar.js b/app/soapbox/components/avatar.js index a61917252..d5000264d 100644 --- a/app/soapbox/components/avatar.js +++ b/app/soapbox/components/avatar.js @@ -8,7 +8,7 @@ import StillImage from 'soapbox/components/still_image'; export default class Avatar extends React.PureComponent { static propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, size: PropTypes.number, style: PropTypes.object, className: PropTypes.string, diff --git a/app/soapbox/components/avatar_overlay.js b/app/soapbox/components/avatar_overlay.js index 01e35b4d1..ce9784c95 100644 --- a/app/soapbox/components/avatar_overlay.js +++ b/app/soapbox/components/avatar_overlay.js @@ -6,7 +6,7 @@ import StillImage from 'soapbox/components/still_image'; export default class AvatarOverlay extends React.PureComponent { static propTypes = { - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, friend: ImmutablePropTypes.map.isRequired, }; diff --git a/app/soapbox/components/display_name.js b/app/soapbox/components/display_name.js index 87947af46..69a1c0177 100644 --- a/app/soapbox/components/display_name.js +++ b/app/soapbox/components/display_name.js @@ -22,7 +22,7 @@ export default @connect(mapStateToProps) class DisplayName extends React.PureComponent { static propTypes = { - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, displayFqn: PropTypes.bool, others: ImmutablePropTypes.list, children: PropTypes.node, diff --git a/app/soapbox/components/dropdown_menu.js b/app/soapbox/components/dropdown_menu.js index bddb47d1a..c471ac27b 100644 --- a/app/soapbox/components/dropdown_menu.js +++ b/app/soapbox/components/dropdown_menu.js @@ -207,7 +207,7 @@ class Dropdown extends React.PureComponent { pressed: PropTypes.bool, title: PropTypes.string, disabled: PropTypes.bool, - status: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, isUserTouching: PropTypes.func, isModalOpen: PropTypes.bool.isRequired, onOpen: PropTypes.func.isRequired, diff --git a/app/soapbox/components/icon.js b/app/soapbox/components/icon.js index 3f357a1a3..7c7625806 100644 --- a/app/soapbox/components/icon.js +++ b/app/soapbox/components/icon.js @@ -17,15 +17,16 @@ export default class Icon extends React.PureComponent { id: PropTypes.string, src: PropTypes.string, className: PropTypes.string, + fixedWidth: PropTypes.bool, }; render() { - const { id, src, ...rest } = this.props; + const { id, src, fixedWidth, ...rest } = this.props; if (src) { return ; } else { - return ; + return ; } } diff --git a/app/soapbox/components/media_gallery.js b/app/soapbox/components/media_gallery.js index bb293f416..df1e4db42 100644 --- a/app/soapbox/components/media_gallery.js +++ b/app/soapbox/components/media_gallery.js @@ -45,7 +45,7 @@ const shouldLetterbox = attachment => { class Item extends React.PureComponent { static propTypes = { - attachment: ImmutablePropTypes.map.isRequired, + attachment: ImmutablePropTypes.record.isRequired, standalone: PropTypes.bool, index: PropTypes.number.isRequired, size: PropTypes.number.isRequired, diff --git a/app/soapbox/components/permalink.js b/app/soapbox/components/permalink.js deleted file mode 100644 index 5bae087ac..000000000 --- a/app/soapbox/components/permalink.js +++ /dev/null @@ -1,39 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { withRouter } from 'react-router-dom'; - -export default @withRouter -class Permalink extends React.PureComponent { - - static propTypes = { - className: PropTypes.string, - href: PropTypes.string.isRequired, - to: PropTypes.string.isRequired, - children: PropTypes.node, - onInterceptClick: PropTypes.func, - history: PropTypes.object, - }; - - handleClick = e => { - if (this.props.onInterceptClick && this.props.onInterceptClick()) { - e.preventDefault(); - return; - } - - if (this.props.history && e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - this.props.history.push(this.props.to); - } - } - - render() { - const { href, children, className, onInterceptClick, ...other } = this.props; - - return ( - - {children} - - ); - } - -} diff --git a/app/soapbox/components/permalink.tsx b/app/soapbox/components/permalink.tsx new file mode 100644 index 000000000..db68811e5 --- /dev/null +++ b/app/soapbox/components/permalink.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { useHistory } from 'react-router-dom'; + +interface IPermaLink extends Pick, 'dangerouslySetInnerHTML'> { + className?: string, + href: string, + title?: string, + to: string, +} + +const Permalink: React.FC = (props) => { + const history = useHistory(); + + const { className, href, title, to, children, ...filteredProps } = props; + + const handleClick = (event: React.MouseEvent) => { + if (event.button === 0 && !(event.ctrlKey || event.metaKey)) { + event.preventDefault(); + history.push(to); + } + }; + + return ( + + {children} + + ); +}; + +export default Permalink; diff --git a/app/soapbox/components/profile_hover_card.js b/app/soapbox/components/profile_hover_card.js index 0a7e57509..a1da1e435 100644 --- a/app/soapbox/components/profile_hover_card.js +++ b/app/soapbox/components/profile_hover_card.js @@ -134,7 +134,7 @@ export const ProfileHoverCard = ({ visible }) => { ProfileHoverCard.propTypes = { visible: PropTypes.bool, accountId: PropTypes.string, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, }; ProfileHoverCard.defaultProps = { diff --git a/app/soapbox/components/scrollable_list.js b/app/soapbox/components/scrollable_list.js index 1c96b5d57..8021acf32 100644 --- a/app/soapbox/components/scrollable_list.js +++ b/app/soapbox/components/scrollable_list.js @@ -43,7 +43,7 @@ class ScrollableList extends PureComponent { children: PropTypes.node, onScrollToTop: PropTypes.func, onScroll: PropTypes.func, - placeholderComponent: PropTypes.func, + placeholderComponent: PropTypes.object, placeholderCount: PropTypes.number, autoload: PropTypes.bool, onRefresh: PropTypes.func, diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index 261ada64e..a2c086912 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -55,8 +55,8 @@ export default @injectIntl @withRouter class Status extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map, - account: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, + account: ImmutablePropTypes.record, otherAccounts: ImmutablePropTypes.list, onClick: PropTypes.func, onReply: PropTypes.func, diff --git a/app/soapbox/components/status_action_bar.js b/app/soapbox/components/status_action_bar.js index dbdb7f8d0..00937f00d 100644 --- a/app/soapbox/components/status_action_bar.js +++ b/app/soapbox/components/status_action_bar.js @@ -69,7 +69,7 @@ const messages = defineMessages({ class StatusActionBar extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.record.isRequired, onOpenUnauthorizedModal: PropTypes.func.isRequired, onOpenReblogsModal: PropTypes.func.isRequired, onReply: PropTypes.func, diff --git a/app/soapbox/components/status_content.js b/app/soapbox/components/status_content.js index be07ed28d..2e6105a52 100644 --- a/app/soapbox/components/status_content.js +++ b/app/soapbox/components/status_content.js @@ -28,7 +28,7 @@ export default @connect(mapStateToProps) class StatusContent extends React.PureComponent { static propTypes = { - status: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.record.isRequired, reblogContent: PropTypes.string, expanded: PropTypes.bool, onExpandedToggle: PropTypes.func, diff --git a/app/soapbox/components/status_list.js b/app/soapbox/components/status_list.js index 65bbdba52..c8bcbb217 100644 --- a/app/soapbox/components/status_list.js +++ b/app/soapbox/components/status_list.js @@ -223,7 +223,7 @@ export default class StatusList extends ImmutablePureComponent { isLoading={isLoading} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} - placeholderComponent={() => } + placeholderComponent={PlaceholderStatus} placeholderCount={20} ref={this.setRef} className={divideType === 'border' ? 'divide-y divide-solid divide-gray-200' : 'sm:space-y-3 divide-y divide-solid divide-gray-200 sm:divide-none'} diff --git a/app/soapbox/components/status_reply_mentions.js b/app/soapbox/components/status_reply_mentions.js index 0e9484aa0..76a48d5df 100644 --- a/app/soapbox/components/status_reply_mentions.js +++ b/app/soapbox/components/status_reply_mentions.js @@ -23,7 +23,7 @@ export default @connect(null, mapDispatchToProps) class StatusReplyMentions extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.record.isRequired, onOpenMentionsModal: PropTypes.func, } diff --git a/app/soapbox/components/thumb_navigation.js b/app/soapbox/components/thumb_navigation.js index 17256569d..caf6566f6 100644 --- a/app/soapbox/components/thumb_navigation.js +++ b/app/soapbox/components/thumb_navigation.js @@ -33,7 +33,7 @@ class ThumbNavigation extends React.PureComponent { static propTypes = { logo: PropTypes.string, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, dashboardCount: PropTypes.number, notificationCount: PropTypes.number, chatsCount: PropTypes.number, diff --git a/app/soapbox/components/ui/hstack/hstack.tsx b/app/soapbox/components/ui/hstack/hstack.tsx index 224a322b5..d97697670 100644 --- a/app/soapbox/components/ui/hstack/hstack.tsx +++ b/app/soapbox/components/ui/hstack/hstack.tsx @@ -29,6 +29,7 @@ interface IHStack { justifyContent?: 'between' | 'center', space?: 0.5 | 1 | 1.5 | 2 | 3 | 4 | 6, grow?: boolean, + style?: React.CSSProperties } const HStack: React.FC = (props) => { diff --git a/app/soapbox/containers/soapbox.js b/app/soapbox/containers/soapbox.js index aad96badb..3571a687d 100644 --- a/app/soapbox/containers/soapbox.js +++ b/app/soapbox/containers/soapbox.js @@ -97,7 +97,7 @@ class SoapboxMount extends React.PureComponent { static propTypes = { showIntroduction: PropTypes.bool, me: SoapboxPropTypes.me, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, instanceLoaded: PropTypes.bool, reduceMotion: PropTypes.bool, underlineLinks: PropTypes.bool, diff --git a/app/soapbox/features/account/components/header.js b/app/soapbox/features/account/components/header.js index 42fcad65d..65c2160c0 100644 --- a/app/soapbox/features/account/components/header.js +++ b/app/soapbox/features/account/components/header.js @@ -86,8 +86,8 @@ export default @connect(mapStateToProps) class Header extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map, - meAccount: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, + meaccount: ImmutablePropTypes.record, identity_props: ImmutablePropTypes.list, intl: PropTypes.object.isRequired, username: PropTypes.string, diff --git a/app/soapbox/features/account_timeline/components/header.js b/app/soapbox/features/account_timeline/components/header.js index add4ff79f..f827e9314 100644 --- a/app/soapbox/features/account_timeline/components/header.js +++ b/app/soapbox/features/account_timeline/components/header.js @@ -12,7 +12,7 @@ export default @withRouter class Header extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, identity_proofs: ImmutablePropTypes.list, onFollow: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired, diff --git a/app/soapbox/features/admin/components/report_status.js b/app/soapbox/features/admin/components/report_status.js index 90c0d062f..721dfdfb2 100644 --- a/app/soapbox/features/admin/components/report_status.js +++ b/app/soapbox/features/admin/components/report_status.js @@ -22,7 +22,7 @@ export default @connect() class ReportStatus extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.record.isRequired, report: ImmutablePropTypes.map, }; diff --git a/app/soapbox/features/admin/components/unapproved_account.js b/app/soapbox/features/admin/components/unapproved_account.js index f00b0dd6a..80ca4821d 100644 --- a/app/soapbox/features/admin/components/unapproved_account.js +++ b/app/soapbox/features/admin/components/unapproved_account.js @@ -35,7 +35,7 @@ class UnapprovedAccount extends ImmutablePureComponent { static propTypes = { intl: PropTypes.object.isRequired, - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, }; handleApprove = () => { diff --git a/app/soapbox/features/admin/index.js b/app/soapbox/features/admin/index.js index db76d4407..61e6e1211 100644 --- a/app/soapbox/features/admin/index.js +++ b/app/soapbox/features/admin/index.js @@ -50,7 +50,7 @@ class Dashboard extends ImmutablePureComponent { intl: PropTypes.object.isRequired, instance: ImmutablePropTypes.map.isRequired, supportsEmailList: PropTypes.bool, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, }; handleSubscribersClick = e => { diff --git a/app/soapbox/features/aliases/components/account.js b/app/soapbox/features/aliases/components/account.js index fd0abd21b..38bce11bb 100644 --- a/app/soapbox/features/aliases/components/account.js +++ b/app/soapbox/features/aliases/components/account.js @@ -49,7 +49,7 @@ export default @connect(makeMapStateToProps, mapDispatchToProps) class Account extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, apId: PropTypes.string.isRequired, intl: PropTypes.object.isRequired, onAdd: PropTypes.func.isRequired, diff --git a/app/soapbox/features/birthdays/account.js b/app/soapbox/features/birthdays/account.js index e79273f89..b29d79589 100644 --- a/app/soapbox/features/birthdays/account.js +++ b/app/soapbox/features/birthdays/account.js @@ -36,7 +36,7 @@ class Account extends ImmutablePureComponent { static propTypes = { accountId: PropTypes.string.isRequired, intl: PropTypes.object.isRequired, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, }; static defaultProps = { diff --git a/app/soapbox/features/compose/components/privacy_dropdown.js b/app/soapbox/features/compose/components/privacy_dropdown.js index 415789ba5..23111ab1b 100644 --- a/app/soapbox/features/compose/components/privacy_dropdown.js +++ b/app/soapbox/features/compose/components/privacy_dropdown.js @@ -256,8 +256,6 @@ class PrivacyDropdown extends React.PureComponent { className='text-gray-400 hover:text-gray-600' src={valueOption.icon} title={intl.formatMessage(messages.change_privacy)} - expanded={open} - active={open} onClick={this.handleToggle} onMouseDown={this.handleMouseDown} onKeyDown={this.handleButtonKeyDown} diff --git a/app/soapbox/features/compose/components/reply_indicator.js b/app/soapbox/features/compose/components/reply_indicator.js index b3e8109be..24deb8906 100644 --- a/app/soapbox/features/compose/components/reply_indicator.js +++ b/app/soapbox/features/compose/components/reply_indicator.js @@ -12,7 +12,7 @@ import { isRtl } from '../../../rtl'; export default class ReplyIndicator extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, onCancel: PropTypes.func.isRequired, hideActions: PropTypes.bool, }; diff --git a/app/soapbox/features/developers/apps/create.js b/app/soapbox/features/developers/apps/create.js index a06aa9811..de237d451 100644 --- a/app/soapbox/features/developers/apps/create.js +++ b/app/soapbox/features/developers/apps/create.js @@ -40,7 +40,7 @@ class CreateApp extends ImmutablePureComponent { static propTypes = { intl: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, defaultScopes: PropTypes.string, } diff --git a/app/soapbox/features/directory/components/account_card.js b/app/soapbox/features/directory/components/account_card.js index 36b53d416..cc64dbad1 100644 --- a/app/soapbox/features/directory/components/account_card.js +++ b/app/soapbox/features/directory/components/account_card.js @@ -34,7 +34,7 @@ class AccountCard extends ImmutablePureComponent { static propTypes = { me: SoapboxPropTypes.me, - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, autoPlayGif: PropTypes.bool, }; diff --git a/app/soapbox/features/edit_profile/components/profile_preview.js b/app/soapbox/features/edit_profile/components/profile_preview.js index ad1db11ed..7fe1e8e4c 100644 --- a/app/soapbox/features/edit_profile/components/profile_preview.js +++ b/app/soapbox/features/edit_profile/components/profile_preview.js @@ -39,7 +39,7 @@ const ProfilePreview = ({ account, displayFqn }) => ( ); ProfilePreview.propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, displayFqn: PropTypes.bool, }; diff --git a/app/soapbox/features/edit_profile/index.js b/app/soapbox/features/edit_profile/index.js index 008711462..a656d6e0b 100644 --- a/app/soapbox/features/edit_profile/index.js +++ b/app/soapbox/features/edit_profile/index.js @@ -88,7 +88,7 @@ class EditProfile extends ImmutablePureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, maxFields: PropTypes.number, verifiedCanEditName: PropTypes.bool, }; diff --git a/app/soapbox/features/follow_recommendations/components/account.js b/app/soapbox/features/follow_recommendations/components/account.js index bca3c8400..c15fc6bcc 100644 --- a/app/soapbox/features/follow_recommendations/components/account.js +++ b/app/soapbox/features/follow_recommendations/components/account.js @@ -30,7 +30,7 @@ export default @connect(makeMapStateToProps) class Account extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, intl: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, }; diff --git a/app/soapbox/features/follow_requests/components/account_authorize.js b/app/soapbox/features/follow_requests/components/account_authorize.js index 8dd6a9252..56aac382a 100644 --- a/app/soapbox/features/follow_requests/components/account_authorize.js +++ b/app/soapbox/features/follow_requests/components/account_authorize.js @@ -18,7 +18,7 @@ export default @injectIntl class AccountAuthorize extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, onAuthorize: PropTypes.func.isRequired, onReject: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, diff --git a/app/soapbox/features/groups/timeline/index.js b/app/soapbox/features/groups/timeline/index.js index 529c971ff..0e60bd886 100644 --- a/app/soapbox/features/groups/timeline/index.js +++ b/app/soapbox/features/groups/timeline/index.js @@ -35,7 +35,7 @@ class GroupTimeline extends React.PureComponent { hasUnread: PropTypes.bool, group: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]), relationships: ImmutablePropTypes.map, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, intl: PropTypes.object.isRequired, }; diff --git a/app/soapbox/features/list_adder/components/account.js b/app/soapbox/features/list_adder/components/account.js index bbf792d17..014f3ac22 100644 --- a/app/soapbox/features/list_adder/components/account.js +++ b/app/soapbox/features/list_adder/components/account.js @@ -24,7 +24,7 @@ export default @connect(makeMapStateToProps) class Account extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, }; render() { diff --git a/app/soapbox/features/list_adder/index.js b/app/soapbox/features/list_adder/index.js index 3283ac04f..7df28d07f 100644 --- a/app/soapbox/features/list_adder/index.js +++ b/app/soapbox/features/list_adder/index.js @@ -51,7 +51,7 @@ class ListAdder extends ImmutablePureComponent { onInitialize: PropTypes.func.isRequired, onReset: PropTypes.func.isRequired, listIds: ImmutablePropTypes.list.isRequired, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, }; componentDidMount() { diff --git a/app/soapbox/features/list_editor/components/account.js b/app/soapbox/features/list_editor/components/account.js index eee56b97a..8054bb52f 100644 --- a/app/soapbox/features/list_editor/components/account.js +++ b/app/soapbox/features/list_editor/components/account.js @@ -37,7 +37,7 @@ export default @connect(makeMapStateToProps, mapDispatchToProps) class Account extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, intl: PropTypes.object.isRequired, onRemove: PropTypes.func.isRequired, onAdd: PropTypes.func.isRequired, diff --git a/app/soapbox/features/notifications/components/follow_request.js b/app/soapbox/features/notifications/components/follow_request.js index 2a2e79269..2bf25131b 100644 --- a/app/soapbox/features/notifications/components/follow_request.js +++ b/app/soapbox/features/notifications/components/follow_request.js @@ -18,7 +18,7 @@ export default @injectIntl class FollowRequest extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, onAuthorize: PropTypes.func.isRequired, onReject: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, diff --git a/app/soapbox/features/notifications/components/notification.js b/app/soapbox/features/notifications/components/notification.js index 3c5462ce3..7ff10cc2c 100644 --- a/app/soapbox/features/notifications/components/notification.js +++ b/app/soapbox/features/notifications/components/notification.js @@ -228,7 +228,7 @@ const Notification = (props) => { Notification.propTypes = { hidden: PropTypes.bool, - notification: ImmutablePropTypes.map.isRequired, + notification: ImmutablePropTypes.record.isRequired, onMoveUp: PropTypes.func.isRequired, onMoveDown: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, diff --git a/app/soapbox/features/report/components/status_check_box.js b/app/soapbox/features/report/components/status_check_box.js index fcb73c902..b74253f96 100644 --- a/app/soapbox/features/report/components/status_check_box.js +++ b/app/soapbox/features/report/components/status_check_box.js @@ -11,7 +11,7 @@ import { MediaGallery, Video, Audio } from '../../ui/util/async-components'; export default class StatusCheckBox extends React.PureComponent { static propTypes = { - status: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.record.isRequired, checked: PropTypes.bool, onToggle: PropTypes.func.isRequired, disabled: PropTypes.bool, diff --git a/app/soapbox/features/scheduled_statuses/components/scheduled_status_action_bar.js b/app/soapbox/features/scheduled_statuses/components/scheduled_status_action_bar.js index 0e1773901..7ed5c4d08 100644 --- a/app/soapbox/features/scheduled_statuses/components/scheduled_status_action_bar.js +++ b/app/soapbox/features/scheduled_statuses/components/scheduled_status_action_bar.js @@ -48,7 +48,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ class ScheduledStatusActionBar extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.record.isRequired, intl: PropTypes.object.isRequired, me: SoapboxPropTypes.me, onCancelClick: PropTypes.func.isRequired, diff --git a/app/soapbox/features/status/components/action_bar.js b/app/soapbox/features/status/components/action_bar.js index 317cdfe6a..b3af67ca2 100644 --- a/app/soapbox/features/status/components/action_bar.js +++ b/app/soapbox/features/status/components/action_bar.js @@ -84,7 +84,7 @@ const mapDispatchToProps = (dispatch, { status }) => ({ class ActionBar extends React.PureComponent { static propTypes = { - status: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.record.isRequired, onReply: PropTypes.func.isRequired, onReblog: PropTypes.func.isRequired, onQuote: PropTypes.func.isRequired, diff --git a/app/soapbox/features/status/components/card.js b/app/soapbox/features/status/components/card.js index c9f1ec309..ac933ff42 100644 --- a/app/soapbox/features/status/components/card.js +++ b/app/soapbox/features/status/components/card.js @@ -61,7 +61,7 @@ const addAutoPlay = html => { export default class Card extends React.PureComponent { static propTypes = { - card: ImmutablePropTypes.map, + card: ImmutablePropTypes.record, maxDescription: PropTypes.number, onOpenMedia: PropTypes.func.isRequired, compact: PropTypes.bool, diff --git a/app/soapbox/features/status/components/detailed_status.js b/app/soapbox/features/status/components/detailed_status.js index 8d45af06b..0ae645129 100644 --- a/app/soapbox/features/status/components/detailed_status.js +++ b/app/soapbox/features/status/components/detailed_status.js @@ -27,7 +27,7 @@ export default @injectIntl class DetailedStatus extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, onOpenMedia: PropTypes.func.isRequired, onOpenVideo: PropTypes.func.isRequired, onToggleHidden: PropTypes.func.isRequired, diff --git a/app/soapbox/features/status/components/quoted_status.js b/app/soapbox/features/status/components/quoted_status.js index c3af169ce..c31415a02 100644 --- a/app/soapbox/features/status/components/quoted_status.js +++ b/app/soapbox/features/status/components/quoted_status.js @@ -20,7 +20,7 @@ export default @injectIntl @withRouter class QuotedStatus extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, onCancel: PropTypes.func, intl: PropTypes.object.isRequired, compose: PropTypes.bool, diff --git a/app/soapbox/features/status/components/status_interaction_bar.js b/app/soapbox/features/status/components/status_interaction_bar.js index 6c69699c0..e1f931865 100644 --- a/app/soapbox/features/status/components/status_interaction_bar.js +++ b/app/soapbox/features/status/components/status_interaction_bar.js @@ -55,7 +55,7 @@ export default @connect(mapStateToProps, mapDispatchToProps) class StatusInteractionBar extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, me: SoapboxPropTypes.me, allowedEmoji: ImmutablePropTypes.list, features: PropTypes.object.isRequired, diff --git a/app/soapbox/features/status/index.js b/app/soapbox/features/status/index.js index fc0359d69..cf8fce97b 100644 --- a/app/soapbox/features/status/index.js +++ b/app/soapbox/features/status/index.js @@ -165,7 +165,7 @@ class Status extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - status: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, ancestorsIds: ImmutablePropTypes.orderedSet, descendantsIds: ImmutablePropTypes.orderedSet, intl: PropTypes.object.isRequired, diff --git a/app/soapbox/features/ui/components/action_button.js b/app/soapbox/features/ui/components/action_button.js index 2ccdb8309..926134c39 100644 --- a/app/soapbox/features/ui/components/action_button.js +++ b/app/soapbox/features/ui/components/action_button.js @@ -68,7 +68,7 @@ export default @connect(mapStateToProps, mapDispatchToProps) class ActionButton extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, onFollow: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired, onOpenUnauthorizedModal: PropTypes.func.isRequired, diff --git a/app/soapbox/features/ui/components/actions_modal.js b/app/soapbox/features/ui/components/actions_modal.js index 3429c166e..bcac0c67c 100644 --- a/app/soapbox/features/ui/components/actions_modal.js +++ b/app/soapbox/features/ui/components/actions_modal.js @@ -74,7 +74,7 @@ const ActionsModal = ({ status, actions, onClick, onClose }) => { }; ActionsModal.propTypes = { - status: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, actions: PropTypes.array, onClick: PropTypes.func, onClose: PropTypes.func.isRequired, diff --git a/app/soapbox/features/ui/components/boost_modal.js b/app/soapbox/features/ui/components/boost_modal.js index 49763625f..8a3862b35 100644 --- a/app/soapbox/features/ui/components/boost_modal.js +++ b/app/soapbox/features/ui/components/boost_modal.js @@ -18,7 +18,7 @@ export default @injectIntl @withRouter class BoostModal extends ImmutablePureComponent { static propTypes = { - status: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.record.isRequired, onReblog: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, diff --git a/app/soapbox/features/ui/components/compose_modal.js b/app/soapbox/features/ui/components/compose_modal.js index 7d6bf119b..ff9ce136e 100644 --- a/app/soapbox/features/ui/components/compose_modal.js +++ b/app/soapbox/features/ui/components/compose_modal.js @@ -29,7 +29,7 @@ const mapStateToProps = state => { class ComposeModal extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, intl: PropTypes.object.isRequired, onClose: PropTypes.func.isRequired, composeText: PropTypes.string, diff --git a/app/soapbox/features/ui/components/link_footer.js b/app/soapbox/features/ui/components/link_footer.js index 7861242b0..a049a6a18 100644 --- a/app/soapbox/features/ui/components/link_footer.js +++ b/app/soapbox/features/ui/components/link_footer.js @@ -6,10 +6,11 @@ import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; import { logOut } from 'soapbox/actions/auth'; +import { getSoapboxConfig } from 'soapbox/actions/soapbox'; import { Text } from 'soapbox/components/ui'; import emojify from 'soapbox/features/emoji/emoji'; import { getBaseURL } from 'soapbox/utils/accounts'; -// import sourceCode from 'soapbox/utils/code'; +import sourceCode from 'soapbox/utils/code'; import { getFeatures } from 'soapbox/utils/features'; import { openModal } from '../../../actions/modals'; @@ -19,9 +20,11 @@ const mapStateToProps = state => { const account = state.getIn(['accounts', me]); const instance = state.get('instance'); const features = getFeatures(instance); + const soapboxConfig = getSoapboxConfig(state); return { account, + soapboxConfig, profileDirectory: features.profileDirectory, federating: features.federating, showAliases: features.accountAliasesAPI, @@ -41,7 +44,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ }, }); -const LinkFooter = ({ onOpenHotkeys, account, profileDirectory, federating, showAliases, importAPI, onClickLogOut, baseURL }) => ( +const LinkFooter = ({ onOpenHotkeys, account, profileDirectory, federating, showAliases, importAPI, onClickLogOut, baseURL, soapboxConfig }) => (
    {account && <> @@ -68,26 +71,29 @@ const LinkFooter = ({ onOpenHotkeys, account, profileDirectory, federating, show
- {/*{sourceCode.repository}, - code_version: sourceCode.version, - }} - />*/} - }} - /> + {soapboxConfig.get('linkFooterMessage') ? ( + + ) : ( + {sourceCode.repository}, + code_version: sourceCode.version, + }} + /> + )}
); LinkFooter.propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, + soapboxConfig: ImmutablePropTypes.map, profileDirectory: PropTypes.bool, federating: PropTypes.bool, showAliases: PropTypes.bool, diff --git a/app/soapbox/features/ui/components/media_modal.js b/app/soapbox/features/ui/components/media_modal.js index 8e8053549..31571cde3 100644 --- a/app/soapbox/features/ui/components/media_modal.js +++ b/app/soapbox/features/ui/components/media_modal.js @@ -26,8 +26,8 @@ class MediaModal extends ImmutablePureComponent { static propTypes = { media: ImmutablePropTypes.list.isRequired, - status: ImmutablePropTypes.map, - account: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, + account: ImmutablePropTypes.record, index: PropTypes.number.isRequired, onClose: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, diff --git a/app/soapbox/features/ui/components/profile-dropdown.tsx b/app/soapbox/features/ui/components/profile-dropdown.tsx index 43f31cfa3..7ff98ea1a 100644 --- a/app/soapbox/features/ui/components/profile-dropdown.tsx +++ b/app/soapbox/features/ui/components/profile-dropdown.tsx @@ -116,7 +116,7 @@ const ProfileDropdown: React.FC = ({ account, children }) => { const itemProps = menuItem.action ? { onSelect: menuItem.action } : { to: menuItem.to, as: Link }; return ( - + {menuItem.text} ); diff --git a/app/soapbox/features/ui/components/profile_info_panel.js b/app/soapbox/features/ui/components/profile_info_panel.js index 8a19dd83f..41fef805e 100644 --- a/app/soapbox/features/ui/components/profile_info_panel.js +++ b/app/soapbox/features/ui/components/profile_info_panel.js @@ -37,7 +37,7 @@ const messages = defineMessages({ class ProfileInfoPanel extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, identity_proofs: ImmutablePropTypes.list, intl: PropTypes.object.isRequired, username: PropTypes.string, diff --git a/app/soapbox/features/ui/components/profile_media_panel.js b/app/soapbox/features/ui/components/profile_media_panel.js index dfdc70fff..54482ad79 100644 --- a/app/soapbox/features/ui/components/profile_media_panel.js +++ b/app/soapbox/features/ui/components/profile_media_panel.js @@ -16,7 +16,7 @@ import MediaItem from '../../account_gallery/components/media_item'; class ProfileMediaPanel extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, attachments: ImmutablePropTypes.list, dispatch: PropTypes.func.isRequired, }; diff --git a/app/soapbox/features/ui/components/profile_stats.js b/app/soapbox/features/ui/components/profile_stats.js index 1a43e858b..325a9c72f 100644 --- a/app/soapbox/features/ui/components/profile_stats.js +++ b/app/soapbox/features/ui/components/profile_stats.js @@ -18,7 +18,7 @@ class ProfileStats extends React.PureComponent { static propTypes = { intl: PropTypes.object.isRequired, - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, className: PropTypes.string, onClickHandler: PropTypes.func, } diff --git a/app/soapbox/features/ui/components/report_modal.js b/app/soapbox/features/ui/components/report_modal.js index c9ecf622a..4dc572d65 100644 --- a/app/soapbox/features/ui/components/report_modal.js +++ b/app/soapbox/features/ui/components/report_modal.js @@ -53,7 +53,7 @@ class ReportModal extends ImmutablePureComponent { static propTypes = { isSubmitting: PropTypes.bool, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, statusIds: ImmutablePropTypes.orderedSet.isRequired, comment: PropTypes.string.isRequired, forward: PropTypes.bool, diff --git a/app/soapbox/features/ui/components/subscription_button.js b/app/soapbox/features/ui/components/subscription_button.js index f8fec8436..1de1a6f88 100644 --- a/app/soapbox/features/ui/components/subscription_button.js +++ b/app/soapbox/features/ui/components/subscription_button.js @@ -49,7 +49,7 @@ export default @connect(mapStateToProps, mapDispatchToProps) class SubscriptionButton extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, features: PropTypes.object.isRequired, }; diff --git a/app/soapbox/features/ui/components/user_panel.js b/app/soapbox/features/ui/components/user_panel.js index 0c849c151..e77268614 100644 --- a/app/soapbox/features/ui/components/user_panel.js +++ b/app/soapbox/features/ui/components/user_panel.js @@ -19,7 +19,7 @@ import { makeGetAccount } from '../../../selectors'; class UserPanel extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, displayFqn: PropTypes.bool, intl: PropTypes.object.isRequired, domain: PropTypes.string, diff --git a/app/soapbox/features/ui/components/video_modal.js b/app/soapbox/features/ui/components/video_modal.js index a42cc76d2..066aa9d79 100644 --- a/app/soapbox/features/ui/components/video_modal.js +++ b/app/soapbox/features/ui/components/video_modal.js @@ -12,8 +12,8 @@ class VideoModal extends ImmutablePureComponent { static propTypes = { media: ImmutablePropTypes.map.isRequired, - status: ImmutablePropTypes.map, - account: ImmutablePropTypes.map, + status: ImmutablePropTypes.record, + account: ImmutablePropTypes.record, time: PropTypes.number, onClose: PropTypes.func.isRequired, history: PropTypes.object, diff --git a/app/soapbox/features/ui/components/welcome_button.js b/app/soapbox/features/ui/components/welcome_button.js index f4f352f2f..c9c8fe386 100644 --- a/app/soapbox/features/ui/components/welcome_button.js +++ b/app/soapbox/features/ui/components/welcome_button.js @@ -40,7 +40,7 @@ class WelcomeButton extends ImmutablePureComponent { static propTypes = { intl: PropTypes.object.isRequired, - account: ImmutablePropTypes.map.isRequired, + account: ImmutablePropTypes.record.isRequired, onClick: PropTypes.func.isRequired, }; diff --git a/app/soapbox/features/ui/util/react_router_helpers.js b/app/soapbox/features/ui/util/react_router_helpers.js index 4942787b8..295e20019 100644 --- a/app/soapbox/features/ui/util/react_router_helpers.js +++ b/app/soapbox/features/ui/util/react_router_helpers.js @@ -30,7 +30,7 @@ class WrappedRoute extends React.Component { content: PropTypes.node, componentParams: PropTypes.object, layout: PropTypes.object, - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, settings: ImmutablePropTypes.map.isRequired, publicRoute: PropTypes.bool, staffOnly: PropTypes.bool, diff --git a/app/soapbox/hooks/index.ts b/app/soapbox/hooks/index.ts index 88f47b434..9fe270dd4 100644 --- a/app/soapbox/hooks/index.ts +++ b/app/soapbox/hooks/index.ts @@ -1 +1,2 @@ export { useAppSelector } from './useAppSelector'; +export { useOnScreen } from './useOnScreen'; diff --git a/app/soapbox/hooks/useOnScreen.ts b/app/soapbox/hooks/useOnScreen.ts new file mode 100644 index 000000000..1ae207326 --- /dev/null +++ b/app/soapbox/hooks/useOnScreen.ts @@ -0,0 +1,19 @@ +import * as React from 'react'; + +export const useOnScreen = (ref: React.MutableRefObject) => { + const [isIntersecting, setIntersecting] = React.useState(false); + + const observer = new IntersectionObserver( + ([entry]) => setIntersecting(entry.isIntersecting), + ); + + React.useEffect(() => { + observer.observe(ref.current); + + return () => { + observer.disconnect(); + }; + }, []); + + return isIntersecting; +}; diff --git a/app/soapbox/pages/groups_page.js b/app/soapbox/pages/groups_page.js index 76a86c714..e11096abf 100644 --- a/app/soapbox/pages/groups_page.js +++ b/app/soapbox/pages/groups_page.js @@ -20,7 +20,7 @@ export default @connect(mapStateToProps) class GroupsPage extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, }; render() { diff --git a/app/soapbox/pages/profile_page.js b/app/soapbox/pages/profile_page.js index 58c2d53bc..05960b6fe 100644 --- a/app/soapbox/pages/profile_page.js +++ b/app/soapbox/pages/profile_page.js @@ -68,7 +68,7 @@ export default @connect(mapStateToProps) class ProfilePage extends ImmutablePureComponent { static propTypes = { - account: ImmutablePropTypes.map, + account: ImmutablePropTypes.record, accountUsername: PropTypes.string.isRequired, displayFqn: PropTypes.bool, features: PropTypes.object, diff --git a/app/styles/components/snackbar.scss b/app/styles/components/snackbar.scss index da22a3ff3..7a397ef1e 100644 --- a/app/styles/components/snackbar.scss +++ b/app/styles/components/snackbar.scss @@ -40,7 +40,7 @@ } .notification-bar-wrapper { - @apply p-4 flex items-start; + @apply p-4 flex items-center justify-between w-full space-x-2; } .notification-bar-title { @@ -50,3 +50,10 @@ .notification-bar-message { @apply text-sm text-gray-700; } + +.notification-bar-action a { + @apply inline-flex items-center px-2.5 py-1 border border-solid border-gray-300 + shadow-sm text-xs font-medium rounded-full text-gray-700 bg-white + hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 + focus:ring-primary-500; +}