From ca5955ed76ebec90fa87983017904a7b2b0c56a3 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Sun, 19 May 2024 19:07:32 +0200 Subject: [PATCH] [Glitch] Use a modern React context for identity in the app Port a178ba7cd5fa10b018ecaf3c8e3dd5f298a08818 to glitch-soc Signed-off-by: Claire --- .../glitch/components/column_header.jsx | 12 ++- .../flavours/glitch/components/poll.jsx | 11 +-- .../glitch/components/status_action_bar.jsx | 17 ++--- .../glitch/components/status_content.jsx | 11 +-- .../flavours/glitch/containers/mastodon.jsx | 52 ++++--------- .../features/account/components/header.jsx | 10 +-- .../features/community_timeline/index.jsx | 13 ++-- .../features/compose/components/search.jsx | 13 ++-- .../glitch/features/explore/index.jsx | 11 +-- .../glitch/features/firehose/index.jsx | 12 +-- .../glitch/features/getting_started/index.jsx | 13 ++-- .../features/getting_started_misc/index.jsx | 11 +-- .../features/hashtag_timeline/index.jsx | 15 ++-- .../glitch/features/home_timeline/index.jsx | 11 +-- .../components/column_settings.jsx | 15 ++-- .../glitch/features/notifications/index.jsx | 11 +-- .../picture_in_picture/components/footer.jsx | 15 ++-- .../glitch/features/public_timeline/index.jsx | 13 ++-- .../features/status/components/action_bar.jsx | 11 +-- .../flavours/glitch/features/status/index.jsx | 15 ++-- .../features/ui/components/compose_panel.jsx | 11 +-- .../glitch/features/ui/components/header.jsx | 11 +-- .../features/ui/components/link_footer.jsx | 11 +-- .../ui/components/navigation_panel.jsx | 11 +-- .../flavours/glitch/features/ui/index.jsx | 25 +++---- .../flavours/glitch/identity_context.tsx | 74 +++++++++++++++++++ .../flavours/glitch/initial_state.js | 10 +++ 27 files changed, 215 insertions(+), 230 deletions(-) create mode 100644 app/javascript/flavours/glitch/identity_context.tsx diff --git a/app/javascript/flavours/glitch/components/column_header.jsx b/app/javascript/flavours/glitch/components/column_header.jsx index 0177812480..210ec396fa 100644 --- a/app/javascript/flavours/glitch/components/column_header.jsx +++ b/app/javascript/flavours/glitch/components/column_header.jsx @@ -14,8 +14,10 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import SettingsIcon from '@/material-icons/400-24px/settings.svg?react'; import { Icon } from 'flavours/glitch/components/icon'; import { ButtonInTabsBar } from 'flavours/glitch/features/ui/util/columns_context'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; + import { useAppHistory } from './router'; const messages = defineMessages({ @@ -51,12 +53,8 @@ BackButton.propTypes = { }; class ColumnHeader extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, intl: PropTypes.object.isRequired, title: PropTypes.node, icon: PropTypes.string, @@ -171,7 +169,7 @@ class ColumnHeader extends PureComponent { ); } - if (this.context.identity.signedIn && (children || (multiColumn && this.props.onPin))) { + if (this.props.identity.signedIn && (children || (multiColumn && this.props.onPin))) { collapseButton = ( } + {!showResults && } {!showResults && <> · } {showResults && !this.props.disabled && <> · } {votesCount} @@ -247,4 +244,4 @@ class Poll extends ImmutablePureComponent { } -export default injectIntl(Poll); +export default injectIntl(withIdentity(Poll)); diff --git a/app/javascript/flavours/glitch/components/status_action_bar.jsx b/app/javascript/flavours/glitch/components/status_action_bar.jsx index f27719a6b9..3dfe403987 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.jsx +++ b/app/javascript/flavours/glitch/components/status_action_bar.jsx @@ -21,6 +21,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react'; import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg'; import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg'; import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; @@ -67,12 +68,8 @@ const messages = defineMessages({ }); class StatusActionBar extends ImmutablePureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, status: ImmutablePropTypes.map.isRequired, onReply: PropTypes.func, onFavourite: PropTypes.func, @@ -108,7 +105,7 @@ class StatusActionBar extends ImmutablePureComponent { ]; handleReplyClick = () => { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (signedIn) { this.props.onReply(this.props.status, this.props.history); @@ -124,7 +121,7 @@ class StatusActionBar extends ImmutablePureComponent { }; handleFavouriteClick = (e) => { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (signedIn) { this.props.onFavourite(this.props.status, e); @@ -134,7 +131,7 @@ class StatusActionBar extends ImmutablePureComponent { }; handleReblogClick = e => { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (signedIn) { this.props.onReblog(this.props.status, e); @@ -210,7 +207,7 @@ class StatusActionBar extends ImmutablePureComponent { render () { const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props; - const { permissions, signedIn } = this.context.identity; + const { permissions, signedIn } = this.props.identity; const mutingConversation = status.get('muted'); const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); @@ -359,4 +356,4 @@ class StatusActionBar extends ImmutablePureComponent { } -export default withRouter(injectIntl(StatusActionBar)); +export default withRouter(withIdentity(injectIntl(StatusActionBar))); diff --git a/app/javascript/flavours/glitch/components/status_content.jsx b/app/javascript/flavours/glitch/components/status_content.jsx index f5d5ccc70a..24da69cdf2 100644 --- a/app/javascript/flavours/glitch/components/status_content.jsx +++ b/app/javascript/flavours/glitch/components/status_content.jsx @@ -15,6 +15,7 @@ import LinkIcon from '@/material-icons/400-24px/link.svg?react'; import MovieIcon from '@/material-icons/400-24px/movie.svg?react'; import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react'; import { Icon } from 'flavours/glitch/components/icon'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state'; import { decode as decodeIDNA } from 'flavours/glitch/utils/idna'; @@ -126,12 +127,8 @@ const mapStateToProps = state => ({ }); class StatusContent extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, status: ImmutablePropTypes.map.isRequired, statusContent: PropTypes.string, expanded: PropTypes.bool, @@ -349,7 +346,7 @@ class StatusContent extends PureComponent { const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; const contentLocale = intl.locale.replace(/[_-].*/, ''); const targetLanguages = this.props.languages?.get(status.get('language') || 'und'); - const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale); + const renderTranslate = this.props.onTranslate && this.props.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale); const content = { __html: statusContent ?? getStatusContent(status) }; const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') }; @@ -503,4 +500,4 @@ class StatusContent extends PureComponent { } -export default withRouter(connect(mapStateToProps)(injectIntl(StatusContent))); +export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusContent)))); diff --git a/app/javascript/flavours/glitch/containers/mastodon.jsx b/app/javascript/flavours/glitch/containers/mastodon.jsx index 1704336f2c..c0f63b95b4 100644 --- a/app/javascript/flavours/glitch/containers/mastodon.jsx +++ b/app/javascript/flavours/glitch/containers/mastodon.jsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import { PureComponent } from 'react'; import { Helmet } from 'react-helmet'; @@ -15,6 +14,7 @@ import { connectUserStream } from 'flavours/glitch/actions/streaming'; import ErrorBoundary from 'flavours/glitch/components/error_boundary'; import { Router } from 'flavours/glitch/components/router'; import UI from 'flavours/glitch/features/ui'; +import { IdentityContext, createIdentityContext } from 'flavours/glitch/identity_context'; import initialState, { title as siteTitle } from 'flavours/glitch/initial_state'; import { IntlProvider } from 'flavours/glitch/locales'; import { store } from 'flavours/glitch/store'; @@ -33,33 +33,9 @@ if (initialState.meta.me) { store.dispatch(fetchCustomEmojis()); } -const createIdentityContext = state => ({ - signedIn: !!state.meta.me, - accountId: state.meta.me, - disabledAccountId: state.meta.disabled_account_id, - accessToken: state.meta.access_token, - permissions: state.role ? state.role.permissions : 0, -}); - export default class Mastodon extends PureComponent { - - static childContextTypes = { - identity: PropTypes.shape({ - signedIn: PropTypes.bool.isRequired, - accountId: PropTypes.string, - disabledAccountId: PropTypes.string, - accessToken: PropTypes.string, - }).isRequired, - }; - identity = createIdentityContext(initialState); - getChildContext() { - return { - identity: this.identity, - }; - } - componentDidMount() { if (this.identity.signedIn) { this.disconnect = store.dispatch(connectUserStream()); @@ -79,19 +55,21 @@ export default class Mastodon extends PureComponent { render () { return ( - - - - - - - - + + + + + + + + + - - - - + + + + + ); } diff --git a/app/javascript/flavours/glitch/features/account/components/header.jsx b/app/javascript/flavours/glitch/features/account/components/header.jsx index e311936af9..62c8c84c66 100644 --- a/app/javascript/flavours/glitch/features/account/components/header.jsx +++ b/app/javascript/flavours/glitch/features/account/components/header.jsx @@ -23,6 +23,7 @@ import { Icon } from 'flavours/glitch/components/icon'; import { IconButton } from 'flavours/glitch/components/icon_button'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { autoPlayGif, me, domain as localDomain } from 'flavours/glitch/initial_state'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; import { preferencesLink, profileLink, accountAdminLink } from 'flavours/glitch/utils/backend_links'; @@ -94,6 +95,7 @@ const dateFormatOptions = { class Header extends ImmutablePureComponent { static propTypes = { + identity: identityContextPropShape, account: ImmutablePropTypes.record, identity_props: ImmutablePropTypes.list, onFollow: PropTypes.func.isRequired, @@ -117,10 +119,6 @@ class Header extends ImmutablePureComponent { ...WithRouterPropTypes, }; - static contextTypes = { - identity: PropTypes.object, - }; - openEditProfile = () => { window.open(profileLink, '_blank'); }; @@ -170,7 +168,7 @@ class Header extends ImmutablePureComponent { render () { const { account, hidden, intl } = this.props; - const { signedIn, permissions } = this.context.identity; + const { signedIn, permissions } = this.props.identity; if (!account) { return null; @@ -414,4 +412,4 @@ class Header extends ImmutablePureComponent { } -export default withRouter(injectIntl(Header)); +export default withRouter(withIdentity(injectIntl(Header))); diff --git a/app/javascript/flavours/glitch/features/community_timeline/index.jsx b/app/javascript/flavours/glitch/features/community_timeline/index.jsx index 7be2196511..9f306923e3 100644 --- a/app/javascript/flavours/glitch/features/community_timeline/index.jsx +++ b/app/javascript/flavours/glitch/features/community_timeline/index.jsx @@ -9,6 +9,7 @@ import { connect } from 'react-redux'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { domain } from 'flavours/glitch/initial_state'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; @@ -40,16 +41,12 @@ const mapStateToProps = (state, { columnId }) => { }; class CommunityTimeline extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static defaultProps = { onlyMedia: false, }; static propTypes = { + identity: identityContextPropShape, dispatch: PropTypes.func.isRequired, columnId: PropTypes.string, intl: PropTypes.object.isRequired, @@ -80,7 +77,7 @@ class CommunityTimeline extends PureComponent { componentDidMount () { const { dispatch, onlyMedia } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; dispatch(expandCommunityTimeline({ onlyMedia })); @@ -90,7 +87,7 @@ class CommunityTimeline extends PureComponent { } componentDidUpdate (prevProps) { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (prevProps.onlyMedia !== this.props.onlyMedia) { const { dispatch, onlyMedia } = this.props; @@ -165,4 +162,4 @@ class CommunityTimeline extends PureComponent { } -export default connect(mapStateToProps)(injectIntl(CommunityTimeline)); +export default withIdentity(connect(mapStateToProps)(injectIntl(CommunityTimeline))); diff --git a/app/javascript/flavours/glitch/features/compose/components/search.jsx b/app/javascript/flavours/glitch/features/compose/components/search.jsx index 4b64038f57..f5a51334af 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/search.jsx @@ -12,6 +12,7 @@ import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import SearchIcon from '@/material-icons/400-24px/search.svg?react'; import { Icon } from 'flavours/glitch/components/icon'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { domain, searchEnabled } from 'flavours/glitch/initial_state'; import { HASHTAG_REGEX } from 'flavours/glitch/utils/hashtags'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; @@ -33,12 +34,8 @@ const labelForRecentSearch = search => { }; class Search extends PureComponent { - - static contextTypes = { - identity: PropTypes.object.isRequired, - }; - static propTypes = { + identity: identityContextPropShape, value: PropTypes.string.isRequired, recent: ImmutablePropTypes.orderedSet, submitted: PropTypes.bool, @@ -276,7 +273,7 @@ class Search extends PureComponent { } _calculateOptions (value) { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; const trimmedValue = value.trim(); const options = []; @@ -318,7 +315,7 @@ class Search extends PureComponent { render () { const { intl, value, submitted, recent } = this.props; const { expanded, options, selectedOption } = this.state; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; const hasValue = value.length > 0 || submitted; @@ -402,4 +399,4 @@ class Search extends PureComponent { } -export default withRouter(injectIntl(Search)); +export default withRouter(withIdentity(injectIntl(Search))); diff --git a/app/javascript/flavours/glitch/features/explore/index.jsx b/app/javascript/flavours/glitch/features/explore/index.jsx index cca9cada47..8137e75ea4 100644 --- a/app/javascript/flavours/glitch/features/explore/index.jsx +++ b/app/javascript/flavours/glitch/features/explore/index.jsx @@ -13,6 +13,7 @@ import SearchIcon from '@/material-icons/400-24px/search.svg?react'; import Column from 'flavours/glitch/components/column'; import ColumnHeader from 'flavours/glitch/components/column_header'; import Search from 'flavours/glitch/features/compose/containers/search_container'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { trendsEnabled } from 'flavours/glitch/initial_state'; import Links from './links'; @@ -32,12 +33,8 @@ const mapStateToProps = state => ({ }); class Explore extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, intl: PropTypes.object.isRequired, multiColumn: PropTypes.bool, isSearching: PropTypes.bool, @@ -53,7 +50,7 @@ class Explore extends PureComponent { render() { const { intl, multiColumn, isSearching } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; return ( @@ -114,4 +111,4 @@ class Explore extends PureComponent { } -export default connect(mapStateToProps)(injectIntl(Explore)); +export default withIdentity(connect(mapStateToProps)(injectIntl(Explore))); diff --git a/app/javascript/flavours/glitch/features/firehose/index.jsx b/app/javascript/flavours/glitch/features/firehose/index.jsx index dc6a38ae2e..2a6d790d52 100644 --- a/app/javascript/flavours/glitch/features/firehose/index.jsx +++ b/app/javascript/flavours/glitch/features/firehose/index.jsx @@ -6,6 +6,7 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import { Helmet } from 'react-helmet'; import { NavLink } from 'react-router-dom'; +import { useIdentity } from '@/flavours/glitch/identity_context'; import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import { addColumn } from 'flavours/glitch/actions/columns'; import { changeSetting } from 'flavours/glitch/actions/settings'; @@ -13,7 +14,7 @@ import { connectPublicStream, connectCommunityStream } from 'flavours/glitch/act import { expandPublicTimeline, expandCommunityTimeline } from 'flavours/glitch/actions/timelines'; import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; import SettingText from 'flavours/glitch/components/setting_text'; -import initialState, { domain } from 'flavours/glitch/initial_state'; +import { domain } from 'flavours/glitch/initial_state'; import { useAppDispatch, useAppSelector } from 'flavours/glitch/store'; import Column from '../../components/column'; @@ -26,15 +27,6 @@ const messages = defineMessages({ filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' }, }); -// TODO: use a proper React context later on -const useIdentity = () => ({ - signedIn: !!initialState.meta.me, - accountId: initialState.meta.me, - disabledAccountId: initialState.meta.disabled_account_id, - accessToken: initialState.meta.access_token, - permissions: initialState.role ? initialState.role.permissions : 0, -}); - const ColumnSettings = () => { const intl = useIntl(); const dispatch = useAppDispatch(); diff --git a/app/javascript/flavours/glitch/features/getting_started/index.jsx b/app/javascript/flavours/glitch/features/getting_started/index.jsx index 145cb09a30..2d13d3d584 100644 --- a/app/javascript/flavours/glitch/features/getting_started/index.jsx +++ b/app/javascript/flavours/glitch/features/getting_started/index.jsx @@ -28,6 +28,7 @@ import { fetchLists } from 'flavours/glitch/actions/lists'; import { openModal } from 'flavours/glitch/actions/modal'; import Column from 'flavours/glitch/features/ui/components/column'; import LinkFooter from 'flavours/glitch/features/ui/components/link_footer'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { preferencesLink } from 'flavours/glitch/utils/backend_links'; @@ -99,12 +100,8 @@ const badgeDisplay = (number, limit) => { }; class GettingStarted extends ImmutablePureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, intl: PropTypes.object.isRequired, myAccount: ImmutablePropTypes.record, columns: ImmutablePropTypes.list, @@ -123,7 +120,7 @@ class GettingStarted extends ImmutablePureComponent { componentDidMount () { const { fetchFollowRequests } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (!signedIn) { return; @@ -134,7 +131,7 @@ class GettingStarted extends ImmutablePureComponent { render () { const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; const navItems = []; let listItems = []; @@ -219,4 +216,4 @@ class GettingStarted extends ImmutablePureComponent { } -export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted)); +export default withIdentity(connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted))); diff --git a/app/javascript/flavours/glitch/features/getting_started_misc/index.jsx b/app/javascript/flavours/glitch/features/getting_started_misc/index.jsx index 7c4979bd63..4a0dbb4a1c 100644 --- a/app/javascript/flavours/glitch/features/getting_started_misc/index.jsx +++ b/app/javascript/flavours/glitch/features/getting_started_misc/index.jsx @@ -16,7 +16,7 @@ import { openModal } from 'flavours/glitch/actions/modal'; import Column from 'flavours/glitch/features/ui/components/column'; import ColumnLink from 'flavours/glitch/features/ui/components/column_link'; import ColumnSubheading from 'flavours/glitch/features/ui/components/column_subheading'; - +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; const messages = defineMessages({ heading: { id: 'column.heading', defaultMessage: 'Misc' }, @@ -32,11 +32,8 @@ const messages = defineMessages({ class GettingStartedMisc extends ImmutablePureComponent { - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, intl: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, }; @@ -49,7 +46,7 @@ class GettingStartedMisc extends ImmutablePureComponent { render () { const { intl } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; return ( @@ -69,4 +66,4 @@ class GettingStartedMisc extends ImmutablePureComponent { } -export default connect()(injectIntl(GettingStartedMisc)); +export default connect()(withIdentity(injectIntl(GettingStartedMisc))); diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/index.jsx b/app/javascript/flavours/glitch/features/hashtag_timeline/index.jsx index c2a7574307..c33c458c27 100644 --- a/app/javascript/flavours/glitch/features/hashtag_timeline/index.jsx +++ b/app/javascript/flavours/glitch/features/hashtag_timeline/index.jsx @@ -17,6 +17,7 @@ import { fetchHashtag, followHashtag, unfollowHashtag } from 'flavours/glitch/ac import { expandHashtagTimeline, clearTimeline } from 'flavours/glitch/actions/timelines'; import Column from 'flavours/glitch/components/column'; import ColumnHeader from 'flavours/glitch/components/column_header'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import StatusListContainer from '../ui/containers/status_list_container'; @@ -29,14 +30,10 @@ const mapStateToProps = (state, props) => ({ }); class HashtagTimeline extends PureComponent { - disconnects = []; - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, params: PropTypes.object.isRequired, columnId: PropTypes.string, dispatch: PropTypes.func.isRequired, @@ -94,7 +91,7 @@ class HashtagTimeline extends PureComponent { }; _subscribe (dispatch, id, tags = {}, local) { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (!signedIn) { return; @@ -168,7 +165,7 @@ class HashtagTimeline extends PureComponent { handleFollow = () => { const { dispatch, params, tag } = this.props; const { id } = params; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (!signedIn) { return; @@ -185,7 +182,7 @@ class HashtagTimeline extends PureComponent { const { hasUnread, columnId, multiColumn, tag } = this.props; const { id, local } = this.props.params; const pinned = !!columnId; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; return ( @@ -225,4 +222,4 @@ class HashtagTimeline extends PureComponent { } -export default connect(mapStateToProps)(HashtagTimeline); +export default connect(mapStateToProps)(withIdentity(HashtagTimeline)); diff --git a/app/javascript/flavours/glitch/features/home_timeline/index.jsx b/app/javascript/flavours/glitch/features/home_timeline/index.jsx index a2683f587f..3220f777e1 100644 --- a/app/javascript/flavours/glitch/features/home_timeline/index.jsx +++ b/app/javascript/flavours/glitch/features/home_timeline/index.jsx @@ -14,6 +14,7 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'flavours/glitch/act import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge'; import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator'; import AnnouncementsContainer from 'flavours/glitch/features/getting_started/containers/announcements_container'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { criticalUpdatesPending } from 'flavours/glitch/initial_state'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; @@ -41,12 +42,8 @@ const mapStateToProps = state => ({ }); class HomeTimeline extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, dispatch: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, hasUnread: PropTypes.bool, @@ -128,7 +125,7 @@ class HomeTimeline extends PureComponent { render () { const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const pinned = !!columnId; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; const banners = []; let announcementsButton; @@ -192,4 +189,4 @@ class HomeTimeline extends PureComponent { } -export default connect(mapStateToProps)(injectIntl(HomeTimeline)); +export default connect(mapStateToProps)(withIdentity(injectIntl(HomeTimeline))); diff --git a/app/javascript/flavours/glitch/features/notifications/components/column_settings.jsx b/app/javascript/flavours/glitch/features/notifications/components/column_settings.jsx index 3f02df1881..ee78466b90 100644 --- a/app/javascript/flavours/glitch/features/notifications/components/column_settings.jsx +++ b/app/javascript/flavours/glitch/features/notifications/components/column_settings.jsx @@ -5,6 +5,7 @@ import { FormattedMessage } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'flavours/glitch/permissions'; import { CheckboxWithLabel } from './checkbox_with_label'; @@ -13,13 +14,9 @@ import GrantPermissionButton from './grant_permission_button'; import PillBarButton from './pill_bar_button'; import SettingToggle from './setting_toggle'; -export default class ColumnSettings extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - +class ColumnSettings extends PureComponent { static propTypes = { + identity: identityContextPropShape, settings: ImmutablePropTypes.map.isRequired, pushSettings: ImmutablePropTypes.map.isRequired, onChange: PropTypes.func.isRequired, @@ -216,7 +213,7 @@ export default class ColumnSettings extends PureComponent { - {((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && ( + {((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (

@@ -229,7 +226,7 @@ export default class ColumnSettings extends PureComponent {
)} - {((this.context.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && ( + {((this.props.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (

@@ -246,3 +243,5 @@ export default class ColumnSettings extends PureComponent { } } + +export default withIdentity(ColumnSettings); diff --git a/app/javascript/flavours/glitch/features/notifications/index.jsx b/app/javascript/flavours/glitch/features/notifications/index.jsx index e84ef70b05..b4f1bf5dfe 100644 --- a/app/javascript/flavours/glitch/features/notifications/index.jsx +++ b/app/javascript/flavours/glitch/features/notifications/index.jsx @@ -19,6 +19,7 @@ import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg? import { compareId } from 'flavours/glitch/compare_id'; import { Icon } from 'flavours/glitch/components/icon'; import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { submitMarkers } from '../../actions/markers'; @@ -93,12 +94,8 @@ const mapDispatchToProps = dispatch => ({ }); class Notifications extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, columnId: PropTypes.string, notifications: ImmutablePropTypes.list.isRequired, showFilterBar: PropTypes.bool.isRequired, @@ -225,7 +222,7 @@ class Notifications extends PureComponent { const { animatingNCD } = this.state; const pinned = !!columnId; const emptyMessage = ; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; let scrollableContent = null; @@ -373,4 +370,4 @@ class Notifications extends PureComponent { } -export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Notifications)); +export default connect(mapStateToProps, mapDispatchToProps)(withIdentity(injectIntl(Notifications))); diff --git a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx index e21ee2d3d3..0b6b91fe91 100644 --- a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx +++ b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx @@ -18,6 +18,7 @@ import { replyCompose } from 'flavours/glitch/actions/compose'; import { reblog, favourite, unreblog, unfavourite } from 'flavours/glitch/actions/interactions'; import { openModal } from 'flavours/glitch/actions/modal'; import { IconButton } from 'flavours/glitch/components/icon_button'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { me, boostModal } from 'flavours/glitch/initial_state'; import { makeGetStatus } from 'flavours/glitch/selectors'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; @@ -48,12 +49,8 @@ const makeMapStateToProps = () => { }; class Footer extends ImmutablePureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, statusId: PropTypes.string.isRequired, status: ImmutablePropTypes.map.isRequired, intl: PropTypes.object.isRequired, @@ -77,7 +74,7 @@ class Footer extends ImmutablePureComponent { handleReplyClick = () => { const { dispatch, askReplyConfirmation, status, intl } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (signedIn) { if (askReplyConfirmation) { @@ -106,7 +103,7 @@ class Footer extends ImmutablePureComponent { handleFavouriteClick = () => { const { dispatch, status } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (signedIn) { if (status.get('favourited')) { @@ -133,7 +130,7 @@ class Footer extends ImmutablePureComponent { handleReblogClick = e => { const { dispatch, status } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (signedIn) { if (status.get('reblogged')) { @@ -236,4 +233,4 @@ class Footer extends ImmutablePureComponent { } -export default connect(makeMapStateToProps)(withRouter(injectIntl(Footer))); +export default connect(makeMapStateToProps)(withIdentity(withRouter(injectIntl(Footer)))); diff --git a/app/javascript/flavours/glitch/features/public_timeline/index.jsx b/app/javascript/flavours/glitch/features/public_timeline/index.jsx index 8b9503928b..1f255a468d 100644 --- a/app/javascript/flavours/glitch/features/public_timeline/index.jsx +++ b/app/javascript/flavours/glitch/features/public_timeline/index.jsx @@ -9,6 +9,7 @@ import { connect } from 'react-redux'; import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { domain } from 'flavours/glitch/initial_state'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; @@ -44,16 +45,12 @@ const mapStateToProps = (state, { columnId }) => { }; class PublicTimeline extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static defaultProps = { onlyMedia: false, }; static propTypes = { + identity: identityContextPropShape, dispatch: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, columnId: PropTypes.string, @@ -86,7 +83,7 @@ class PublicTimeline extends PureComponent { componentDidMount () { const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; dispatch(expandPublicTimeline({ onlyMedia, onlyRemote, allowLocalOnly })); if (signedIn) { @@ -95,7 +92,7 @@ class PublicTimeline extends PureComponent { } componentDidUpdate (prevProps) { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote || prevProps.allowLocalOnly !== this.props.allowLocalOnly) { const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props; @@ -170,4 +167,4 @@ class PublicTimeline extends PureComponent { } -export default connect(mapStateToProps)(injectIntl(PublicTimeline)); +export default connect(mapStateToProps)(withIdentity(injectIntl(PublicTimeline))); diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.jsx b/app/javascript/flavours/glitch/features/status/components/action_bar.jsx index ab00091200..808712b021 100644 --- a/app/javascript/flavours/glitch/features/status/components/action_bar.jsx +++ b/app/javascript/flavours/glitch/features/status/components/action_bar.jsx @@ -20,6 +20,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react'; import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react'; import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react'; import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; @@ -59,12 +60,8 @@ const messages = defineMessages({ }); class ActionBar extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, status: ImmutablePropTypes.map.isRequired, onReply: PropTypes.func.isRequired, onReblog: PropTypes.func.isRequired, @@ -157,7 +154,7 @@ class ActionBar extends PureComponent { render () { const { status, intl } = this.props; - const { signedIn, permissions } = this.context.identity; + const { signedIn, permissions } = this.props.identity; const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility')); @@ -265,4 +262,4 @@ class ActionBar extends PureComponent { } -export default withRouter(injectIntl(ActionBar)); +export default withRouter(withIdentity(injectIntl(ActionBar))); diff --git a/app/javascript/flavours/glitch/features/status/index.jsx b/app/javascript/flavours/glitch/features/status/index.jsx index 6274bf672c..2656f8afdd 100644 --- a/app/javascript/flavours/glitch/features/status/index.jsx +++ b/app/javascript/flavours/glitch/features/status/index.jsx @@ -21,6 +21,7 @@ import { Icon } from 'flavours/glitch/components/icon'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; import ScrollContainer from 'flavours/glitch/containers/scroll_container'; import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; @@ -185,12 +186,8 @@ const titleFromStatus = (intl, status) => { }; class Status extends ImmutablePureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, status: ImmutablePropTypes.map, @@ -277,7 +274,7 @@ class Status extends ImmutablePureComponent { handleFavouriteClick = (status, e) => { const { dispatch } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (signedIn) { if (status.get('favourited')) { @@ -317,7 +314,7 @@ class Status extends ImmutablePureComponent { handleReplyClick = (status) => { const { askReplyConfirmation, dispatch, intl } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (signedIn) { if (askReplyConfirmation) { @@ -357,7 +354,7 @@ class Status extends ImmutablePureComponent { handleReblogClick = (status, e) => { const { settings, dispatch } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; if (signedIn) { if (settings.get('confirm_boost_missing_media_description') && status.get('media_attachments').some(item => !item.get('description')) && !status.get('reblogged')) { @@ -792,4 +789,4 @@ class Status extends ImmutablePureComponent { } -export default withRouter(injectIntl(connect(makeMapStateToProps)(Status))); +export default withRouter(injectIntl(connect(makeMapStateToProps)(withIdentity(Status)))); diff --git a/app/javascript/flavours/glitch/features/ui/components/compose_panel.jsx b/app/javascript/flavours/glitch/features/ui/components/compose_panel.jsx index a99d76c1a4..e530b87d26 100644 --- a/app/javascript/flavours/glitch/features/ui/components/compose_panel.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/compose_panel.jsx @@ -7,16 +7,13 @@ import { mountCompose, unmountCompose } from 'flavours/glitch/actions/compose'; import ServerBanner from 'flavours/glitch/components/server_banner'; import ComposeFormContainer from 'flavours/glitch/features/compose/containers/compose_form_container'; import SearchContainer from 'flavours/glitch/features/compose/containers/search_container'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import LinkFooter from './link_footer'; class ComposePanel extends PureComponent { - - static contextTypes = { - identity: PropTypes.object.isRequired, - }; - static propTypes = { + identity: identityContextPropShape, dispatch: PropTypes.func.isRequired, }; @@ -31,7 +28,7 @@ class ComposePanel extends PureComponent { } render() { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; return (
@@ -55,4 +52,4 @@ class ComposePanel extends PureComponent { } -export default connect()(ComposePanel); +export default connect()(withIdentity(ComposePanel)); diff --git a/app/javascript/flavours/glitch/features/ui/components/header.jsx b/app/javascript/flavours/glitch/features/ui/components/header.jsx index 618a6b63d4..f102912faa 100644 --- a/app/javascript/flavours/glitch/features/ui/components/header.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/header.jsx @@ -14,6 +14,7 @@ import { Avatar } from 'flavours/glitch/components/avatar'; import { Icon } from 'flavours/glitch/components/icon'; import { WordmarkLogo, SymbolLogo } from 'flavours/glitch/components/logo'; import { Permalink } from 'flavours/glitch/components/permalink'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { registrationsOpen, me, sso_redirect } from 'flavours/glitch/initial_state'; const Account = connect(state => ({ @@ -42,12 +43,8 @@ const mapDispatchToProps = (dispatch) => ({ }); class Header extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, openClosedRegistrationsModal: PropTypes.func, location: PropTypes.object, signupUrl: PropTypes.string.isRequired, @@ -61,7 +58,7 @@ class Header extends PureComponent { } render () { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props; let content; @@ -122,4 +119,4 @@ class Header extends PureComponent { } -export default injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(Header))); +export default injectIntl(withRouter(withIdentity(connect(mapStateToProps, mapDispatchToProps)(Header)))); diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.jsx b/app/javascript/flavours/glitch/features/ui/components/link_footer.jsx index 6741552731..7c0ece465f 100644 --- a/app/javascript/flavours/glitch/features/ui/components/link_footer.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.jsx @@ -8,6 +8,7 @@ import { Link } from 'react-router-dom'; import { connect } from 'react-redux'; import { openModal } from 'flavours/glitch/actions/modal'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'flavours/glitch/initial_state'; import { PERMISSION_INVITE_USERS } from 'flavours/glitch/permissions'; import { logOut } from 'flavours/glitch/utils/log_out'; @@ -32,12 +33,8 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ }); class LinkFooter extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, multiColumn: PropTypes.bool, onLogout: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, @@ -53,7 +50,7 @@ class LinkFooter extends PureComponent { }; render () { - const { signedIn, permissions } = this.context.identity; + const { signedIn, permissions } = this.props.identity; const { multiColumn } = this.props; const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS); @@ -108,4 +105,4 @@ class LinkFooter extends PureComponent { } -export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter)); +export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter))); diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx index fa1df612fa..98d82342cf 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx @@ -30,6 +30,7 @@ import StarIcon from '@/material-icons/400-24px/star.svg?react'; import { fetchFollowRequests } from 'flavours/glitch/actions/accounts'; import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge'; import { NavigationPortal } from 'flavours/glitch/components/navigation_portal'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { timelinePreview, trendsEnabled } from 'flavours/glitch/initial_state'; import { transientSingleColumn } from 'flavours/glitch/is_mobile'; import { preferencesLink } from 'flavours/glitch/utils/backend_links'; @@ -98,12 +99,8 @@ const FollowRequestsLink = () => { }; class NavigationPanel extends Component { - - static contextTypes = { - identity: PropTypes.object.isRequired, - }; - static propTypes = { + identity: identityContextPropShape, intl: PropTypes.object.isRequired, onOpenSettings: PropTypes.func, }; @@ -114,7 +111,7 @@ class NavigationPanel extends Component { render () { const { intl, onOpenSettings } = this.props; - const { signedIn, disabledAccountId } = this.context.identity; + const { signedIn, disabledAccountId } = this.props.identity; let banner = undefined; @@ -188,4 +185,4 @@ class NavigationPanel extends Component { } -export default injectIntl(NavigationPanel); +export default injectIntl(withIdentity(NavigationPanel)); diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index c257b8cdf5..4a7b9ebfde 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -17,6 +17,7 @@ import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'flavour import { INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding'; import { Permalink } from 'flavours/glitch/components/permalink'; import { PictureInPicture } from 'flavours/glitch/features/picture_in_picture'; +import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { layoutFromWindow } from 'flavours/glitch/is_mobile'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; @@ -129,12 +130,8 @@ const keyMap = { }; class SwitchingColumnsArea extends PureComponent { - - static contextTypes = { - identity: PropTypes.object, - }; - static propTypes = { + identity: identityContextPropShape, children: PropTypes.node, location: PropTypes.object, singleColumn: PropTypes.bool, @@ -169,7 +166,7 @@ class SwitchingColumnsArea extends PureComponent { render () { const { children, singleColumn } = this.props; - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; const pathName = this.props.location.pathname; let redirect; @@ -262,12 +259,8 @@ class SwitchingColumnsArea extends PureComponent { } class UI extends PureComponent { - - static contextTypes = { - identity: PropTypes.object.isRequired, - }; - static propTypes = { + identity: identityContextPropShape, dispatch: PropTypes.func.isRequired, children: PropTypes.node, isWide: PropTypes.bool, @@ -323,7 +316,7 @@ class UI extends PureComponent { this.dragTargets.push(e.target); } - if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) { + if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.props.identity.signedIn) { this.setState({ draggingOver: true }); } }; @@ -351,7 +344,7 @@ class UI extends PureComponent { this.setState({ draggingOver: false }); this.dragTargets = []; - if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.context.identity.signedIn) { + if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.props.identity.signedIn) { this.props.dispatch(uploadCompose(e.dataTransfer.files)); } }; @@ -403,7 +396,7 @@ class UI extends PureComponent { }; componentDidMount () { - const { signedIn } = this.context.identity; + const { signedIn } = this.props.identity; window.addEventListener('beforeunload', this.handleBeforeUnload, false); window.addEventListener('resize', this.handleResize, { passive: true }); @@ -649,7 +642,7 @@ class UI extends PureComponent {
- + {children} @@ -665,4 +658,4 @@ class UI extends PureComponent { } -export default connect(mapStateToProps)(injectIntl(withRouter(UI))); +export default connect(mapStateToProps)(injectIntl(withRouter(withIdentity(UI)))); diff --git a/app/javascript/flavours/glitch/identity_context.tsx b/app/javascript/flavours/glitch/identity_context.tsx new file mode 100644 index 0000000000..28dcb1f14b --- /dev/null +++ b/app/javascript/flavours/glitch/identity_context.tsx @@ -0,0 +1,74 @@ +import PropTypes from 'prop-types'; +import { createContext, useContext } from 'react'; + +import hoistStatics from 'hoist-non-react-statics'; + +import type { InitialState } from 'flavours/glitch/initial_state'; + +export interface IdentityContextType { + signedIn: boolean; + accountId: string | undefined; + disabledAccountId: string | undefined; + accessToken: string | undefined; + permissions: number; +} + +export const identityContextPropShape = PropTypes.shape({ + signedIn: PropTypes.bool.isRequired, + accountId: PropTypes.string, + disabledAccountId: PropTypes.string, + accessToken: PropTypes.string, +}).isRequired; + +export const createIdentityContext = (state: InitialState) => ({ + signedIn: !!state.meta.me, + accountId: state.meta.me, + disabledAccountId: state.meta.disabled_account_id, + accessToken: state.meta.access_token, + permissions: state.role?.permissions ?? 0, +}); + +export const IdentityContext = createContext({ + signedIn: false, + permissions: 0, + accountId: undefined, + disabledAccountId: undefined, + accessToken: undefined, +}); + +export const useIdentity = () => useContext(IdentityContext); + +export interface IdentityProps { + ref?: unknown; + wrappedComponentRef?: unknown; +} + +/* Injects an `identity` props into the wrapped component to be able to use the new context in class components */ +export function withIdentity< + ComponentType extends React.ComponentType, +>(Component: ComponentType) { + const displayName = `withIdentity(${Component.displayName ?? Component.name})`; + const C = (props: React.ComponentProps) => { + const { wrappedComponentRef, ...remainingProps } = props; + + return ( + + {(context) => { + return ( + // @ts-expect-error - Dynamic covariant generic components are tough to type. + + ); + }} + + ); + }; + + C.displayName = displayName; + C.WrappedComponent = Component; + + return hoistStatics(C, Component); +} diff --git a/app/javascript/flavours/glitch/initial_state.js b/app/javascript/flavours/glitch/initial_state.js index 3e84966cb4..1a40810418 100644 --- a/app/javascript/flavours/glitch/initial_state.js +++ b/app/javascript/flavours/glitch/initial_state.js @@ -50,12 +50,22 @@ * @property {string} default_content_type */ +/** + * @typedef Role + * @property {string} id + * @property {string} name + * @property {string} permissions + * @property {string} color + * @property {boolean} highlighted + */ + /** * @typedef InitialState * @property {Record} accounts * @property {InitialStateLanguage[]} languages * @property {boolean=} critical_updates_pending * @property {InitialStateMeta} meta + * @property {Role?} role * @property {object} local_settings * @property {number} max_feed_hashtags * @property {number} poll_limits