diff --git a/app/soapbox/components/column_header.js b/app/soapbox/components/column_header.js index 42f8eebb8..83a61b7ea 100644 --- a/app/soapbox/components/column_header.js +++ b/app/soapbox/components/column_header.js @@ -14,7 +14,6 @@ import SubNavigation from 'soapbox/components/sub_navigation'; // hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' }, // }); -export default @withRouter class ColumnHeader extends React.PureComponent { static propTypes = { @@ -126,3 +125,5 @@ class ColumnHeader extends React.PureComponent { // } } + +export default withRouter(ColumnHeader); \ No newline at end of file diff --git a/app/soapbox/features/admin/user_index.js b/app/soapbox/features/admin/user_index.js index 213041f98..032ed6dc2 100644 --- a/app/soapbox/features/admin/user_index.js +++ b/app/soapbox/features/admin/user_index.js @@ -18,8 +18,6 @@ const messages = defineMessages({ searchPlaceholder: { id: 'admin.user_index.search_input_placeholder', defaultMessage: 'Who are you looking for?' }, }); -export default @connect() -@injectIntl class UserIndex extends ImmutablePureComponent { static propTypes = { @@ -130,3 +128,5 @@ class UserIndex extends ImmutablePureComponent { } } + +export default injectIntl(connect()(UserIndex)); \ No newline at end of file diff --git a/app/soapbox/features/federation_restrictions/components/instance_restrictions.js b/app/soapbox/features/federation_restrictions/components/instance_restrictions.tsx similarity index 80% rename from app/soapbox/features/federation_restrictions/components/instance_restrictions.js rename to app/soapbox/features/federation_restrictions/components/instance_restrictions.tsx index fdb3cd328..ac86039fd 100644 --- a/app/soapbox/features/federation_restrictions/components/instance_restrictions.js +++ b/app/soapbox/features/federation_restrictions/components/instance_restrictions.tsx @@ -1,39 +1,29 @@ 'use strict'; -import PropTypes from 'prop-types'; import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; import { FormattedMessage } from 'react-intl'; -import { connect } from 'react-redux'; import Icon from 'soapbox/components/icon'; import { Text } from 'soapbox/components/ui'; +import { useAppSelector } from 'soapbox/hooks'; -const hasRestrictions = remoteInstance => { +import type { Map as ImmutableMap } from 'immutable'; + +const hasRestrictions = (remoteInstance: ImmutableMap): boolean => { return remoteInstance .get('federation') .deleteAll(['accept', 'reject_deletes', 'report_removal']) - .reduce((acc, value) => acc || value, false); + .reduce((acc: boolean, value: boolean) => acc || value, false); }; -const mapStateToProps = state => { - return { - instance: state.get('instance'), - }; -}; +interface IInstanceRestrictions { + remoteInstance: ImmutableMap, +} -export default @connect(mapStateToProps) -class InstanceRestrictions extends ImmutablePureComponent { +const InstanceRestrictions: React.FC = ({ remoteInstance }) => { + const instance = useAppSelector(state => state.instance); - static propTypes = { - intl: PropTypes.object.isRequired, - remoteInstance: ImmutablePropTypes.map.isRequired, - instance: ImmutablePropTypes.map, - }; - - renderRestrictions = () => { - const { remoteInstance } = this.props; + const renderRestrictions = () => { const items = []; const { @@ -105,10 +95,9 @@ class InstanceRestrictions extends ImmutablePureComponent { } return items; - } + }; - renderContent = () => { - const { instance, remoteInstance } = this.props; + const renderContent = () => { if (!instance || !remoteInstance) return null; const host = remoteInstance.get('host'); @@ -136,7 +125,7 @@ class InstanceRestrictions extends ImmutablePureComponent { /> ), - this.renderRestrictions(), + renderRestrictions(), ]; } else { return ( @@ -150,14 +139,13 @@ class InstanceRestrictions extends ImmutablePureComponent { ); } - } + }; - render() { - return ( -
- {this.renderContent()} -
- ); - } + return ( +
+ {renderContent()} +
+ ); +}; -} +export default InstanceRestrictions; diff --git a/app/soapbox/features/soapbox_config/components/icon-picker-menu.js b/app/soapbox/features/soapbox_config/components/icon-picker-menu.js new file mode 100644 index 000000000..c89b00488 --- /dev/null +++ b/app/soapbox/features/soapbox_config/components/icon-picker-menu.js @@ -0,0 +1,154 @@ +import classNames from 'clsx'; +import { supportsPassiveEvents } from 'detect-passive-events'; +import Picker from 'emoji-mart/dist-es/components/picker/picker'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { defineMessages, injectIntl } from 'react-intl'; + +const messages = defineMessages({ + emoji: { id: 'icon_button.label', defaultMessage: 'Select icon' }, + emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search…' }, + emoji_not_found: { id: 'icon_button.not_found', defaultMessage: 'No icons!! (╯°□°)╯︵ ┻━┻' }, + custom: { id: 'icon_button.icons', defaultMessage: 'Icons' }, + search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' }, +}); + +const backgroundImageFn = () => ''; +const listenerOptions = supportsPassiveEvents ? { passive: true } : false; + +const categoriesSort = ['custom']; + + +class IconPickerMenu extends React.PureComponent { + + static propTypes = { + custom_emojis: PropTypes.object, + loading: PropTypes.bool, + onClose: PropTypes.func.isRequired, + onPick: PropTypes.func.isRequired, + style: PropTypes.object, + placement: PropTypes.string, + arrowOffsetLeft: PropTypes.string, + arrowOffsetTop: PropTypes.string, + intl: PropTypes.object.isRequired, + }; + + static defaultProps = { + style: {}, + loading: true, + }; + + state = { + modifierOpen: false, + placement: null, + }; + + handleDocumentClick = e => { + if (this.node && !this.node.contains(e.target)) { + this.props.onClose(); + } + } + + componentDidMount() { + document.addEventListener('click', this.handleDocumentClick, false); + document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); + } + + componentWillUnmount() { + document.removeEventListener('click', this.handleDocumentClick, false); + document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); + } + + setRef = c => { + this.node = c; + + if (!c) return; + + // Nice and dirty hack to display the icons + c.querySelectorAll('button.emoji-mart-emoji > img').forEach(elem => { + const newIcon = document.createElement('span'); + newIcon.innerHTML = ``; + elem.parentNode.replaceChild(newIcon, elem); + }); + } + + getI18n = () => { + const { intl } = this.props; + + return { + search: intl.formatMessage(messages.emoji_search), + notfound: intl.formatMessage(messages.emoji_not_found), + categories: { + search: intl.formatMessage(messages.search_results), + custom: intl.formatMessage(messages.custom), + }, + }; + } + + handleClick = emoji => { + emoji.native = emoji.colons; + + this.props.onClose(); + this.props.onPick(emoji); + } + + buildIcons = (customEmojis, autoplay = false) => { + const emojis = []; + + Object.values(customEmojis).forEach(category => { + category.forEach(function(icon) { + const name = icon.replace('fa fa-', ''); + if (icon !== 'email' && icon !== 'memo') { + emojis.push({ + id: name, + name, + short_names: [name], + emoticons: [], + keywords: [name], + imageUrl: '', + }); + } + }); + }); + + return emojis; + }; + + render() { + const { loading, style, intl, custom_emojis } = this.props; + + if (loading) { + return
; + } + + const data = { compressed: true, categories: [], aliases: [], emojis: [] }; + const title = intl.formatMessage(messages.emoji); + const { modifierOpen } = this.state; + + return ( +
+ +
+ ); + } + +} + +export default injectIntl(IconPickerMenu); diff --git a/app/soapbox/features/soapbox_config/components/icon_picker_dropdown.js b/app/soapbox/features/soapbox_config/components/icon_picker_dropdown.js index cc2986a7b..5393f0dc8 100644 --- a/app/soapbox/features/soapbox_config/components/icon_picker_dropdown.js +++ b/app/soapbox/features/soapbox_config/components/icon_picker_dropdown.js @@ -1,6 +1,3 @@ -import classNames from 'clsx'; -import { supportsPassiveEvents } from 'detect-passive-events'; -import Picker from 'emoji-mart/dist-es/components/picker/picker'; import PropTypes from 'prop-types'; import React from 'react'; import { defineMessages, injectIntl } from 'react-intl'; @@ -8,153 +5,12 @@ import Overlay from 'react-overlays/lib/Overlay'; import Icon from 'soapbox/components/icon'; +import IconPickerMenu from './icon-picker-menu'; + const messages = defineMessages({ emoji: { id: 'icon_button.label', defaultMessage: 'Select icon' }, - emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search…' }, - emoji_not_found: { id: 'icon_button.not_found', defaultMessage: 'No icons!! (╯°□°)╯︵ ┻━┻' }, - custom: { id: 'icon_button.icons', defaultMessage: 'Icons' }, - search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' }, }); -const backgroundImageFn = () => ''; -const listenerOptions = supportsPassiveEvents ? { passive: true } : false; - -const categoriesSort = ['custom']; - -@injectIntl -class IconPickerMenu extends React.PureComponent { - - static propTypes = { - custom_emojis: PropTypes.object, - loading: PropTypes.bool, - onClose: PropTypes.func.isRequired, - onPick: PropTypes.func.isRequired, - style: PropTypes.object, - placement: PropTypes.string, - arrowOffsetLeft: PropTypes.string, - arrowOffsetTop: PropTypes.string, - intl: PropTypes.object.isRequired, - }; - - static defaultProps = { - style: {}, - loading: true, - }; - - state = { - modifierOpen: false, - placement: null, - }; - - handleDocumentClick = e => { - if (this.node && !this.node.contains(e.target)) { - this.props.onClose(); - } - } - - componentDidMount() { - document.addEventListener('click', this.handleDocumentClick, false); - document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); - } - - componentWillUnmount() { - document.removeEventListener('click', this.handleDocumentClick, false); - document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); - } - - setRef = c => { - this.node = c; - - if (!c) return; - - // Nice and dirty hack to display the icons - c.querySelectorAll('button.emoji-mart-emoji > img').forEach(elem => { - const newIcon = document.createElement('span'); - newIcon.innerHTML = ``; - elem.parentNode.replaceChild(newIcon, elem); - }); - } - - getI18n = () => { - const { intl } = this.props; - - return { - search: intl.formatMessage(messages.emoji_search), - notfound: intl.formatMessage(messages.emoji_not_found), - categories: { - search: intl.formatMessage(messages.search_results), - custom: intl.formatMessage(messages.custom), - }, - }; - } - - handleClick = emoji => { - emoji.native = emoji.colons; - - this.props.onClose(); - this.props.onPick(emoji); - } - - buildIcons = (customEmojis, autoplay = false) => { - const emojis = []; - - Object.values(customEmojis).forEach(category => { - category.forEach(function(icon) { - const name = icon.replace('fa fa-', ''); - if (icon !== 'email' && icon !== 'memo') { - emojis.push({ - id: name, - name, - short_names: [name], - emoticons: [], - keywords: [name], - imageUrl: '', - }); - } - }); - }); - - return emojis; - }; - - render() { - const { loading, style, intl, custom_emojis } = this.props; - - if (loading) { - return
; - } - - const data = { compressed: true, categories: [], aliases: [], emojis: [] }; - const title = intl.formatMessage(messages.emoji); - const { modifierOpen } = this.state; - - return ( -
- -
- ); - } - -} - -export default @injectIntl class IconPickerDropdown extends React.PureComponent { static propTypes = { @@ -243,3 +99,5 @@ class IconPickerDropdown extends React.PureComponent { } } + +export default injectIntl(IconPickerDropdown); \ No newline at end of file diff --git a/babel.config.js b/babel.config.js index 17af1a21c..f999f83ef 100644 --- a/babel.config.js +++ b/babel.config.js @@ -17,7 +17,6 @@ module.exports = (api) => { plugins: [ '@babel/syntax-dynamic-import', ['@babel/proposal-object-rest-spread', { useBuiltIns: true }], - ['@babel/proposal-decorators', { legacy: true }], '@babel/proposal-class-properties', ['react-intl', { messagesDir: './build/messages/' }], 'preval', diff --git a/package.json b/package.json index f1755ccac..e4813bf50 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "dependencies": { "@babel/core": "^7.18.2", "@babel/plugin-proposal-class-properties": "^7.17.12", - "@babel/plugin-proposal-decorators": "^7.18.2", "@babel/plugin-proposal-object-rest-spread": "^7.18.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-react-inline-elements": "^7.16.7", diff --git a/tsconfig.json b/tsconfig.json index 04ec4a470..c47b1a33f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,6 @@ "allowJs": true, "moduleResolution": "node", "resolveJsonModule": true, - "experimentalDecorators": true, "esModuleInterop": true, "typeRoots": [ "./types", "./node_modules/@types"] }, diff --git a/yarn.lock b/yarn.lock index 7ecfb05f8..9c2729f93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -538,18 +538,6 @@ "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-decorators@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.2.tgz#dbe4086d2d42db489399783c3aa9272e9700afd4" - integrity sha512-kbDISufFOxeczi0v4NQP3p5kIeW6izn/6klfWBrIIdGZZe4UpHR+QU03FAoWjGGd9SUXAwbw2pup1kaL4OQsJQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-replace-supers" "^7.18.2" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/plugin-syntax-decorators" "^7.17.12" - charcodes "^0.2.0" - "@babel/plugin-proposal-dynamic-import@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" @@ -688,13 +676,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-decorators@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.12.tgz#02e8f678602f0af8222235271efea945cfdb018a" - integrity sha512-D1Hz0qtGTza8K2xGyEdVNCYLdVHukAcbQr4K3/s6r/esadyEriZovpJimQOpu8ju4/jV8dW/1xdaE0UpDroidw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" @@ -4196,11 +4177,6 @@ character-reference-invalid@^1.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== -charcodes@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/charcodes/-/charcodes-0.2.0.tgz#5208d327e6cc05f99eb80ffc814707572d1f14e4" - integrity sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ== - cheerio-select@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823"