From 2bd344a594f45dd8586ca94d7099f3dc5b466db9 Mon Sep 17 00:00:00 2001 From: mgabdev <> Date: Fri, 27 Mar 2020 11:29:52 -0400 Subject: [PATCH] Progress --- app/javascript/gabsocial/assets/globe_icon.js | 1 + app/javascript/gabsocial/components/button.js | 1 + .../components/modal/modal_layout.js | 3 +- .../moved_note => components}/moved_note.js | 4 +- .../panel/notification_filter_panel.js | 27 +- .../components/panel/trends_panel.js | 8 +- .../popover/emoji_picker_popover.js} | 176 ++++++--- .../components/popover/popover_root.js | 2 + .../popover/status_privacy_popover.js | 259 -------------- .../popover/status_visibility_popover.js | 132 ++++++- .../gabsocial/components/profile_header.js | 2 + .../components/sidebar_section_item.js | 2 +- .../card.js => components/status_card.js} | 2 +- .../status_content/status_content.js | 14 +- .../gabsocial/components/trends_panel_item.js | 7 +- .../components/trends_panel_item_card.js | 2 +- .../{features/video => components}/video.js | 2 + .../account_timeline.js | 6 +- .../components/header/header.js | 105 ------ .../components/header/header.scss | 6 - .../components/header/index.js | 1 - .../components/inner_header/index.js | 1 - .../components/inner_header/inner_header.js | 337 ------------------ .../components/inner_header/inner_header.scss | 17 - .../components/moved_note/index.js | 1 - .../components/moved_note/moved_note.scss | 55 --- .../containers/header_container.js | 117 ------ .../features/account_timeline/index.js | 1 - .../{blocks.js => blocked_accounts.js} | 0 .../{domain_blocks.js => blocked_domains.js} | 0 .../components/compose_extra_button.js | 17 +- .../components/compose_form/compose_form.js | 55 ++- .../compose/components/emoji_picker_button.js | 38 +- .../compose/components/gif_selector_button.js | 53 +++ .../compose/components/post_privacy_button.js | 65 ---- .../components/rich_text_editor_button.js | 53 +++ .../components/status_visibility_button.js | 72 ++++ .../emoji_picker_dropdown_container.js | 82 ----- .../gabsocial/features/groups_collection.js | 17 +- .../features/status/components/card/card.scss | 169 --------- .../features/status/components/card/index.js | 1 - .../features/ui/util/async_components.js | 4 +- .../gabsocial/features/video/index.js | 1 - .../gabsocial/features/video/video.scss | 71 ---- app/javascript/gabsocial/reducers/compose.js | 2 +- .../styles/gabsocial/emoji_picker.scss | 2 +- 46 files changed, 545 insertions(+), 1448 deletions(-) rename app/javascript/gabsocial/{features/account_timeline/components/moved_note => components}/moved_note.js (92%) rename app/javascript/gabsocial/{features/compose/components/emoji_picker_dropdown/emoji_picker_dropdown.js => components/popover/emoji_picker_popover.js} (73%) delete mode 100644 app/javascript/gabsocial/components/popover/status_privacy_popover.js rename app/javascript/gabsocial/{features/status/components/card/card.js => components/status_card.js} (99%) rename app/javascript/gabsocial/{features/video => components}/video.js (99%) rename app/javascript/gabsocial/features/{account_timeline => }/account_timeline.js (95%) delete mode 100644 app/javascript/gabsocial/features/account_timeline/components/header/header.js delete mode 100644 app/javascript/gabsocial/features/account_timeline/components/header/header.scss delete mode 100644 app/javascript/gabsocial/features/account_timeline/components/header/index.js delete mode 100644 app/javascript/gabsocial/features/account_timeline/components/inner_header/index.js delete mode 100644 app/javascript/gabsocial/features/account_timeline/components/inner_header/inner_header.js delete mode 100644 app/javascript/gabsocial/features/account_timeline/components/inner_header/inner_header.scss delete mode 100644 app/javascript/gabsocial/features/account_timeline/components/moved_note/index.js delete mode 100644 app/javascript/gabsocial/features/account_timeline/components/moved_note/moved_note.scss delete mode 100644 app/javascript/gabsocial/features/account_timeline/containers/header_container.js delete mode 100644 app/javascript/gabsocial/features/account_timeline/index.js rename app/javascript/gabsocial/features/{blocks.js => blocked_accounts.js} (100%) rename app/javascript/gabsocial/features/{domain_blocks.js => blocked_domains.js} (100%) create mode 100644 app/javascript/gabsocial/features/compose/components/gif_selector_button.js delete mode 100644 app/javascript/gabsocial/features/compose/components/post_privacy_button.js create mode 100644 app/javascript/gabsocial/features/compose/components/rich_text_editor_button.js create mode 100644 app/javascript/gabsocial/features/compose/components/status_visibility_button.js delete mode 100644 app/javascript/gabsocial/features/compose/containers/emoji_picker_dropdown_container.js delete mode 100644 app/javascript/gabsocial/features/status/components/card/card.scss delete mode 100644 app/javascript/gabsocial/features/status/components/card/index.js delete mode 100644 app/javascript/gabsocial/features/video/index.js delete mode 100644 app/javascript/gabsocial/features/video/video.scss diff --git a/app/javascript/gabsocial/assets/globe_icon.js b/app/javascript/gabsocial/assets/globe_icon.js index c59355d0..5bb4905d 100644 --- a/app/javascript/gabsocial/assets/globe_icon.js +++ b/app/javascript/gabsocial/assets/globe_icon.js @@ -1,3 +1,4 @@ +// : todo : fill, stroke colors const GlobeIcon = ({ className = '', width = '12px', diff --git a/app/javascript/gabsocial/components/button.js b/app/javascript/gabsocial/components/button.js index e2f7b13a..411f0f00 100644 --- a/app/javascript/gabsocial/components/button.js +++ b/app/javascript/gabsocial/components/button.js @@ -139,6 +139,7 @@ export default class Button extends PureComponent { backgroundColorBrand_onHover: color === COLORS.brand && outline, colorWhite_onHover: !!children && color === COLORS.brand && outline, + fillColorSecondary: !!icon && color === COLORS.secondary, fillColorWhite: !!icon && color === COLORS.white, fillColorBrand: !!icon && color === COLORS.brand, fillColorWhite_onHover: !!icon && color === COLORS.brand && outline, diff --git a/app/javascript/gabsocial/components/modal/modal_layout.js b/app/javascript/gabsocial/components/modal/modal_layout.js index db7e8420..ad0c542d 100644 --- a/app/javascript/gabsocial/components/modal/modal_layout.js +++ b/app/javascript/gabsocial/components/modal/modal_layout.js @@ -58,7 +58,7 @@ class ModalLayout extends PureComponent { const childrenContainerClasses = cx({ default: 1, heightMax80VH: 1, - overflowScroll: 1, + overflowYScroll: 1, px15: !noPadding, py10: !noPadding, }) @@ -77,6 +77,7 @@ class ModalLayout extends PureComponent { title={intl.formatMessage(messages.close)} className={_s.marginLeftAuto} onClick={this.onHandleCloseModal} + color='secondary' icon='close' iconWidth='10px' iconWidth='10px' diff --git a/app/javascript/gabsocial/features/account_timeline/components/moved_note/moved_note.js b/app/javascript/gabsocial/components/moved_note.js similarity index 92% rename from app/javascript/gabsocial/features/account_timeline/components/moved_note/moved_note.js rename to app/javascript/gabsocial/components/moved_note.js index 6219861c..81b49275 100644 --- a/app/javascript/gabsocial/features/account_timeline/components/moved_note/moved_note.js +++ b/app/javascript/gabsocial/components/moved_note.js @@ -2,8 +2,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { NavLink } from 'react-router-dom'; -import DisplayName from '../../../../components/display_name'; -import Icon from '../../../../components/icon'; +import DisplayName from './display_name'; +import Icon from './icon'; export default class MovedNote extends ImmutablePureComponent { diff --git a/app/javascript/gabsocial/components/panel/notification_filter_panel.js b/app/javascript/gabsocial/components/panel/notification_filter_panel.js index 30c3015d..e428652a 100644 --- a/app/javascript/gabsocial/components/panel/notification_filter_panel.js +++ b/app/javascript/gabsocial/components/panel/notification_filter_panel.js @@ -8,6 +8,8 @@ import Button from '../button' import Divider from '../divider' import Heading from '../heading' import Icon from '../icon' +import Input from '../input' +import Switch from '../switch' import Text from '../text' const messages = defineMessages({ @@ -33,11 +35,26 @@ class NotificationFilterPanel extends ImmutablePureComponent { return ( - Date - Verified - Users - Status Id - Only People I Follow + Start Date + End Date + + + + + + + + + + + + ) } diff --git a/app/javascript/gabsocial/components/panel/trends_panel.js b/app/javascript/gabsocial/components/panel/trends_panel.js index b0ed2f1a..04a52300 100644 --- a/app/javascript/gabsocial/components/panel/trends_panel.js +++ b/app/javascript/gabsocial/components/panel/trends_panel.js @@ -54,10 +54,10 @@ class TrendsPanel extends ImmutablePureComponent { { /* trends && trends.map(hashtag => ( )) */ } - - - - + + + + ) diff --git a/app/javascript/gabsocial/features/compose/components/emoji_picker_dropdown/emoji_picker_dropdown.js b/app/javascript/gabsocial/components/popover/emoji_picker_popover.js similarity index 73% rename from app/javascript/gabsocial/features/compose/components/emoji_picker_dropdown/emoji_picker_dropdown.js rename to app/javascript/gabsocial/components/popover/emoji_picker_popover.js index 4e5f53a1..b9cc46e2 100644 --- a/app/javascript/gabsocial/features/compose/components/emoji_picker_dropdown/emoji_picker_dropdown.js +++ b/app/javascript/gabsocial/components/popover/emoji_picker_popover.js @@ -1,11 +1,14 @@ -import { defineMessages, injectIntl } from 'react-intl'; -import classNames from 'classnames'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import detectPassiveEvents from 'detect-passive-events'; -import Overlay from 'react-overlays/lib/Overlay'; -import { EmojiPicker as EmojiPickerAsync } from '../../../ui/util/async_components'; -import { buildCustomEmojis } from '../../../../components/emoji/emoji'; +import { defineMessages, injectIntl } from 'react-intl' +import ImmutablePropTypes from 'react-immutable-proptypes' +import ImmutablePureComponent from 'react-immutable-pure-component' +import { Map as ImmutableMap } from 'immutable' +import classNames from 'classnames' +import { createSelector } from 'reselect' +import detectPassiveEvents from 'detect-passive-events' +import { changeSetting } from '../../actions/settings' +import { useEmoji } from '../../actions/emojis' +import { EmojiPicker as EmojiPickerAsync } from '../../features/ui/util/async_components' +import { buildCustomEmojis } from '../emoji/emoji' const messages = defineMessages({ emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' }, @@ -22,13 +25,13 @@ const messages = defineMessages({ objects: { id: 'emoji_button.objects', defaultMessage: 'Objects' }, symbols: { id: 'emoji_button.symbols', defaultMessage: 'Symbols' }, flags: { id: 'emoji_button.flags', defaultMessage: 'Flags' }, -}); +}) -const assetHost = process.env.CDN_HOST || ''; -let EmojiPicker, Emoji; // load asynchronously +const assetHost = process.env.CDN_HOST || '' +let EmojiPicker, Emoji // load asynchronously -const backgroundImageFn = () => `${assetHost}/emoji/sheet_10.png`; -const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false; +const backgroundImageFn = () => `${assetHost}/emoji/sheet_10.png` +const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false const categoriesSort = [ 'recent', @@ -93,9 +96,9 @@ class ModifierPickerMenu extends PureComponent { return (
- + */}
); } @@ -144,7 +147,7 @@ class ModifierPicker extends PureComponent { return (
- + { /* */ }
); @@ -162,7 +165,6 @@ class EmojiPickerMenu extends ImmutablePureComponent { onClose: PropTypes.func.isRequired, onPick: PropTypes.func.isRequired, style: PropTypes.object, - placement: PropTypes.string, arrowOffsetLeft: PropTypes.string, arrowOffsetTop: PropTypes.string, intl: PropTypes.object.isRequired, @@ -178,7 +180,6 @@ class EmojiPickerMenu extends ImmutablePureComponent { state = { modifierOpen: false, - placement: null, }; handleDocumentClick = e => { @@ -252,7 +253,7 @@ class EmojiPickerMenu extends ImmutablePureComponent { } const title = intl.formatMessage(messages.emoji); - const { modifierOpen } = this.state; + const { modifierOpen } = this.state return (
@@ -289,9 +290,85 @@ class EmojiPickerMenu extends ImmutablePureComponent { } +const perLine = 8 +const lines = 2 + +const DEFAULTS = [ + '+1', + 'grinning', + 'kissing_heart', + 'heart_eyes', + 'laughing', + 'stuck_out_tongue_winking_eye', + 'sweat_smile', + 'joy', + 'yum', + 'disappointed', + 'thinking_face', + 'weary', + 'sob', + 'sunglasses', + 'heart', + 'ok_hand', +]; + +const getFrequentlyUsedEmojis = createSelector([ + state => state.getIn(['settings', 'frequentlyUsedEmojis'], ImmutableMap()), +], emojiCounters => { + let emojis = emojiCounters + .keySeq() + .sort((a, b) => emojiCounters.get(a) - emojiCounters.get(b)) + .reverse() + .slice(0, perLine * lines) + .toArray(); + + if (emojis.length < DEFAULTS.length) { + let uniqueDefaults = DEFAULTS.filter(emoji => !emojis.includes(emoji)); + emojis = emojis.concat(uniqueDefaults.slice(0, DEFAULTS.length - emojis.length)); + } + + return emojis; +}); + +const getCustomEmojis = createSelector([ + state => state.get('custom_emojis'), +], emojis => emojis.filter(e => e.get('visible_in_picker')).sort((a, b) => { + const aShort = a.get('shortcode').toLowerCase(); + const bShort = b.get('shortcode').toLowerCase(); + + if (aShort < bShort) { + return -1; + } else if (aShort > bShort ) { + return 1; + } + + return 0; +})); + +const mapStateToProps = state => ({ + custom_emojis: getCustomEmojis(state), + skinTone: state.getIn(['settings', 'skinTone']), + frequentlyUsedEmojis: getFrequentlyUsedEmojis(state), +}); + +const mapDispatchToProps = (dispatch, { onPickEmoji }) => ({ + onSkinTone: skinTone => { + dispatch(changeSetting(['skinTone'], skinTone)); + }, + + onPickEmoji: emoji => { + dispatch(useEmoji(emoji)); + + if (onPickEmoji) { + onPickEmoji(emoji); + } + }, +}); + export default @injectIntl -class EmojiPickerDropdown extends ImmutablePureComponent { +@connect(mapStateToProps, mapDispatchToProps) +class EmojiPickerPopover extends ImmutablePureComponent { static propTypes = { custom_emojis: ImmutablePropTypes.list, @@ -307,11 +384,7 @@ class EmojiPickerDropdown extends ImmutablePureComponent { loading: false, }; - setRef = (c) => { - this.dropdown = c; - } - - onShowDropdown = ({ target }) => { + componentWillMount = () => { this.setState({ active: true }); if (!EmojiPicker) { @@ -326,9 +399,6 @@ class EmojiPickerDropdown extends ImmutablePureComponent { this.setState({ loading: false }); }); } - - const { top } = target.getBoundingClientRect(); - this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' }); } onHideDropdown = () => { @@ -351,40 +421,28 @@ class EmojiPickerDropdown extends ImmutablePureComponent { } } - setTargetRef = c => { - this.target = c; - } - - findTarget = () => { - return this.target; - } - render () { - const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis } = this.props; - const title = intl.formatMessage(messages.emoji); - const { active, loading, placement } = this.state; + const { + intl, + onPickEmoji, + onSkinTone, + skinTone, + frequentlyUsedEmojis + } = this.props + + const { active, loading } = this.state; return (
-
- 🙂 -
- - - - +
); } diff --git a/app/javascript/gabsocial/components/popover/popover_root.js b/app/javascript/gabsocial/components/popover/popover_root.js index 0a82d35c..95b3328d 100644 --- a/app/javascript/gabsocial/components/popover/popover_root.js +++ b/app/javascript/gabsocial/components/popover/popover_root.js @@ -5,6 +5,7 @@ import BundleModalError from '../bundle_modal_error' import PopoverBase from './popover_base' import ContentWarningPopover from './content_warning_popover' import DatePickerPopover from './date_picker_popover' +import EmojiPickerPopover from './emoji_picker_popover' import GroupInfoPopover from './group_info_popover' import ProfileOptionsPopover from './profile_options_popover' import SearchPopover from './search_popover' @@ -19,6 +20,7 @@ const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : fal const POPOVER_COMPONENTS = { CONTENT_WARNING: () => Promise.resolve({ default: ContentWarningPopover }), DATE_PICKER: () => Promise.resolve({ default: DatePickerPopover }), + EMOJI_PICKER: () => Promise.resolve({ default: EmojiPickerPopover }), GROUP_INFO: () => GroupInfoPopover, PROFILE_OPTIONS: () => Promise.resolve({ default: ProfileOptionsPopover }), SEARCH: () => Promise.resolve({ default: SearchPopover }), diff --git a/app/javascript/gabsocial/components/popover/status_privacy_popover.js b/app/javascript/gabsocial/components/popover/status_privacy_popover.js deleted file mode 100644 index ef78c638..00000000 --- a/app/javascript/gabsocial/components/popover/status_privacy_popover.js +++ /dev/null @@ -1,259 +0,0 @@ -import { injectIntl, defineMessages } from 'react-intl' -import spring from 'react-motion/lib/spring' -import detectPassiveEvents from 'detect-passive-events' -import classNames from 'classnames' -import Overlay from 'react-overlays/lib/Overlay' -import { changeComposeVisibility } from '../../actions/compose' -import { openModal, closeModal } from '../../actions/modal' -import { isUserTouching } from '../../utils/is_mobile' -import Motion from '../../features/ui/util/optional_motion' -import Icon from '../icon' -import ComposeExtraButton from '../../features/compose/components/compose_extra_button' - -const messages = defineMessages({ - public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, - public_long: { id: 'privacy.public.long', defaultMessage: 'Post to public timelines' }, - unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' }, - unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Do not show in public timelines' }, - private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' }, - private_long: { id: 'privacy.private.long', defaultMessage: 'Post to followers only' }, - change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' }, - visibility: { id: 'privacy.visibility', defaultMessage: 'Visibility' }, -}) - -const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false - -class PrivacyDropdownMenu extends PureComponent { - - static propTypes = { - style: PropTypes.object, - items: PropTypes.array.isRequired, - value: PropTypes.string.isRequired, - placement: PropTypes.string.isRequired, - onClose: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, - } - - state = { - mounted: false, - } - - handleDocumentClick = e => { - if (this.node && !this.node.contains(e.target)) { - this.props.onClose() - } - } - - handleKeyDown = e => { - const { items } = this.props - const value = e.currentTarget.getAttribute('data-index') - const index = items.findIndex(item => { - return (item.value === value) - }) - let element - - switch(e.key) { - case 'Escape': - this.props.onClose() - break - case 'Enter': - this.handleClick(e) - break - case 'ArrowDown': - element = this.node.childNodes[index + 1] - if (element) { - element.focus() - this.props.onChange(element.getAttribute('data-index')) - } - break - case 'ArrowUp': - element = this.node.childNodes[index - 1] - if (element) { - element.focus() - this.props.onChange(element.getAttribute('data-index')) - } - break - case 'Home': - element = this.node.firstChild - if (element) { - element.focus() - this.props.onChange(element.getAttribute('data-index')) - } - break - case 'End': - element = this.node.lastChild - if (element) { - element.focus() - this.props.onChange(element.getAttribute('data-index')) - } - break - } - } - - handleClick = e => { - const value = e.currentTarget.getAttribute('data-index') - - e.preventDefault() - - this.props.onClose() - this.props.onChange(value) - } - - componentDidMount () { - document.addEventListener('click', this.handleDocumentClick, false) - document.addEventListener('touchend', this.handleDocumentClick, listenerOptions) - if (this.focusedItem) this.focusedItem.focus() - this.setState({ mounted: true }) - } - - componentWillUnmount () { - document.removeEventListener('click', this.handleDocumentClick, false) - document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions) - } - - setRef = c => { - this.node = c - } - - setFocusRef = c => { - this.focusedItem = c - } - - render () { - const { mounted } = this.state - const { style, items, placement, value } = this.props - - return ( - - {({ opacity, scaleX, scaleY }) => ( - // It should not be transformed when mounting because the resulting - // size will be used to determine the coordinate of the menu by - // react-overlays -
- {items.map(item => ( -
-
- -
- -
- {item.text} - {item.meta} -
-
- ))} -
- )} -
- ) - } - -} - -const mapStateToProps = state => ({ - isModalOpen: state.get('modal').modalType === 'ACTIONS', - value: state.getIn(['compose', 'privacy']), -}) - -const mapDispatchToProps = dispatch => ({ - - onChange (value) { - dispatch(changeComposeVisibility(value)) - }, - - isUserTouching, - onModalOpen: props => dispatch(openModal('ACTIONS', props)), - onModalClose: () => dispatch(closeModal()), - -}) - -export default -@connect(mapStateToProps, mapDispatchToProps) -@injectIntl -class PrivacyDropdown extends PureComponent { - - static propTypes = { - isUserTouching: PropTypes.func, - isModalOpen: PropTypes.bool.isRequired, - onModalOpen: PropTypes.func, - onModalClose: PropTypes.func, - value: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, - } - - state = { - open: false, - placement: 'bottom', - } - - handleToggle = ({ target }) => { - if (this.props.isUserTouching()) { - if (this.state.open) { - this.props.onModalClose() - } else { - this.props.onModalOpen({ - actions: this.options.map(option => ({ ...option, active: option.value === this.props.value })), - onClick: this.handleModalActionClick, - }) - } - } else { - const { top } = target.getBoundingClientRect() - this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' }) - this.setState({ open: !this.state.open }) - } - } - - handleModalActionClick = (e) => { - e.preventDefault() - - const { value } = this.options[e.currentTarget.getAttribute('data-index')] - - this.props.onModalClose() - this.props.onChange(value) - } - - handleKeyDown = e => { - switch(e.key) { - case 'Escape': - this.handleClose() - break - } - } - - handleClose = () => { - this.setState({ open: false }) - } - - handleChange = value => { - this.props.onChange(value) - } - - componentWillMount () { - const { intl: { formatMessage } } = this.props - - this.options = [ - { icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) }, - { icon: 'unlock', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) }, - { icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) }, - ] - } - - render () { - const { value, intl } = this.props - const { open, placement } = this.state - - const valueOption = this.options.find(item => item.value === value) - - return ( - - ) - } - -} diff --git a/app/javascript/gabsocial/components/popover/status_visibility_popover.js b/app/javascript/gabsocial/components/popover/status_visibility_popover.js index daa5a774..bcf69b51 100644 --- a/app/javascript/gabsocial/components/popover/status_visibility_popover.js +++ b/app/javascript/gabsocial/components/popover/status_visibility_popover.js @@ -1,9 +1,129 @@ -export default class StatusVisibilityPopover extends PureComponent { - render() { +import { injectIntl, defineMessages } from 'react-intl' +import classNames from 'classnames/bind' +import { changeComposeVisibility } from '../../actions/compose' +import { closePopover } from '../../actions/popover' +import PopoverLayout from './popover_layout' +import Icon from '../icon' +import Text from '../text' + +const cx = classNames.bind(_s) + +const messages = defineMessages({ + public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, + public_long: { id: 'privacy.public.long', defaultMessage: 'Post to public timelines' }, + unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' }, + unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Do not show in public timelines' }, + private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' }, + private_long: { id: 'privacy.private.long', defaultMessage: 'Post to followers only' }, + change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' }, + visibility: { id: 'privacy.visibility', defaultMessage: 'Visibility' }, +}) + +const mapStateToProps = state => ({ + value: state.getIn(['compose', 'privacy']), +}) + +const mapDispatchToProps = dispatch => ({ + + onChange (value) { + dispatch(changeComposeVisibility(value)) + dispatch(closePopover()) + }, + +}) + +export default +@connect(mapStateToProps, mapDispatchToProps) +@injectIntl +class StatusVisibilityDropdown extends PureComponent { + + static propTypes = { + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + } + + handleChange = value => { + this.props.onChange(value) + } + + componentWillMount () { + const { intl } = this.props + + this.options = [ + { + icon: 'globe', + value: 'public', + title: intl.formatMessage(messages.public_short), + subtitle: intl.formatMessage(messages.public_long) + }, + { + icon: 'unlock', + value: 'unlisted', + title: intl.formatMessage(messages.unlisted_short), + subtitle: intl.formatMessage(messages.unlisted_long) + }, + { + icon: 'lock', + value: 'private', + title: intl.formatMessage(messages.private_short), + subtitle: intl.formatMessage(messages.private_long) + }, + ] + } + + render () { + const { value } = this.props + return ( -
- { /* */ } -
+ +
+ { + this.options.map((option, i) => { + const isActive = option.value === value + const isLast = i === this.options.length - 1 + + const containerClasses = cx({ + default: 1, + flexRow: 1, + py10: 1, + cursorPointer: 1, + borderBottom1PX: !isLast, + borderColorSecondary: !isLast, + backgroundSubtle_onHover: !isActive, + backgroundColorBrand: isActive, + }) + + const iconClasses = cx({ + ml10: 1, + mt2: 1, + fillColorWhite: isActive, + }) + + return ( +
this.handleChange(option.value)} + className={containerClasses} + > + +
+ + {option.title} + + + {option.subtitle} + +
+
+ ) + }) + } +
+
) } -} \ No newline at end of file + +} + + diff --git a/app/javascript/gabsocial/components/profile_header.js b/app/javascript/gabsocial/components/profile_header.js index 21da7138..9c29766c 100644 --- a/app/javascript/gabsocial/components/profile_header.js +++ b/app/javascript/gabsocial/components/profile_header.js @@ -242,6 +242,8 @@ class ProfileHeader extends ImmutablePureComponent { console.log("buttonOptions:", buttonText, buttonOptions) + // : todo : "follows you", "mutual follow" + return (
diff --git a/app/javascript/gabsocial/components/sidebar_section_item.js b/app/javascript/gabsocial/components/sidebar_section_item.js index 6581659c..3ea637b5 100644 --- a/app/javascript/gabsocial/components/sidebar_section_item.js +++ b/app/javascript/gabsocial/components/sidebar_section_item.js @@ -103,7 +103,7 @@ export default class SidebarSectionItem extends PureComponent { buttonRef={buttonRef} onMouseEnter={() => this.handleOnMouseEnter()} onMouseLeave={() => this.handleOnMouseLeave()} - className={[_s.default, _s.noUnderline, _s.cursorPointer, _s.width100PC, _s.alignItemsStart, _s.backgroundTransparent].join(' ')} + className={[_s.default, _s.noUnderline, _s.cursorPointer, _s.width100PC, _s.backgroundTransparent].join(' ')} >
diff --git a/app/javascript/gabsocial/features/status/components/card/card.js b/app/javascript/gabsocial/components/status_card.js similarity index 99% rename from app/javascript/gabsocial/features/status/components/card/card.js rename to app/javascript/gabsocial/components/status_card.js index 76f6c501..4d846903 100644 --- a/app/javascript/gabsocial/features/status/components/card/card.js +++ b/app/javascript/gabsocial/components/status_card.js @@ -3,7 +3,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import punycode from 'punycode'; import classnames from 'classnames'; -import Icon from '../../../../components/icon'; +import Icon from './icon'; const IDNA_PREFIX = 'xn--'; diff --git a/app/javascript/gabsocial/components/status_content/status_content.js b/app/javascript/gabsocial/components/status_content/status_content.js index b03aa7a1..737b16fc 100644 --- a/app/javascript/gabsocial/components/status_content/status_content.js +++ b/app/javascript/gabsocial/components/status_content/status_content.js @@ -285,12 +285,18 @@ class StatusContent extends ImmutablePureComponent { /> { this.state.collapsed && - + + {intl.formatMessage(messages.readMore)} + + }
) diff --git a/app/javascript/gabsocial/components/trends_panel_item.js b/app/javascript/gabsocial/components/trends_panel_item.js index d31360ed..8ba5c3f4 100644 --- a/app/javascript/gabsocial/components/trends_panel_item.js +++ b/app/javascript/gabsocial/components/trends_panel_item.js @@ -15,6 +15,7 @@ const cx = classNames.bind(_s) export default class TrendingItem extends ImmutablePureComponent { static propTypes = { + index: PropTypes.number, trend: ImmutablePropTypes.map.isRequired, } @@ -31,7 +32,7 @@ export default class TrendingItem extends ImmutablePureComponent { } render() { - const { trend } = this.props + const { trend, index } = this.props const { hovering } = this.state const subtitleClasses = cx({ @@ -44,7 +45,7 @@ export default class TrendingItem extends ImmutablePureComponent { underline: hovering, }) - return null; + // return null; // : todo : @@ -56,7 +57,7 @@ export default class TrendingItem extends ImmutablePureComponent { onMouseLeave={() => this.handleOnMouseLeave()} >
- 1 + {index} Politics
diff --git a/app/javascript/gabsocial/components/trends_panel_item_card.js b/app/javascript/gabsocial/components/trends_panel_item_card.js index 6aba9282..d9d44ff6 100644 --- a/app/javascript/gabsocial/components/trends_panel_item_card.js +++ b/app/javascript/gabsocial/components/trends_panel_item_card.js @@ -58,7 +58,7 @@ export default class TrendingItemCard extends ImmutablePureComponent { The best flower subscription services: BloomsyBox, Bouqs...
- +
) } diff --git a/app/javascript/gabsocial/features/video/video.js b/app/javascript/gabsocial/components/video.js similarity index 99% rename from app/javascript/gabsocial/features/video/video.js rename to app/javascript/gabsocial/components/video.js index 1d34413b..ca6d8234 100644 --- a/app/javascript/gabsocial/features/video/video.js +++ b/app/javascript/gabsocial/components/video.js @@ -452,6 +452,8 @@ class Video extends PureComponent { // fullscreen // })} + // : todo spoiler : + const seekHandleClasses = cx({ default: 1, positionAbsolute: 1, diff --git a/app/javascript/gabsocial/features/account_timeline/account_timeline.js b/app/javascript/gabsocial/features/account_timeline.js similarity index 95% rename from app/javascript/gabsocial/features/account_timeline/account_timeline.js rename to app/javascript/gabsocial/features/account_timeline.js index f54305bc..707c0339 100644 --- a/app/javascript/gabsocial/features/account_timeline/account_timeline.js +++ b/app/javascript/gabsocial/features/account_timeline.js @@ -2,9 +2,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePureComponent from 'react-immutable-pure-component' import { List as ImmutableList } from 'immutable' import { injectIntl, defineMessages } from 'react-intl' -import { expandAccountFeaturedTimeline, expandAccountTimeline } from '../../actions/timelines' -import { fetchAccountIdentityProofs } from '../../actions/identity_proofs' -import StatusList from '../../components/status_list' +import { expandAccountFeaturedTimeline, expandAccountTimeline } from '../actions/timelines' +import { fetchAccountIdentityProofs } from '../actions/identity_proofs' +import StatusList from '../components/status_list' const messages = defineMessages({ empty: { id: 'empty_column.account_timeline', defaultMessage: 'No gabs here!' }, diff --git a/app/javascript/gabsocial/features/account_timeline/components/header/header.js b/app/javascript/gabsocial/features/account_timeline/components/header/header.js deleted file mode 100644 index 5f210f5a..00000000 --- a/app/javascript/gabsocial/features/account_timeline/components/header/header.js +++ /dev/null @@ -1,105 +0,0 @@ -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import InnerHeader from '../inner_header'; -import MovedNote from '../moved_note'; - -export default class Header extends ImmutablePureComponent { - - static propTypes = { - account: ImmutablePropTypes.map, - identity_proofs: ImmutablePropTypes.list, - onFollow: PropTypes.func.isRequired, - onBlock: PropTypes.func.isRequired, - onMention: PropTypes.func.isRequired, - onRepostToggle: PropTypes.func.isRequired, - onReport: PropTypes.func.isRequired, - onMute: PropTypes.func.isRequired, - onBlockDomain: PropTypes.func.isRequired, - onUnblockDomain: PropTypes.func.isRequired, - onEndorseToggle: PropTypes.func.isRequired, - onAddToList: PropTypes.func.isRequired, - domain: PropTypes.string.isRequired, - username: PropTypes.string, - }; - - static contextTypes = { - router: PropTypes.object, - }; - - handleFollow = () => { - this.props.onFollow(this.props.account); - } - - handleBlock = () => { - this.props.onBlock(this.props.account); - } - - handleMention = () => { - this.props.onMention(this.props.account, this.context.router.history); - } - - handleReport = () => { - this.props.onReport(this.props.account); - } - - handleRepostToggle = () => { - this.props.onRepostToggle(this.props.account); - } - - handleMute = () => { - this.props.onMute(this.props.account); - } - - handleBlockDomain = () => { - const domain = this.props.account.get('acct').split('@')[1]; - - if (!domain) return; - - this.props.onBlockDomain(domain); - } - - handleUnblockDomain = () => { - const domain = this.props.account.get('acct').split('@')[1]; - - if (!domain) return; - - this.props.onUnblockDomain(domain); - } - - handleEndorseToggle = () => { - this.props.onEndorseToggle(this.props.account); - } - - handleAddToList = () => { - this.props.onAddToList(this.props.account); - } - - render () { - const { account, identity_proofs } = this.props; - const moved = (account) ? account.get('moved') : false; - - return ( -
- { moved && } - - -
- ); - } - -} diff --git a/app/javascript/gabsocial/features/account_timeline/components/header/header.scss b/app/javascript/gabsocial/features/account_timeline/components/header/header.scss deleted file mode 100644 index d135b43b..00000000 --- a/app/javascript/gabsocial/features/account_timeline/components/header/header.scss +++ /dev/null @@ -1,6 +0,0 @@ -.account-timeline { - &__header { - display: block; - width: 100%; - } -} diff --git a/app/javascript/gabsocial/features/account_timeline/components/header/index.js b/app/javascript/gabsocial/features/account_timeline/components/header/index.js deleted file mode 100644 index 08b400a2..00000000 --- a/app/javascript/gabsocial/features/account_timeline/components/header/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './header' \ No newline at end of file diff --git a/app/javascript/gabsocial/features/account_timeline/components/inner_header/index.js b/app/javascript/gabsocial/features/account_timeline/components/inner_header/index.js deleted file mode 100644 index 052b8042..00000000 --- a/app/javascript/gabsocial/features/account_timeline/components/inner_header/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './inner_header' diff --git a/app/javascript/gabsocial/features/account_timeline/components/inner_header/inner_header.js b/app/javascript/gabsocial/features/account_timeline/components/inner_header/inner_header.js deleted file mode 100644 index 73940577..00000000 --- a/app/javascript/gabsocial/features/account_timeline/components/inner_header/inner_header.js +++ /dev/null @@ -1,337 +0,0 @@ -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; -import classNames from 'classnames'; -import { NavLink } from 'react-router-dom'; -import { debounce } from 'lodash'; -import Button from '../../../components/button'; -import { autoPlayGif, me, isStaff } from '../../../initial_state'; -import Avatar from '../../../components/avatar'; -import { shortNumberFormat } from '../../../utils/numbers'; -import ProfileInfoPanel from './profile_info_panel/profile_info_panel'; - -const messages = defineMessages({ - unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, - follow: { id: 'account.follow', defaultMessage: 'Follow' }, - requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' }, - unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, - edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, - linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' }, - account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' }, - mention: { id: 'account.mention', defaultMessage: 'Mention' }, - unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, - block: { id: 'account.block', defaultMessage: 'Block @{name}' }, - mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' }, - report: { id: 'account.report', defaultMessage: 'Report @{name}' }, - share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' }, - media: { id: 'account.media', defaultMessage: 'Media' }, - blockDomain: { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' }, - unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' }, - hideReposts: { id: 'account.hide_reblogs', defaultMessage: 'Hide reposts from @{name}' }, - showReposts: { id: 'account.show_reblogs', defaultMessage: 'Show reposts from @{name}' }, - preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, - follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, - blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, - domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' }, - mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, - endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' }, - unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' }, - admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, - add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' }, - accountFollowsYou: { id: 'account.follows_you', defaultMessage: 'Follows you' }, - accountBlocked: { id: 'account.blocked', defaultMessage: 'Blocked' }, - accountMuted: { id: 'account.muted', defaultMessage: 'Muted' }, - domainBlocked: { id: 'account.domain_blocked', defaultMessage: 'Domain hidden' }, -}); - -export default -@injectIntl -class Header extends ImmutablePureComponent { - - static propTypes = { - account: ImmutablePropTypes.map, - identity_props: ImmutablePropTypes.list, - onFollow: PropTypes.func.isRequired, - onBlock: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, - domain: PropTypes.string.isRequired, - username: PropTypes.string, - }; - - state = { - isSmallScreen: (window.innerWidth <= 895), - } - - openEditProfile = () => { - window.open('/settings/profile', '_blank'); - } - - isStatusesPageActive = (match, location) => { - if (!match) { - return false; - } - - return !location.pathname.match(/\/(followers|following|favorites|pins)\/?$/); - } - - componentWillMount () { - window.addEventListener('resize', this.handleResize, { passive: true }); - } - - componentWillUnmount () { - window.removeEventListener('resize', this.handleResize); - } - - onChat = () => { - const { account } = this.props; - - axios.post('https://chat.gab.com/private-message', { - username: account.get('username'), - }) - .then(function (response) { - console.log(response); - }) - .catch(function (error) { - console.log(error); - }); - } - - handleResize = debounce(() => { - this.setState({ isSmallScreen: (window.innerWidth <= 895) }); - }, 5, { - trailing: true, - }); - - makeMenu() { - const { account, intl, domain } = this.props; - - let menu = []; - - if (!account || !me) { - return []; - } - - if ('share' in navigator) { - menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare }); - menu.push(null); - } - - if (account.get('id') === me) { - menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' }); - menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' }); - menu.push(null); - menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' }); - menu.push(null); - menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); - menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); - menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }); - } else { - menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('acct') }), action: this.props.onMention }); - - if (account.getIn(['relationship', 'following'])) { - if (account.getIn(['relationship', 'showing_reblogs'])) { - menu.push({ text: intl.formatMessage(messages.hideReposts, { name: account.get('username') }), action: this.props.onRepostToggle }); - } else { - menu.push({ text: intl.formatMessage(messages.showReposts, { name: account.get('username') }), action: this.props.onRepostToggle }); - } - - menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList }); - menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle }); - menu.push(null); - } - - if (account.getIn(['relationship', 'muting'])) { - menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.props.onMute }); - } else { - menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute }); - } - - if (account.getIn(['relationship', 'blocking'])) { - menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock }); - } else { - menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock }); - } - - menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport }); - } - - if (account.get('acct') !== account.get('username')) { - const domain = account.get('acct').split('@')[1]; - - menu.push(null); - - if (account.getIn(['relationship', 'domain_blocking'])) { - menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.props.onUnblockDomain }); - } else { - menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.props.onBlockDomain }); - } - } - - if (account.get('id') !== me && isStaff) { - menu.push(null); - menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` }); - } - - return menu; - } - - makeInfo() { - const { account, intl } = this.props; - - let info = []; - - if (!account || !me) return info; - - if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) { - info.push({intl.formatMessage(messages.accountFollowsYou)}); - } else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) { - info.push({intl.formatMessage(messages.accountBlocked)}); - } - - if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) { - info.push({intl.formatMessage(messages.accountMuted)}); - } else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) { - info.push({intl.formatMessage(messages.domainBlocked)}); - } - - return info; - }; - - getActionBtn() { - const { account, intl } = this.props; - - let actionBtn = null; - - if (!account || !me) return actionBtn; - - if (me !== account.get('id')) { - if (!account.get('relationship')) { // Wait until the relationship is loaded - // - } else if (account.getIn(['relationship', 'requested'])) { - actionBtn = - } - {/**/} -
- } - - - - - ); - } - -} diff --git a/app/javascript/gabsocial/features/account_timeline/components/inner_header/inner_header.scss b/app/javascript/gabsocial/features/account_timeline/components/inner_header/inner_header.scss deleted file mode 100644 index 45082120..00000000 --- a/app/javascript/gabsocial/features/account_timeline/components/inner_header/inner_header.scss +++ /dev/null @@ -1,17 +0,0 @@ -.relationship-tag { - display: block; - margin-bottom: 4px; - vertical-align: top; - color: $primary-text-color; - background-color: $base-overlay-background; - text-transform: uppercase; - padding: 4px; - border-radius: 4px; - opacity: 0.7; - - @include text-sizing(11px, 500); - - &:hover { - opacity: 1; - } -} \ No newline at end of file diff --git a/app/javascript/gabsocial/features/account_timeline/components/moved_note/index.js b/app/javascript/gabsocial/features/account_timeline/components/moved_note/index.js deleted file mode 100644 index 5173652d..00000000 --- a/app/javascript/gabsocial/features/account_timeline/components/moved_note/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './moved_note' diff --git a/app/javascript/gabsocial/features/account_timeline/components/moved_note/moved_note.scss b/app/javascript/gabsocial/features/account_timeline/components/moved_note/moved_note.scss deleted file mode 100644 index 6dda86be..00000000 --- a/app/javascript/gabsocial/features/account_timeline/components/moved_note/moved_note.scss +++ /dev/null @@ -1,55 +0,0 @@ -.moved-note { - padding: 14px 10px 16px 10px; - background: lighten($ui-base-color, 4%); - border-top: 1px solid lighten($ui-base-color, 8%); - border-bottom: 1px solid lighten($ui-base-color, 8%); - - &__message { - position: relative; - margin-left: 58px; - color: $dark-text-color; - padding-bottom: 4px; - font-size: 14px; - - >span { - display: block; - - @include text-overflow; - } - } - - &__icon-wrapper { - left: -26px; - position: absolute; - } - - &__display-name { - display: block; - color: $secondary-text-color; - margin-bottom: 0; - line-height: 24px; - overflow: hidden; - - strong, - span { - display: block; - - @include text-overflow; - } - - strong { - font-size: 16px; - color: $primary-text-color; - } - - &:hover strong { - text-decoration: underline; - } - } - - &__display-avatar { - position: relative; - float: left; - margin-right: 10px; - } -} \ No newline at end of file diff --git a/app/javascript/gabsocial/features/account_timeline/containers/header_container.js b/app/javascript/gabsocial/features/account_timeline/containers/header_container.js deleted file mode 100644 index bea9a37e..00000000 --- a/app/javascript/gabsocial/features/account_timeline/containers/header_container.js +++ /dev/null @@ -1,117 +0,0 @@ -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; -import { List as ImmutableList } from 'immutable'; -import { - followAccount, - unfollowAccount, - blockAccount, - unblockAccount, - unmuteAccount, - pinAccount, - unpinAccount, -} from '../../../actions/accounts'; -import { - mentionCompose, -} from '../../../actions/compose'; -import { initMuteModal } from '../../../actions/mutes'; -import { initReport } from '../../../actions/reports'; -import { openModal } from '../../../actions/modal'; -import { blockDomain, unblockDomain } from '../../../actions/domain_blocks'; -import { unfollowModal } from '../../../initial_state'; -import { makeGetAccount } from '../../../selectors'; -import Header from '../components/header'; - -const messages = defineMessages({ - unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' }, - blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' }, - blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' }, -}); - -const makeMapStateToProps = () => { - const getAccount = makeGetAccount(); - - const mapStateToProps = (state, { accountId }) => ({ - account: getAccount(state, accountId), - domain: state.getIn(['meta', 'domain']), - identity_proofs: state.getIn(['identity_proofs', accountId], ImmutableList()), - }); - - return mapStateToProps; -}; - -const mapDispatchToProps = (dispatch, { intl }) => ({ - - onFollow (account) { - if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) { - if (unfollowModal) { - dispatch(openModal('UNFOLLOW', { - accountId: account.get('id'), - })) - } else { - dispatch(unfollowAccount(account.get('id'))) - } - } else { - dispatch(followAccount(account.get('id'))) - } - }, - - onBlock (account) { - if (account.getIn(['relationship', 'blocking'])) { - dispatch(unblockAccount(account.get('id'))); - } else { - dispatch(openModal('BLOCK_ACCOUNT', { - accountId: account.get('id'), - })); - } - }, - - onMention (account, router) { - dispatch(mentionCompose(account, router)); - }, - - onRepostToggle (account) { - if (account.getIn(['relationship', 'showing_reblogs'])) { - dispatch(followAccount(account.get('id'), false)); - } else { - dispatch(followAccount(account.get('id'), true)); - } - }, - - onEndorseToggle (account) { - if (account.getIn(['relationship', 'endorsed'])) { - dispatch(unpinAccount(account.get('id'))); - } else { - dispatch(pinAccount(account.get('id'))); - } - }, - - onReport (account) { - dispatch(initReport(account)); - }, - - onMute (account) { - if (account.getIn(['relationship', 'muting'])) { - dispatch(unmuteAccount(account.get('id'))); - } else { - dispatch(initMuteModal(account)); - } - }, - - onBlockDomain (domain) { - dispatch(openModal('BLOCK_DOMAIN', { - domain, - })); - }, - - onUnblockDomain (domain) { - dispatch(unblockDomain(domain)); - }, - - onAddToList(account){ - dispatch(openModal('LIST_ADDER', { - accountId: account.get('id'), - })); - }, - -}); - -export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header)); diff --git a/app/javascript/gabsocial/features/account_timeline/index.js b/app/javascript/gabsocial/features/account_timeline/index.js deleted file mode 100644 index 48037c10..00000000 --- a/app/javascript/gabsocial/features/account_timeline/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './account_timeline' \ No newline at end of file diff --git a/app/javascript/gabsocial/features/blocks.js b/app/javascript/gabsocial/features/blocked_accounts.js similarity index 100% rename from app/javascript/gabsocial/features/blocks.js rename to app/javascript/gabsocial/features/blocked_accounts.js diff --git a/app/javascript/gabsocial/features/domain_blocks.js b/app/javascript/gabsocial/features/blocked_domains.js similarity index 100% rename from app/javascript/gabsocial/features/domain_blocks.js rename to app/javascript/gabsocial/features/blocked_domains.js diff --git a/app/javascript/gabsocial/features/compose/components/compose_extra_button.js b/app/javascript/gabsocial/features/compose/components/compose_extra_button.js index 183bc0f1..d3eef03c 100644 --- a/app/javascript/gabsocial/features/compose/components/compose_extra_button.js +++ b/app/javascript/gabsocial/features/compose/components/compose_extra_button.js @@ -11,6 +11,7 @@ export default class ComposeExtraButton extends PureComponent { icon: PropTypes.string, small: PropTypes.bool, active: PropTypes.bool, + buttonRef: PropTypes.func, } render() { @@ -21,16 +22,12 @@ export default class ComposeExtraButton extends PureComponent { icon, children, small, - active + active, + buttonRef } = this.props - const containerClasses = cx({ - default: 1, - mr10: !small, - mr2: small, - }) - const btnClasses = cx({ + backgroundSubtle_onHover: 1, backgroundColorBrandLight: active, py10: !small, px10: !small, @@ -43,16 +40,16 @@ export default class ComposeExtraButton extends PureComponent { fillColorWhite: active, }) - const iconSize = !!small ? '12px' : '18px' + const iconSize = !!small ? '12px' : '16px' return ( -
+
- { /* -
- -
*/ } -
+ { !edit && } { !shouldCondense && - + } +
diff --git a/app/javascript/gabsocial/features/compose/components/emoji_picker_button.js b/app/javascript/gabsocial/features/compose/components/emoji_picker_button.js index dc73f3a2..34727aff 100644 --- a/app/javascript/gabsocial/features/compose/components/emoji_picker_button.js +++ b/app/javascript/gabsocial/features/compose/components/emoji_picker_button.js @@ -1,6 +1,11 @@ -import { addPoll, removePoll } from '../../../actions/compose' +import { defineMessages, injectIntl } from 'react-intl' +import { openPopover } from '../../../actions/popover' import ComposeExtraButton from './compose_extra_button' +const messages = defineMessages({ + emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' }, +}) + const mapStateToProps = state => ({ // unavailable: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 0), // active: state.getIn(['compose', 'poll']) !== null, @@ -8,46 +13,47 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ - onClick() { - dispatch((_, getState) => { - if (getState().getIn(['compose', 'poll'])) { - dispatch(removePoll()) - } else { - dispatch(addPoll()) - } - }) + onClick(targetRef) { + dispatch(openPopover('EMOJI_PICKER', { + targetRef, + })) }, }) export default +@injectIntl @connect(mapStateToProps, mapDispatchToProps) class EmojiPickerButton extends PureComponent { static propTypes = { - disabled: PropTypes.bool, - unavailable: PropTypes.bool, - active: PropTypes.bool, + intl: PropTypes.object.isRequired, onClick: PropTypes.func.isRequired, + active: PropTypes.bool, small: PropTypes.bool, } handleClick = () => { - this.props.onClick() + this.props.onClick(this.button) + } + + setButton = (n) => { + this.button = n } render() { - const { active, unavailable, disabled, small } = this.props + const { active, small, intl } = this.props - if (unavailable) return null + const title = intl.formatMessage(messages.emoji) return ( ) } diff --git a/app/javascript/gabsocial/features/compose/components/gif_selector_button.js b/app/javascript/gabsocial/features/compose/components/gif_selector_button.js new file mode 100644 index 00000000..a74a1030 --- /dev/null +++ b/app/javascript/gabsocial/features/compose/components/gif_selector_button.js @@ -0,0 +1,53 @@ +import { injectIntl, defineMessages } from 'react-intl' +import { changeComposeSpoilerness } from '../../../actions/compose' +import ComposeExtraButton from './compose_extra_button' + +const messages = defineMessages({ + marked: { id: 'compose_form.spoiler.marked', defaultMessage: 'Text is hidden behind warning' }, + unmarked: { id: 'compose_form.spoiler.unmarked', defaultMessage: 'Text is not hidden' }, + title: { id: 'compose_form.spoiler.title', defaultMessage: 'Warning' }, +}) + +const mapStateToProps = (state) => ({ + active: state.getIn(['compose', 'spoiler']), +}) + +const mapDispatchToProps = dispatch => ({ + + onClick () { + dispatch(changeComposeSpoilerness()) + }, + +}) + +export default +@injectIntl +@connect(mapStateToProps, mapDispatchToProps) +class SpoilerButton extends PureComponent { + + static propTypes = { + active: PropTypes.bool, + intl: PropTypes.object.isRequired, + small: PropTypes.bool, + } + + handleClick = (e) => { + e.preventDefault() + this.props.onClick() + } + + render () { + const { active, intl, small } = this.props + + return ( + + ) + } + +} diff --git a/app/javascript/gabsocial/features/compose/components/post_privacy_button.js b/app/javascript/gabsocial/features/compose/components/post_privacy_button.js deleted file mode 100644 index 4f9f78fd..00000000 --- a/app/javascript/gabsocial/features/compose/components/post_privacy_button.js +++ /dev/null @@ -1,65 +0,0 @@ -import { defineMessages, injectIntl } from 'react-intl' -import { addPoll, removePoll } from '../../../actions/compose' -import ComposeExtraButton from './compose_extra_button' - -const messages = defineMessages({ - add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add poll' }, - title: { id: 'poll_button.title', defaultMessage: 'Poll' }, - remove_poll: { id: 'poll_button.remove_poll', defaultMessage: 'Remove poll' }, -}) - -const mapStateToProps = state => ({ - unavailable: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 0), - active: state.getIn(['compose', 'poll']) !== null, -}) - -const mapDispatchToProps = dispatch => ({ - - onClick() { - dispatch((_, getState) => { - if (getState().getIn(['compose', 'poll'])) { - dispatch(removePoll()) - } else { - dispatch(addPoll()) - } - }) - }, - -}) - -export default -@connect(mapStateToProps, mapDispatchToProps) -@injectIntl -class PollButton extends PureComponent { - - static propTypes = { - disabled: PropTypes.bool, - unavailable: PropTypes.bool, - active: PropTypes.bool, - onClick: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, - small: PropTypes.bool, - } - - handleClick = () => { - this.props.onClick() - } - - render() { - const { intl, active, unavailable, disabled, small } = this.props - - if (unavailable) return null - - return ( - - ) - } - -} diff --git a/app/javascript/gabsocial/features/compose/components/rich_text_editor_button.js b/app/javascript/gabsocial/features/compose/components/rich_text_editor_button.js new file mode 100644 index 00000000..f4aa434a --- /dev/null +++ b/app/javascript/gabsocial/features/compose/components/rich_text_editor_button.js @@ -0,0 +1,53 @@ +import { injectIntl, defineMessages } from 'react-intl' +import { changeComposeSpoilerness } from '../../../actions/compose' +import ComposeExtraButton from './compose_extra_button' + +const messages = defineMessages({ + marked: { id: 'compose_form.spoiler.marked', defaultMessage: 'Text is hidden behind warning' }, + unmarked: { id: 'compose_form.spoiler.unmarked', defaultMessage: 'Text is not hidden' }, + title: { id: 'compose_form.spoiler.title', defaultMessage: 'Warning' }, +}) + +const mapStateToProps = (state) => ({ + active: state.getIn(['compose', 'spoiler']), +}) + +const mapDispatchToProps = dispatch => ({ + + onClick () { + dispatch(changeComposeSpoilerness()) + }, + +}) + +export default +@injectIntl +@connect(mapStateToProps, mapDispatchToProps) +class SpoilerButton extends PureComponent { + + static propTypes = { + active: PropTypes.bool, + intl: PropTypes.object.isRequired, + small: PropTypes.bool, + } + + handleClick = (e) => { + e.preventDefault() + this.props.onClick() + } + + render () { + const { active, intl, small } = this.props + + return ( + + ) + } + +} diff --git a/app/javascript/gabsocial/features/compose/components/status_visibility_button.js b/app/javascript/gabsocial/features/compose/components/status_visibility_button.js new file mode 100644 index 00000000..eedfab3a --- /dev/null +++ b/app/javascript/gabsocial/features/compose/components/status_visibility_button.js @@ -0,0 +1,72 @@ +import { defineMessages, injectIntl } from 'react-intl' +import { openPopover } from '../../../actions/popover' +import ComposeExtraButton from './compose_extra_button' + +const messages = defineMessages({ + visibility: { id: 'privacy.visibility', defaultMessage: 'Visibility' }, +}) + +const mapStateToProps = (state) => ({ + value: state.getIn(['compose', 'privacy']), +}) + +const mapDispatchToProps = (dispatch) => ({ + onOpenPopover(targetRef) { + dispatch(openPopover('STATUS_VISIBILITY', { + targetRef, + })) + }, +}) + +export default +@connect(mapStateToProps, mapDispatchToProps) +@injectIntl +class StatusVisibilityButton extends PureComponent { + + static propTypes = { + onClick: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + small: PropTypes.bool, + onOpenPopover: PropTypes.func.isRequired, + } + + handleOnClick = () => { + this.props.onOpenPopover(this.button) + } + + setButton = (n) => { + this.button = n + } + + render() { + const { + intl, + small, + value + } = this.props + + let icon; + switch (value) { + case 'unlisted': + icon = 'unlock' + break; + case 'private': + icon = 'lock' + break; + default: + icon = 'globe' + break; + } + + return ( + + ) + } + +} diff --git a/app/javascript/gabsocial/features/compose/containers/emoji_picker_dropdown_container.js b/app/javascript/gabsocial/features/compose/containers/emoji_picker_dropdown_container.js deleted file mode 100644 index 8e879f07..00000000 --- a/app/javascript/gabsocial/features/compose/containers/emoji_picker_dropdown_container.js +++ /dev/null @@ -1,82 +0,0 @@ -import EmojiPickerDropdown from '../components/emoji_picker_dropdown'; -import { changeSetting } from '../../../actions/settings'; -import { createSelector } from 'reselect'; -import { Map as ImmutableMap } from 'immutable'; -import { useEmoji } from '../../../actions/emojis'; - -const perLine = 8; -const lines = 2; - -const DEFAULTS = [ - '+1', - 'grinning', - 'kissing_heart', - 'heart_eyes', - 'laughing', - 'stuck_out_tongue_winking_eye', - 'sweat_smile', - 'joy', - 'yum', - 'disappointed', - 'thinking_face', - 'weary', - 'sob', - 'sunglasses', - 'heart', - 'ok_hand', -]; - -const getFrequentlyUsedEmojis = createSelector([ - state => state.getIn(['settings', 'frequentlyUsedEmojis'], ImmutableMap()), -], emojiCounters => { - let emojis = emojiCounters - .keySeq() - .sort((a, b) => emojiCounters.get(a) - emojiCounters.get(b)) - .reverse() - .slice(0, perLine * lines) - .toArray(); - - if (emojis.length < DEFAULTS.length) { - let uniqueDefaults = DEFAULTS.filter(emoji => !emojis.includes(emoji)); - emojis = emojis.concat(uniqueDefaults.slice(0, DEFAULTS.length - emojis.length)); - } - - return emojis; -}); - -const getCustomEmojis = createSelector([ - state => state.get('custom_emojis'), -], emojis => emojis.filter(e => e.get('visible_in_picker')).sort((a, b) => { - const aShort = a.get('shortcode').toLowerCase(); - const bShort = b.get('shortcode').toLowerCase(); - - if (aShort < bShort) { - return -1; - } else if (aShort > bShort ) { - return 1; - } else { - return 0; - } -})); - -const mapStateToProps = state => ({ - custom_emojis: getCustomEmojis(state), - skinTone: state.getIn(['settings', 'skinTone']), - frequentlyUsedEmojis: getFrequentlyUsedEmojis(state), -}); - -const mapDispatchToProps = (dispatch, { onPickEmoji }) => ({ - onSkinTone: skinTone => { - dispatch(changeSetting(['skinTone'], skinTone)); - }, - - onPickEmoji: emoji => { - dispatch(useEmoji(emoji)); - - if (onPickEmoji) { - onPickEmoji(emoji); - } - }, -}); - -export default connect(mapStateToProps, mapDispatchToProps)(EmojiPickerDropdown); diff --git a/app/javascript/gabsocial/features/groups_collection.js b/app/javascript/gabsocial/features/groups_collection.js index 2177e61a..776ee70f 100644 --- a/app/javascript/gabsocial/features/groups_collection.js +++ b/app/javascript/gabsocial/features/groups_collection.js @@ -30,19 +30,14 @@ class GroupsCollection extends ImmutablePureComponent { render() { const { groupIds } = this.props - + return (
- - { - groupIds.map((groupId, i) => ( - - )) - } - + { + groupIds.map((groupId, i) => ( + + )) + }
) } diff --git a/app/javascript/gabsocial/features/status/components/card/card.scss b/app/javascript/gabsocial/features/status/components/card/card.scss deleted file mode 100644 index 0e9f1f0d..00000000 --- a/app/javascript/gabsocial/features/status/components/card/card.scss +++ /dev/null @@ -1,169 +0,0 @@ -.status-card { - display: flex; - font-size: 14px; - color: $dark-text-color; - margin-top: 14px; - text-decoration: none; - overflow: hidden; - - @include border-design(lighten($ui-base-color, 8%), 1px, 4px); - - &__actions { - @include flex(center, center); - @include abs-position(0, 0, 0, 0); - - &>div { - background: rgba($base-shadow-color, 0.6); - border-radius: 8px; - padding: 12px 9px; - flex: 0 0 auto; - - @include flex(center, center); - } - - button, - a { - display: inline; - color: $secondary-text-color; - background: transparent; - border: 0; - padding: 0 8px; - text-decoration: none; - font-size: 18px; - line-height: 18px; - - &:hover, - &:active, - &:focus { - color: $primary-text-color; - } - } - - a { - font-size: 19px; - position: relative; - bottom: -1px; - } - } - - &__title { - display: block; - font-weight: 500; - margin-bottom: 5px; - color: $darker-text-color; - text-decoration: none; - - @include text-overflow(nowrap); - } - - &__content { - flex: 1 1 auto; - overflow: hidden; - padding: 14px 14px 14px 8px; - } - - &__description { - color: $darker-text-color; - } - - &__host { - display: block; - margin-top: 5px; - font-size: 13px; - - @include text-overflow(nowrap); - } - - &__image { - flex: 0 0 100px; - background: lighten($ui-base-color, 8%); - position: relative; - - &>.fa { - font-size: 21px; - position: absolute; - transform-origin: 50% 50%; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - } - - &__image-image { - border-radius: 4px 0 0 4px; - display: block; - margin: 0; - object-fit: cover; - background-size: cover; - background-position: center center; - - @include size(100%); - } - - &.horizontal { - display: block; - } - - &.horizontal & { - &__image { - width: 100%; - } - - &__image-image { - border-radius: 4px 4px 0 0; - } - - &__title { - white-space: inherit; - } - } - - &.compact { - border-color: lighten($ui-base-color, 4%); - - &.interactive { - border: 0; - } - } - - &.compact & { - &__content { - padding: 10px 8px 8px 8px; - } - - &__title { - white-space: nowrap; - } - - &__image { - flex: 0 0 60px; - } - } -} - -a.status-card { - cursor: pointer; - - &:hover { - background: lighten($ui-base-color, 8%); - } - - &.compact:hover { - background-color: lighten($ui-base-color, 4%); - } -} - -.status-card-photo { - cursor: zoom-in; - display: block; - text-decoration: none; - margin: 0; - - @include size(100%, auto); -} - -.status-card-video { - iframe { - @include size(100%); - } -} \ No newline at end of file diff --git a/app/javascript/gabsocial/features/status/components/card/index.js b/app/javascript/gabsocial/features/status/components/card/index.js deleted file mode 100644 index 827e4fde..00000000 --- a/app/javascript/gabsocial/features/status/components/card/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './card' \ No newline at end of file diff --git a/app/javascript/gabsocial/features/ui/util/async_components.js b/app/javascript/gabsocial/features/ui/util/async_components.js index 1aa9b154..42598188 100644 --- a/app/javascript/gabsocial/features/ui/util/async_components.js +++ b/app/javascript/gabsocial/features/ui/util/async_components.js @@ -7,7 +7,7 @@ export function AccountGallery() { } export function Blocks() { - return import(/* webpackChunkName: "features/blocks" */'../../blocks') + return import(/* webpackChunkName: "features/blocks" */'../../blocked_accounts') } export function CommunityTimeline() { @@ -19,7 +19,7 @@ export function Compose() { } export function DomainBlocks() { - return import(/* webpackChunkName: "features/domain_blocks" */'../../domain_blocks') + return import(/* webpackChunkName: "features/domain_blocks" */'../../blocked_domains') } export function EmbedModal() { diff --git a/app/javascript/gabsocial/features/video/index.js b/app/javascript/gabsocial/features/video/index.js deleted file mode 100644 index 9d40f37a..00000000 --- a/app/javascript/gabsocial/features/video/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './video' \ No newline at end of file diff --git a/app/javascript/gabsocial/features/video/video.scss b/app/javascript/gabsocial/features/video/video.scss deleted file mode 100644 index 6b39ad1f..00000000 --- a/app/javascript/gabsocial/features/video/video.scss +++ /dev/null @@ -1,71 +0,0 @@ -.video-player { - overflow: hidden; - position: relative; - background: $base-shadow-color; - max-width: 100%; - border-radius: 4px; - - &:focus { - outline: 0; - } - - video { - z-index: 1; - - @include max-size(100vw, 80vh); - } - - &.fullscreen { - margin: 0; - - @include size(100% !important); - - video { - @include max-size(100% !important); - @include size(100% !important); - } - } - - &.inactive { - - video, - .video-player__controls { - visibility: hidden; - } - } - - &__spoiler { - display: none; - z-index: 4; - border: 0; - background: $base-overlay-background; - color: $darker-text-color; - transition: none; - pointer-events: none; - - @include abs-position(0, auto, auto, 0); - @include size(100%); - - &.active { - display: block; - pointer-events: auto; - - &:hover, - &:active, - &:focus { - color: lighten($darker-text-color, 7%); - } - } - - &__title { - display: block; - font-size: 14px; - } - - &__subtitle { - display: block; - - @include text-sizing(11px, 500); - } - } -} \ No newline at end of file diff --git a/app/javascript/gabsocial/reducers/compose.js b/app/javascript/gabsocial/reducers/compose.js index 3e4719ae..8692f3b4 100644 --- a/app/javascript/gabsocial/reducers/compose.js +++ b/app/javascript/gabsocial/reducers/compose.js @@ -176,7 +176,7 @@ const insertEmoji = (state, position, emojiData, needsSpace) => { }; const privacyPreference = (a, b) => { - const order = ['public', 'unlisted', 'private', 'direct']; + const order = ['public', 'unlisted', 'private']; return order[Math.max(order.indexOf(a), order.indexOf(b), 0)]; }; diff --git a/app/javascript/styles/gabsocial/emoji_picker.scss b/app/javascript/styles/gabsocial/emoji_picker.scss index 94ea5976..6d99c714 100644 --- a/app/javascript/styles/gabsocial/emoji_picker.scss +++ b/app/javascript/styles/gabsocial/emoji_picker.scss @@ -15,7 +15,7 @@ } } -.emoji-mart-bar { +.emoji-mart-bar {{ border: 0 solid darken($ui-secondary-color, 8%); &:first-child {