Added Timeline Injections
• Added: - Timeline Injections - FeaturedGroupsInjection, GroupCategoriesInjection, ProUpgradeInjection, PWAInjection, ShopInjection, TimelineInjectionBase, TimelineInjectionLayout, TimelineInjectionRoot, UserSuggestionsInjection - Constants - Redux for timeline_injections - settings for setting - popover for dismissing and saving weight
This commit is contained in:
parent
41f48ea886
commit
d198695bdb
32
app/javascript/gabsocial/actions/timeline_injections.js
Normal file
32
app/javascript/gabsocial/actions/timeline_injections.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
TIMELINE_INJECTION_WEIGHT_DEFAULT,
|
||||||
|
TIMELINE_INJECTION_WEIGHT_MULTIPLIER,
|
||||||
|
TIMELINE_INJECTION_WEIGHT_SUBTRACTOR,
|
||||||
|
TIMELINE_INJECTION_WEIGHT_MIN,
|
||||||
|
} from '../constants'
|
||||||
|
import { changeSetting } from './settings'
|
||||||
|
|
||||||
|
export const TIMELINE_INJECTION_SHOW = 'TIMELINE_INJECTION_SHOW'
|
||||||
|
export const TIMELINE_INJECTION_HIDE = 'TIMELINE_INJECTION_HIDE'
|
||||||
|
|
||||||
|
export const showTimelineInjection = (injectionId) => (dispatch) => {
|
||||||
|
dispatch({
|
||||||
|
type: TIMELINE_INJECTION_SHOW,
|
||||||
|
injectionId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hideTimelineInjection = (injectionId) => (dispatch, getState) => {
|
||||||
|
const existingInjectionWeight = getState().getIn(['settings', 'injections', injectionId], null)
|
||||||
|
|
||||||
|
if (!existingInjectionWeight) return false
|
||||||
|
|
||||||
|
const newInjectionWeight = Math.max(existingInjectionWeight - 0.005, 0.01)
|
||||||
|
|
||||||
|
dispatch(changeSetting(['injections', injectionId], newInjectionWeight))
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: TIMELINE_INJECTION_HIDE,
|
||||||
|
injectionId,
|
||||||
|
})
|
||||||
|
}
|
@ -14,6 +14,7 @@ import {
|
|||||||
POPOVER_STATUS_OPTIONS,
|
POPOVER_STATUS_OPTIONS,
|
||||||
POPOVER_STATUS_EXPIRATION_OPTIONS,
|
POPOVER_STATUS_EXPIRATION_OPTIONS,
|
||||||
POPOVER_STATUS_VISIBILITY,
|
POPOVER_STATUS_VISIBILITY,
|
||||||
|
POPOVER_TIMELINE_INJECTION_OPTIONS,
|
||||||
POPOVER_USER_INFO,
|
POPOVER_USER_INFO,
|
||||||
POPOVER_VIDEO_STATS,
|
POPOVER_VIDEO_STATS,
|
||||||
} from '../../constants'
|
} from '../../constants'
|
||||||
@ -32,6 +33,7 @@ import {
|
|||||||
StatusExpirationOptionsPopover,
|
StatusExpirationOptionsPopover,
|
||||||
StatusOptionsPopover,
|
StatusOptionsPopover,
|
||||||
StatusVisibilityPopover,
|
StatusVisibilityPopover,
|
||||||
|
TimelineInjectionOptionsPopover,
|
||||||
UserInfoPopover,
|
UserInfoPopover,
|
||||||
VideoStatsPopover,
|
VideoStatsPopover,
|
||||||
} from '../../features/ui/util/async_components'
|
} from '../../features/ui/util/async_components'
|
||||||
@ -64,6 +66,7 @@ POPOVER_COMPONENTS[POPOVER_SIDEBAR_MORE] = SidebarMorePopover
|
|||||||
POPOVER_COMPONENTS[POPOVER_STATUS_OPTIONS] = StatusOptionsPopover
|
POPOVER_COMPONENTS[POPOVER_STATUS_OPTIONS] = StatusOptionsPopover
|
||||||
POPOVER_COMPONENTS[POPOVER_STATUS_EXPIRATION_OPTIONS] = StatusExpirationOptionsPopover
|
POPOVER_COMPONENTS[POPOVER_STATUS_EXPIRATION_OPTIONS] = StatusExpirationOptionsPopover
|
||||||
POPOVER_COMPONENTS[POPOVER_STATUS_VISIBILITY] = StatusVisibilityPopover
|
POPOVER_COMPONENTS[POPOVER_STATUS_VISIBILITY] = StatusVisibilityPopover
|
||||||
|
POPOVER_COMPONENTS[POPOVER_TIMELINE_INJECTION_OPTIONS] = TimelineInjectionOptionsPopover
|
||||||
POPOVER_COMPONENTS[POPOVER_USER_INFO] = UserInfoPopover
|
POPOVER_COMPONENTS[POPOVER_USER_INFO] = UserInfoPopover
|
||||||
POPOVER_COMPONENTS[POPOVER_VIDEO_STATS] = VideoStatsPopover
|
POPOVER_COMPONENTS[POPOVER_VIDEO_STATS] = VideoStatsPopover
|
||||||
|
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
|
import { closePopover } from '../../actions/popover'
|
||||||
|
import { hideTimelineInjection } from '../../actions/timeline_injections'
|
||||||
|
import PopoverLayout from './popover_layout'
|
||||||
|
import List from '../list'
|
||||||
|
|
||||||
|
class TimelineInjectionOptionsPopover extends React.PureComponent {
|
||||||
|
|
||||||
|
handleOnClick = () => {
|
||||||
|
this.props.onDismissInjection()
|
||||||
|
this.props.onDismiss()
|
||||||
|
this.props.onClosePopover()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnClosePopover = () => {
|
||||||
|
this.props.onClosePopover()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { intl, isXS } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverLayout
|
||||||
|
width={280}
|
||||||
|
isXS={isXS}
|
||||||
|
onClose={this.handleOnClosePopover}
|
||||||
|
>
|
||||||
|
<List
|
||||||
|
size={isXS ? 'large' : 'small'}
|
||||||
|
scrollKey='timeline_injection_options'
|
||||||
|
items={[{
|
||||||
|
hideArrow: true,
|
||||||
|
title: intl.formatMessage(messages.dismissMessage),
|
||||||
|
onClick: this.handleOnClick,
|
||||||
|
}]}
|
||||||
|
/>
|
||||||
|
</PopoverLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
dismissMessage: { id: 'timeline_injection_popover.dismiss_message', defaultMessage: 'Show this content less often' },
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch, { timelineInjectionId }) => ({
|
||||||
|
onDismissInjection() {
|
||||||
|
dispatch(hideTimelineInjection(timelineInjectionId))
|
||||||
|
},
|
||||||
|
onClosePopover: () => dispatch(closePopover()),
|
||||||
|
})
|
||||||
|
|
||||||
|
TimelineInjectionOptionsPopover.propTypes = {
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
isXS: PropTypes.bool,
|
||||||
|
timelineInjectionId: PropTypes.string.isRequired,
|
||||||
|
onClosePopover: PropTypes.func.isRequired,
|
||||||
|
onDismissInjection: PropTypes.func.isRequired,
|
||||||
|
onDismiss: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectIntl(connect(null, mapDispatchToProps)(TimelineInjectionOptionsPopover))
|
@ -0,0 +1,73 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
|
import { fetchGroups } from '../../actions/groups'
|
||||||
|
import GroupCollectionItem from '../group_collection_item'
|
||||||
|
import TimelineInjectionLayout from './timeline_injection_layout'
|
||||||
|
|
||||||
|
class FeaturedGroupsInjection extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (!this.props.isFetched) {
|
||||||
|
this.props.onFetchGroups('featured')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
groupIds,
|
||||||
|
isLoading,
|
||||||
|
isFetched,
|
||||||
|
isXS,
|
||||||
|
injectionId,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
if (isFetched && groupIds.size === 0) {
|
||||||
|
return <div />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TimelineInjectionLayout
|
||||||
|
id={injectionId}
|
||||||
|
title='Featured groups'
|
||||||
|
buttonLink='/groups/browse/featured'
|
||||||
|
buttonTitle='See more featured groups'
|
||||||
|
isXS={isXS}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
groupIds.map((groupId) => (
|
||||||
|
<div className={[_s.d, _s.w300PX].join(' ')}>
|
||||||
|
<GroupCollectionItem
|
||||||
|
isAddable
|
||||||
|
id={groupId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</TimelineInjectionLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
groupIds: state.getIn(['group_lists', 'featured', 'items']),
|
||||||
|
isFetched: state.getIn(['group_lists', 'featured', 'isFetched']),
|
||||||
|
isLoading: state.getIn(['group_lists', 'featured', 'isLoading']),
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
onFetchGroups: (tab) => dispatch(fetchGroups(tab)),
|
||||||
|
})
|
||||||
|
|
||||||
|
FeaturedGroupsInjection.propTypes = {
|
||||||
|
groupIds: ImmutablePropTypes.list,
|
||||||
|
isFetched: PropTypes.bool.isRequired,
|
||||||
|
isLoading: PropTypes.bool.isRequired,
|
||||||
|
onFetchGroups: PropTypes.func.isRequired,
|
||||||
|
injectionId: PropTypes.string.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(FeaturedGroupsInjection)
|
@ -0,0 +1,101 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { makeGetAccount } from '../../selectors'
|
||||||
|
import { fetchGroupCategories } from '../../actions/group_categories'
|
||||||
|
import slugify from '../../utils/slugify'
|
||||||
|
import Button from '../button'
|
||||||
|
import Text from '../text'
|
||||||
|
import TimelineInjectionLayout from './timeline_injection_layout'
|
||||||
|
|
||||||
|
class FeaturedGroupsInjection extends React.PureComponent {
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.dispatch(fetchGroupCategories())
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
categories,
|
||||||
|
isXS,
|
||||||
|
injectionId,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
let categoriesOptions = []
|
||||||
|
if (categories) {
|
||||||
|
for (let i = 0; i < categories.count(); i++) {
|
||||||
|
const c = categories.get(i)
|
||||||
|
const title = c.get('text')
|
||||||
|
categoriesOptions.push({
|
||||||
|
title,
|
||||||
|
to: `/groups/browse/categories/${slugify(title)}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const split1Arr = categoriesOptions.splice(0, Math.ceil(categoriesOptions.length /2));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TimelineInjectionLayout
|
||||||
|
id={injectionId}
|
||||||
|
title='Popular group categories'
|
||||||
|
subtitle='Find a group by browsing top categories.'
|
||||||
|
buttonLink='/groups/browse/categories'
|
||||||
|
buttonTitle='Browse all categories'
|
||||||
|
isXS={isXS}
|
||||||
|
>
|
||||||
|
<div className={[_s.d, _s.pb10].join(' ')}>
|
||||||
|
<div className={[_s.d, _s.flexRow, _s.mb5].join(' ')}>
|
||||||
|
{
|
||||||
|
split1Arr.map((block) => (
|
||||||
|
<Button
|
||||||
|
isNarrow
|
||||||
|
to={block.to}
|
||||||
|
color='primary'
|
||||||
|
backgroundColor='tertiary'
|
||||||
|
className={[_s.mr10].join(' ')}
|
||||||
|
>
|
||||||
|
<Text color='inherit'>
|
||||||
|
{block.title}
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={[_s.d, _s.flexRow].join(' ')}>
|
||||||
|
{
|
||||||
|
categoriesOptions.map((block) => (
|
||||||
|
<Button
|
||||||
|
isNarrow
|
||||||
|
to={block.to}
|
||||||
|
color='primary'
|
||||||
|
backgroundColor='tertiary'
|
||||||
|
className={[_s.mr10].join(' ')}
|
||||||
|
>
|
||||||
|
<Text color='inherit'>
|
||||||
|
{block.title}
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TimelineInjectionLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
categories: state.getIn(['group_categories', 'items']),
|
||||||
|
})
|
||||||
|
|
||||||
|
FeaturedGroupsInjection.propTypes = {
|
||||||
|
categories: ImmutablePropTypes.list.isRequired,
|
||||||
|
injectionId: PropTypes.string.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(FeaturedGroupsInjection)
|
@ -0,0 +1,73 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { me } from '../../initial_state'
|
||||||
|
import { URL_GAB_PRO } from '../../constants'
|
||||||
|
import Button from '../button'
|
||||||
|
import Text from '../text'
|
||||||
|
|
||||||
|
class ProUpgradeInjection extends React.PureComponent {
|
||||||
|
|
||||||
|
deferredPrompt = null
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnClick = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isPro } = this.props
|
||||||
|
|
||||||
|
if (isPro) return <div />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={[_s.d, _s.w100PC, _s.px15, _s.mb15].join(' ')}>
|
||||||
|
<div className={[_s.d, _s.w100PC, _s.py15, _s.px10, _s.boxShadowBlock, _s.radiusSmall, _s.bgPrimary].join(' ')}>
|
||||||
|
<div className={[_s.d, _s.py15, _s.px10].join(' ')}>
|
||||||
|
<Text size='extraLarge' align='center' weight='bold' className={_s.mb15}>
|
||||||
|
Upgrade to GabPRO
|
||||||
|
</Text>
|
||||||
|
<Text size='large' color='secondary' align='center'>
|
||||||
|
Please consider supporting us on our mission to defend free expression online for all people.
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={[_s.d, _s.mt10, _s.mb5, _s.flexRow, _s.mlAuto, _s.mrAuto].join(' ')}>
|
||||||
|
<Button
|
||||||
|
backgroundColor='secondary'
|
||||||
|
color='secondary'
|
||||||
|
onClick={this.handleOnClick}
|
||||||
|
className={_s.mr10}
|
||||||
|
>
|
||||||
|
<Text color='inherit' className={_s.px5}>
|
||||||
|
Not now
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
<Button href={URL_GAB_PRO}>
|
||||||
|
<Text color='inherit' weight='medium' className={_s.px15}>
|
||||||
|
Learn More
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
isPro: state.getIn(['accounts', me, 'is_pro']),
|
||||||
|
})
|
||||||
|
|
||||||
|
ProUpgradeInjection.propTypes = {
|
||||||
|
isPro: PropTypes.bool.isRequired,
|
||||||
|
injectionId: PropTypes.string,
|
||||||
|
isXS: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(ProUpgradeInjection)
|
@ -0,0 +1,109 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Button from '../button'
|
||||||
|
import Text from '../text'
|
||||||
|
|
||||||
|
class PWAInjection extends React.PureComponent {
|
||||||
|
|
||||||
|
deferredPrompt=null
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
window.addEventListener('beforeinstallprompt',(e) => {
|
||||||
|
console.log("e:",e)
|
||||||
|
// Prevent the mini-infobar from appearing on mobile
|
||||||
|
e.preventDefault()
|
||||||
|
// Stash the event so it can be triggered later.
|
||||||
|
this.deferredPrompt=e
|
||||||
|
// Update UI notify the user they can install the PWA
|
||||||
|
// showInstallPromotion()
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener('appinstalled',(evt) => {
|
||||||
|
// Log install to analytics
|
||||||
|
console.log('INSTALL: Success')
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener('DOMContentLoaded',() => {
|
||||||
|
let displayMode='browser tab'
|
||||||
|
if(navigator.standalone) {
|
||||||
|
displayMode='standalone-ios'
|
||||||
|
}
|
||||||
|
if(window.matchMedia('(display-mode: standalone)').matches) {
|
||||||
|
displayMode='standalone'
|
||||||
|
}
|
||||||
|
// Log launch display mode to analytics
|
||||||
|
console.log('DISPLAY_MODE_LAUNCH:',displayMode)
|
||||||
|
|
||||||
|
window.matchMedia('(display-mode: standalone)').addListener((evt) => {
|
||||||
|
let displayMode='browser tab';
|
||||||
|
if(evt.matches) {
|
||||||
|
displayMode='standalone';
|
||||||
|
}
|
||||||
|
// Log display mode change to analytics
|
||||||
|
console.log('DISPLAY_MODE_CHANGED',displayMode);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnClick=() => {
|
||||||
|
// Hide the app provided install promotion
|
||||||
|
// hideMyInstallPromotion()
|
||||||
|
// Show the install prompt
|
||||||
|
this.deferredPrompt.prompt()
|
||||||
|
// Wait for the user to respond to the prompt
|
||||||
|
this.deferredPrompt.userChoice.then((choiceResult) => {
|
||||||
|
if(choiceResult.outcome==='accepted') {
|
||||||
|
console.log('User accepted the install prompt')
|
||||||
|
} else {
|
||||||
|
console.log('User dismissed the install prompt')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
// : todo :
|
||||||
|
return <div />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={[_s.d,_s.w100PC,_s.px15,_s.mb15].join(' ')}>
|
||||||
|
<div className={[_s.d,_s.w100PC,_s.py15,_s.px10,_s.boxShadowBlock,_s.radiusSmall,_s.bgPrimary].join(' ')}>
|
||||||
|
<div className={[_s.d,_s.py15,_s.px10].join(' ')}>
|
||||||
|
<Text size='large' align='center' className={_s.mb10}>
|
||||||
|
We’re not on the app stores, but you can still get the Gab app on your phone.
|
||||||
|
</Text>
|
||||||
|
<Text size='large' align='center'>
|
||||||
|
Click install to learn how.
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={[_s.d,_s.mt10,_s.mb5,_s.flexRow,_s.mlAuto,_s.mrAuto].join(' ')}>
|
||||||
|
<Button
|
||||||
|
backgroundColor='none'
|
||||||
|
color='secondary'
|
||||||
|
className={_s.mr15}
|
||||||
|
>
|
||||||
|
Not now
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={this.handleOnClick}
|
||||||
|
>
|
||||||
|
<Text color='inherit' weight='medium' className={_s.px10}>
|
||||||
|
Install
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PWAInjection.propTypes = {
|
||||||
|
injectionId: PropTypes.string,
|
||||||
|
isXS: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PWAInjection
|
@ -0,0 +1,77 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
|
import { fetchFeaturedProducts } from '../../actions/shop'
|
||||||
|
import { URL_DISSENTER_SHOP } from '../../constants'
|
||||||
|
import ShopItem from '../shop_item'
|
||||||
|
import TimelineInjectionLayout from './timeline_injection_layout'
|
||||||
|
|
||||||
|
class ShopInjection extends React.PureComponent {
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { items } = this.props
|
||||||
|
|
||||||
|
const doFetch = !Array.isArray(items) || (Array.isArray(items) && items.length === 0)
|
||||||
|
if (doFetch) {
|
||||||
|
this.props.onFetchFeaturedProducts()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
intl,
|
||||||
|
items,
|
||||||
|
isError,
|
||||||
|
injectionId,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
if (!items || isError || !Array.isArray(items)) return <div />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TimelineInjectionLayout
|
||||||
|
id={injectionId}
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
buttonHref={URL_DISSENTER_SHOP}
|
||||||
|
buttonTitle={intl.formatMessage(messages.shop_now)}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
items.map((block, i) => (
|
||||||
|
<ShopItem
|
||||||
|
key={`shop-item-injection-${i}`}
|
||||||
|
image={block.image}
|
||||||
|
name={block.name}
|
||||||
|
link={block.link}
|
||||||
|
price={block.price}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</TimelineInjectionLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'shop_panel.title', defaultMessage: 'Dissenter Shop' },
|
||||||
|
shop_now: { id: 'shop_panel.shop_now', defaultMessage: 'Visit the Dissenter Shop' },
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
items: state.getIn(['shop', 'featured', 'items']),
|
||||||
|
isError: state.getIn(['shop', 'featured', 'isError']),
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
onFetchFeaturedProducts: () => dispatch(fetchFeaturedProducts()),
|
||||||
|
})
|
||||||
|
|
||||||
|
ShopInjection.propTypes = {
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
products: PropTypes.array,
|
||||||
|
onFetchFeaturedProducts: PropTypes.func.isRequired,
|
||||||
|
isError: PropTypes.bool.isRequired,
|
||||||
|
injectionId: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ShopInjection))
|
@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
|
import { me } from '../../initial_state'
|
||||||
|
import {
|
||||||
|
TIMELINE_INJECTION_PWA,
|
||||||
|
TIMELINE_INJECTION_WEIGHT_MULTIPLIER,
|
||||||
|
} from '../../constants'
|
||||||
|
import TimelineInjectionRoot from './timeline_injection_root'
|
||||||
|
|
||||||
|
class TimelineInjectionBase extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
injectionType: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { injectionWeights } = this.props
|
||||||
|
|
||||||
|
const keys = injectionWeights.keySeq().toArray()
|
||||||
|
const values = injectionWeights.valueSeq().toArray()
|
||||||
|
|
||||||
|
const weights = values.map((a) => Math.max(Math.ceil(a * TIMELINE_INJECTION_WEIGHT_MULTIPLIER), 0.01))
|
||||||
|
const totalWeight = weights.reduce((a, b) => a + b, 0)
|
||||||
|
|
||||||
|
let weighedElems = []
|
||||||
|
let currentElem = 0
|
||||||
|
while (currentElem < keys.length) {
|
||||||
|
for (let i = 0; i < weights[currentElem]; i++) {
|
||||||
|
weighedElems[weighedElems.length] = currentElem
|
||||||
|
}
|
||||||
|
currentElem++
|
||||||
|
}
|
||||||
|
|
||||||
|
const rnd = Math.floor(Math.random() * totalWeight)
|
||||||
|
const index = weighedElems[rnd]
|
||||||
|
const injectionType = keys[index]
|
||||||
|
|
||||||
|
this.setState({ injectionType })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { injectionType } = this.state
|
||||||
|
|
||||||
|
// : todo :
|
||||||
|
// hide PWA for now
|
||||||
|
if (injectionType === TIMELINE_INJECTION_PWA) return <div />
|
||||||
|
|
||||||
|
if (!me) return <div />
|
||||||
|
|
||||||
|
return <TimelineInjectionRoot type={injectionType} />
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
injectionWeights: state.getIn(['settings', 'injections']),
|
||||||
|
})
|
||||||
|
|
||||||
|
TimelineInjectionBase.propTypes = {
|
||||||
|
index: PropTypes.number,
|
||||||
|
injectionWeights: ImmutablePropTypes.map,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(TimelineInjectionBase)
|
@ -0,0 +1,115 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { openPopover } from '../../actions/popover'
|
||||||
|
import {
|
||||||
|
CX,
|
||||||
|
POPOVER_TIMELINE_INJECTION_OPTIONS,
|
||||||
|
} from '../../constants'
|
||||||
|
import Button from '../button'
|
||||||
|
import Text from '../text'
|
||||||
|
|
||||||
|
class TimelineInjectionLayout extends React.PureComponent {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
dismissed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnOptionsClick = () => {
|
||||||
|
this.props.dispatch(openPopover(POPOVER_TIMELINE_INJECTION_OPTIONS, {
|
||||||
|
targetRef: this.optionsBtn,
|
||||||
|
timelineInjectionId: this.props.id,
|
||||||
|
onDismiss: this.handleOnDismiss,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnDismiss = () => {
|
||||||
|
this.setState({ dismissed: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
setOptionsBtn = (n) => {
|
||||||
|
this.optionsBtn = n
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
children,
|
||||||
|
buttonLink,
|
||||||
|
buttonTitle,
|
||||||
|
isXS,
|
||||||
|
} = this.props
|
||||||
|
const { dismissed } = this.state
|
||||||
|
|
||||||
|
if (dismissed) return <div />
|
||||||
|
|
||||||
|
const containerClasses = CX({
|
||||||
|
d: 1,
|
||||||
|
w100PC: 1,
|
||||||
|
mb10: 1,
|
||||||
|
borderTop1PX: isXS,
|
||||||
|
borderBottom1PX: isXS,
|
||||||
|
border1PX: !isXS,
|
||||||
|
radiusSmall: !isXS,
|
||||||
|
borderColorSecondary: 1,
|
||||||
|
bgPrimary: 1,
|
||||||
|
overflowHidden: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={containerClasses}>
|
||||||
|
<div className={[_s.d, _s.px15, _s.py5, _s.flexRow, _s.jcCenter, _s.aiCenter].join(' ')}>
|
||||||
|
<div className={[_s.d, _s.pr10].join(' ')}>
|
||||||
|
<Text size='medium'>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
{
|
||||||
|
!!subtitle &&
|
||||||
|
<Text size='small' weight='medium' color='secondary' className={[_s.pt5, _s.pb10].join(' ')}>
|
||||||
|
{subtitle}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
backgroundColor='none'
|
||||||
|
color='secondary'
|
||||||
|
iconSize='16px'
|
||||||
|
icon='ellipsis'
|
||||||
|
onClick={this.handleOnOptionsClick}
|
||||||
|
buttonRef={this.setOptionsBtn}
|
||||||
|
className={[_s.mlAuto].join(' ')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={[_s.d, _s.px10, _s.flexRow, _s.width100PC, _s.overflowHidden, _s.overflowXScroll, _s.noScrollbar, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
<div className={_s.d}>
|
||||||
|
<Button
|
||||||
|
isText
|
||||||
|
color='none'
|
||||||
|
backgroundColor='none'
|
||||||
|
to={buttonLink}
|
||||||
|
className={[_s.px15, _s.py15, _s.bgSubtle_onHover].join(' ')}
|
||||||
|
>
|
||||||
|
<Text color='brand' align='center' size='medium'>
|
||||||
|
{buttonTitle}
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TimelineInjectionLayout.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
buttonLink: PropTypes.string,
|
||||||
|
buttonTitle: PropTypes.string,
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
subtitle: PropTypes.string,
|
||||||
|
isXS: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect()(TimelineInjectionLayout)
|
@ -0,0 +1,104 @@
|
|||||||
|
import {
|
||||||
|
BREAKPOINT_EXTRA_SMALL,
|
||||||
|
TIMELINE_INJECTION_FEATURED_GROUPS,
|
||||||
|
TIMELINE_INJECTION_GROUP_CATEGORIES,
|
||||||
|
TIMELINE_INJECTION_PRO_UPGRADE,
|
||||||
|
TIMELINE_INJECTION_PWA,
|
||||||
|
TIMELINE_INJECTION_SHOP,
|
||||||
|
TIMELINE_INJECTION_USER_SUGGESTIONS,
|
||||||
|
} from '../../constants'
|
||||||
|
import {
|
||||||
|
FeaturedGroupsInjection,
|
||||||
|
GroupCategoriesInjection,
|
||||||
|
ProUpgradeInjection,
|
||||||
|
PWAInjection,
|
||||||
|
ShopInjection,
|
||||||
|
UserSuggestionsInjection,
|
||||||
|
} from '../../features/ui/util/async_components'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { getWindowDimension } from '../../utils/is_mobile'
|
||||||
|
import Bundle from '../../features/ui/util/bundle'
|
||||||
|
|
||||||
|
const initialState = getWindowDimension()
|
||||||
|
|
||||||
|
const INJECTION_COMPONENTS = {}
|
||||||
|
INJECTION_COMPONENTS[TIMELINE_INJECTION_FEATURED_GROUPS] = FeaturedGroupsInjection
|
||||||
|
INJECTION_COMPONENTS[TIMELINE_INJECTION_GROUP_CATEGORIES] = GroupCategoriesInjection
|
||||||
|
INJECTION_COMPONENTS[TIMELINE_INJECTION_PRO_UPGRADE] = ProUpgradeInjection
|
||||||
|
INJECTION_COMPONENTS[TIMELINE_INJECTION_PWA] = PWAInjection
|
||||||
|
INJECTION_COMPONENTS[TIMELINE_INJECTION_SHOP] = ShopInjection
|
||||||
|
INJECTION_COMPONENTS[TIMELINE_INJECTION_USER_SUGGESTIONS] = UserSuggestionsInjection
|
||||||
|
|
||||||
|
class TimelineInjectionRoot extends React.PureComponent {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
width: initialState.width,
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.handleResize()
|
||||||
|
window.addEventListener('resize', this.handleResize, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('resize', this.handleResize, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResize = () => {
|
||||||
|
const { width } = getWindowDimension()
|
||||||
|
|
||||||
|
this.setState({ width })
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLoading = () => {
|
||||||
|
return <div />
|
||||||
|
}
|
||||||
|
|
||||||
|
renderError = () => {
|
||||||
|
return <div />
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { type } = this.props
|
||||||
|
const { width } = this.state
|
||||||
|
|
||||||
|
const visible = !!type
|
||||||
|
|
||||||
|
if (!visible) return <div />
|
||||||
|
|
||||||
|
const isXS = width <= BREAKPOINT_EXTRA_SMALL
|
||||||
|
|
||||||
|
//If is not XS and popover is pwa, dont show
|
||||||
|
//Since not on mobile this should not be visible
|
||||||
|
if (!isXS && type === TIMELINE_INJECTION_PWA) return <div />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Bundle
|
||||||
|
fetchComponent={INJECTION_COMPONENTS[type]}
|
||||||
|
loading={this.renderLoading}
|
||||||
|
error={this.renderError}
|
||||||
|
renderDelay={150}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
(Component) => (
|
||||||
|
<Component
|
||||||
|
isXS={isXS}
|
||||||
|
injectionId={type}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Bundle>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TimelineInjectionRoot.propTypes = {
|
||||||
|
type: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TimelineInjectionRoot
|
@ -0,0 +1,100 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
|
import {
|
||||||
|
fetchRelatedSuggestions,
|
||||||
|
fetchPopularSuggestions,
|
||||||
|
} from '../../actions/suggestions'
|
||||||
|
import Account from '../account'
|
||||||
|
import TimelineInjectionLayout from './timeline_injection_layout'
|
||||||
|
|
||||||
|
class UserSuggestionsInjection extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.handleFetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFetch = () => {
|
||||||
|
if (this.props.suggestionType === 'verified') {
|
||||||
|
this.props.fetchPopularSuggestions()
|
||||||
|
} else {
|
||||||
|
this.props.fetchRelatedSuggestions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
intl,
|
||||||
|
isLoading,
|
||||||
|
isXS,
|
||||||
|
suggestions,
|
||||||
|
suggestionType,
|
||||||
|
injectionId,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
if (suggestions.isEmpty()) return <div />
|
||||||
|
|
||||||
|
const title = suggestionType === 'verified' ? intl.formatMessage(messages.verifiedTitle) : intl.formatMessage(messages.relatedTitle)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TimelineInjectionLayout
|
||||||
|
id={injectionId}
|
||||||
|
title={title}
|
||||||
|
buttonLink='/suggestions'
|
||||||
|
buttonTitle='See more reccomendations'
|
||||||
|
isXS={isXS}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
suggestions.map((accountId) => (
|
||||||
|
<Account
|
||||||
|
isCard
|
||||||
|
key={`user_suggestion_injection_${accountId}`}
|
||||||
|
id={accountId}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</TimelineInjectionLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
|
||||||
|
relatedTitle: { id: 'who_to_follow.title', defaultMessage: 'Who to Follow' },
|
||||||
|
verifiedTitle: { id: 'who_to_follow.verified_title', defaultMessage: 'Verified Accounts to Follow' },
|
||||||
|
show_more: { id: 'who_to_follow.more', defaultMessage: 'Show more' },
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { suggestionType = 'related' }) => ({
|
||||||
|
suggestions: state.getIn(['suggestions', suggestionType, 'items']),
|
||||||
|
isLoading: state.getIn(['suggestions', suggestionType, 'isLoading']),
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
fetchRelatedSuggestions: () => dispatch(fetchRelatedSuggestions()),
|
||||||
|
fetchPopularSuggestions: () => dispatch(fetchPopularSuggestions()),
|
||||||
|
})
|
||||||
|
|
||||||
|
UserSuggestionsInjection.propTypes = {
|
||||||
|
suggestionType: PropTypes.oneOf([
|
||||||
|
'related',
|
||||||
|
'verified'
|
||||||
|
]),
|
||||||
|
fetchRelatedSuggestions: PropTypes.func.isRequired,
|
||||||
|
fetchPopularSuggestions: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
suggestions: ImmutablePropTypes.list.isRequired,
|
||||||
|
isLoading: PropTypes.bool.isRequired,
|
||||||
|
isXS: PropTypes.bool,
|
||||||
|
injectionId: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
UserSuggestionsInjection.defaultProps = {
|
||||||
|
suggestionType: 'related',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(UserSuggestionsInjection))
|
@ -34,6 +34,7 @@ export const POPOVER_SIDEBAR_MORE = 'SIDEBAR_MORE'
|
|||||||
export const POPOVER_STATUS_OPTIONS = 'STATUS_OPTIONS'
|
export const POPOVER_STATUS_OPTIONS = 'STATUS_OPTIONS'
|
||||||
export const POPOVER_STATUS_EXPIRATION_OPTIONS = 'STATUS_EXPIRATION_OPTIONS'
|
export const POPOVER_STATUS_EXPIRATION_OPTIONS = 'STATUS_EXPIRATION_OPTIONS'
|
||||||
export const POPOVER_STATUS_VISIBILITY = 'STATUS_VISIBILITY'
|
export const POPOVER_STATUS_VISIBILITY = 'STATUS_VISIBILITY'
|
||||||
|
export const POPOVER_TIMELINE_INJECTION_OPTIONS = 'TIMELINE_INJECTION_OPTIONS'
|
||||||
export const POPOVER_USER_INFO = 'USER_INFO'
|
export const POPOVER_USER_INFO = 'USER_INFO'
|
||||||
export const POPOVER_VIDEO_STATS = 'VIDEO_STATS'
|
export const POPOVER_VIDEO_STATS = 'VIDEO_STATS'
|
||||||
|
|
||||||
@ -137,3 +138,15 @@ export const GROUP_TIMELINE_SORTING_TYPE_TOP_OPTION_ALL_TIME = 'all_time'
|
|||||||
|
|
||||||
export const TOAST_TYPE_ERROR = 'error'
|
export const TOAST_TYPE_ERROR = 'error'
|
||||||
export const TOAST_TYPE_SUCCESS = 'success'
|
export const TOAST_TYPE_SUCCESS = 'success'
|
||||||
|
|
||||||
|
export const TIMELINE_INJECTION_FEATURED_GROUPS = 'TI_FEATURED_GROUPS'
|
||||||
|
export const TIMELINE_INJECTION_GROUP_CATEGORIES = 'TI_GROUP_CATEGORIES'
|
||||||
|
export const TIMELINE_INJECTION_PRO_UPGRADE = 'TI_PRO_UPGRADE'
|
||||||
|
export const TIMELINE_INJECTION_PWA = 'TI_PWA'
|
||||||
|
export const TIMELINE_INJECTION_SHOP = 'TI_SHOP'
|
||||||
|
export const TIMELINE_INJECTION_USER_SUGGESTIONS = 'TI_USER_SUGGESTIONS'
|
||||||
|
|
||||||
|
export const TIMELINE_INJECTION_WEIGHT_DEFAULT = 1
|
||||||
|
export const TIMELINE_INJECTION_WEIGHT_MULTIPLIER = 100
|
||||||
|
export const TIMELINE_INJECTION_WEIGHT_SUBTRACTOR = 0.005
|
||||||
|
export const TIMELINE_INJECTION_WEIGHT_MIN = 0.01
|
@ -22,14 +22,14 @@ export function EditShortcutsModal() { return import(/* webpackChunkName: "compo
|
|||||||
export function EmbedModal() { return import(/* webpackChunkName: "modals/embed_modal" */'../../../components/modal/embed_modal') }
|
export function EmbedModal() { return import(/* webpackChunkName: "modals/embed_modal" */'../../../components/modal/embed_modal') }
|
||||||
export function EmojiPicker() { return import(/* webpackChunkName: "emoji_picker" */'../../../components/emoji/emoji_picker') }
|
export function EmojiPicker() { return import(/* webpackChunkName: "emoji_picker" */'../../../components/emoji/emoji_picker') }
|
||||||
export function EmojiPickerPopover() { return import(/* webpackChunkName: "components/emoji_picker_popover" */'../../../components/popover/emoji_picker_popover') }
|
export function EmojiPickerPopover() { return import(/* webpackChunkName: "components/emoji_picker_popover" */'../../../components/popover/emoji_picker_popover') }
|
||||||
// export function FeaturedGroupsInjection() { return import(/* webpackChunkName: "components/featured_groups_injection" */'../../../components/timeline_injections/featured_groups_injection') }
|
export function FeaturedGroupsInjection() { return import(/* webpackChunkName: "components/featured_groups_injection" */'../../../components/timeline_injections/featured_groups_injection') }
|
||||||
export function Followers() { return import(/* webpackChunkName: "features/followers" */'../../followers') }
|
export function Followers() { return import(/* webpackChunkName: "features/followers" */'../../followers') }
|
||||||
export function Following() { return import(/* webpackChunkName: "features/following" */'../../following') }
|
export function Following() { return import(/* webpackChunkName: "features/following" */'../../following') }
|
||||||
export function FollowRequests() { return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests') }
|
export function FollowRequests() { return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests') }
|
||||||
export function LikedStatuses() { return import(/* webpackChunkName: "features/liked_statuses" */'../../liked_statuses') }
|
export function LikedStatuses() { return import(/* webpackChunkName: "features/liked_statuses" */'../../liked_statuses') }
|
||||||
export function GenericNotFound() { return import(/* webpackChunkName: "features/generic_not_found" */'../../generic_not_found') }
|
export function GenericNotFound() { return import(/* webpackChunkName: "features/generic_not_found" */'../../generic_not_found') }
|
||||||
export function GlobalFooter() { return import(/* webpackChunkName: "components/global_footer" */'../../../components/global_footer') }
|
export function GlobalFooter() { return import(/* webpackChunkName: "components/global_footer" */'../../../components/global_footer') }
|
||||||
// export function GroupCategoriesInjection() { return import(/* webpackChunkName: "components/group_categories_injection" */'../../../components/timeline_injections/group_categories_injection') }
|
export function GroupCategoriesInjection() { return import(/* webpackChunkName: "components/group_categories_injection" */'../../../components/timeline_injections/group_categories_injection') }
|
||||||
export function GroupsCollection() { return import(/* webpackChunkName: "features/groups_collection" */'../../groups_collection') }
|
export function GroupsCollection() { return import(/* webpackChunkName: "features/groups_collection" */'../../groups_collection') }
|
||||||
export function GroupAbout() { return import(/* webpackChunkName: "features/group_about" */'../../group_about.js') }
|
export function GroupAbout() { return import(/* webpackChunkName: "features/group_about" */'../../group_about.js') }
|
||||||
export function GroupCollectionTimeline() { return import(/* webpackChunkName: "features/group_collection_timeline" */'../../group_collection_timeline') }
|
export function GroupCollectionTimeline() { return import(/* webpackChunkName: "features/group_collection_timeline" */'../../group_collection_timeline') }
|
||||||
@ -85,14 +85,16 @@ export function ProfileInfoPanel() { return import(/* webpackChunkName: "compone
|
|||||||
export function ProfileStatsPanel() { return import(/* webpackChunkName: "components/profile_info_panel" */'../../../components/panel/profile_stats_panel') }
|
export function ProfileStatsPanel() { return import(/* webpackChunkName: "components/profile_info_panel" */'../../../components/panel/profile_stats_panel') }
|
||||||
export function ProgressPanel() { return import(/* webpackChunkName: "components/progress_panel" */'../../../components/panel/progress_panel') }
|
export function ProgressPanel() { return import(/* webpackChunkName: "components/progress_panel" */'../../../components/panel/progress_panel') }
|
||||||
export function ProPanel() { return import(/* webpackChunkName: "components/pro_panel" */'../../../components/panel/pro_panel') }
|
export function ProPanel() { return import(/* webpackChunkName: "components/pro_panel" */'../../../components/panel/pro_panel') }
|
||||||
|
export function ProUpgradeInjection() { return import(/* webpackChunkName: "components/pro_upgrade_injection" */'../../../components/timeline_injections/pro_upgrade_injection') }
|
||||||
export function ProUpgradeModal() { return import(/* webpackChunkName: "components/pro_upgrade_modal" */'../../../components/modal/pro_upgrade_modal') }
|
export function ProUpgradeModal() { return import(/* webpackChunkName: "components/pro_upgrade_modal" */'../../../components/modal/pro_upgrade_modal') }
|
||||||
// export function PWAInjection() { return import(/* webpackChunkName: "components/pwa_injection" */'../../../components/timeline_injections/pwa_injection') }
|
export function PWAInjection() { return import(/* webpackChunkName: "components/pwa_injection" */'../../../components/timeline_injections/pwa_injection') }
|
||||||
export function ReportModal() { return import(/* webpackChunkName: "modals/report_modal" */'../../../components/modal/report_modal') }
|
export function ReportModal() { return import(/* webpackChunkName: "modals/report_modal" */'../../../components/modal/report_modal') }
|
||||||
export function Search() { return import(/*webpackChunkName: "features/search" */'../../search') }
|
export function Search() { return import(/*webpackChunkName: "features/search" */'../../search') }
|
||||||
export function Shortcuts() { return import(/*webpackChunkName: "features/shortcuts" */'../../shortcuts') }
|
export function Shortcuts() { return import(/*webpackChunkName: "features/shortcuts" */'../../shortcuts') }
|
||||||
export function Status() { return import(/* webpackChunkName: "components/status" */'../../../components/status') }
|
export function Status() { return import(/* webpackChunkName: "components/status" */'../../../components/status') }
|
||||||
export function StatusFeature() { return import(/* webpackChunkName: "features/status" */'../../status') }
|
export function StatusFeature() { return import(/* webpackChunkName: "features/status" */'../../status') }
|
||||||
export function SearchFilterPanel() { return import(/* webpackChunkName: "components/search_filter_panel" */'../../../components/panel/search_filter_panel') }
|
export function SearchFilterPanel() { return import(/* webpackChunkName: "components/search_filter_panel" */'../../../components/panel/search_filter_panel') }
|
||||||
|
export function ShopInjection() { return import(/* webpackChunkName: "components/shop_injection" */'../../../components/timeline_injections/shop_injection') }
|
||||||
export function ShopPanel() { return import(/* webpackChunkName: "components/shop_panel" */'../../../components/panel/shop_panel') }
|
export function ShopPanel() { return import(/* webpackChunkName: "components/shop_panel" */'../../../components/panel/shop_panel') }
|
||||||
export function SidebarMorePopover() { return import(/* webpackChunkName: "components/sidebar_more_popover" */'../../../components/popover/sidebar_more_popover') }
|
export function SidebarMorePopover() { return import(/* webpackChunkName: "components/sidebar_more_popover" */'../../../components/popover/sidebar_more_popover') }
|
||||||
export function SignUpLogInPanel() { return import(/* webpackChunkName: "components/sign_up_log_in_panel" */'../../../components/panel/sign_up_log_in_panel') }
|
export function SignUpLogInPanel() { return import(/* webpackChunkName: "components/sign_up_log_in_panel" */'../../../components/panel/sign_up_log_in_panel') }
|
||||||
@ -110,7 +112,7 @@ export function StatusVisibilityPopover() { return import(/* webpackChunkName: "
|
|||||||
export function Suggestions() { return import(/* webpackChunkName: "features/suggestions" */'../../suggestions') }
|
export function Suggestions() { return import(/* webpackChunkName: "features/suggestions" */'../../suggestions') }
|
||||||
export function TermsOfSale() { return import(/* webpackChunkName: "features/about/terms_of_sale" */'../../about/terms_of_sale') }
|
export function TermsOfSale() { return import(/* webpackChunkName: "features/about/terms_of_sale" */'../../about/terms_of_sale') }
|
||||||
export function TermsOfService() { return import(/* webpackChunkName: "features/about/terms_of_service" */'../../about/terms_of_service') }
|
export function TermsOfService() { return import(/* webpackChunkName: "features/about/terms_of_service" */'../../about/terms_of_service') }
|
||||||
// export function TimelineInjectionOptionsPopover() { return import(/* webpackChunkName: "components/timeline_injection_options_popover" */'../../../components/popover/timeline_injection_options_popover') }
|
export function TimelineInjectionOptionsPopover() { return import(/* webpackChunkName: "components/timeline_injection_options_popover" */'../../../components/popover/timeline_injection_options_popover') }
|
||||||
export function TrendsPanel() { return import(/* webpackChunkName: "components/trends_panel" */'../../../components/panel/trends_panel') }
|
export function TrendsPanel() { return import(/* webpackChunkName: "components/trends_panel" */'../../../components/panel/trends_panel') }
|
||||||
export function UnauthorizedModal() { return import(/* webpackChunkName: "components/unauthorized_modal" */'../../../components/modal/unauthorized_modal') }
|
export function UnauthorizedModal() { return import(/* webpackChunkName: "components/unauthorized_modal" */'../../../components/modal/unauthorized_modal') }
|
||||||
export function UnfollowModal() { return import(/* webpackChunkName: "components/unfollow_modal" */'../../../components/modal/unfollow_modal') }
|
export function UnfollowModal() { return import(/* webpackChunkName: "components/unfollow_modal" */'../../../components/modal/unfollow_modal') }
|
||||||
@ -119,5 +121,5 @@ export function UserPanel() { return import(/* webpackChunkName: "components/use
|
|||||||
export function Video() { return import(/* webpackChunkName: "components/video" */'../../../components/video') }
|
export function Video() { return import(/* webpackChunkName: "components/video" */'../../../components/video') }
|
||||||
export function VideoModal() { return import(/* webpackChunkName: "components/video_modal" */'../../../components/modal/video_modal') }
|
export function VideoModal() { return import(/* webpackChunkName: "components/video_modal" */'../../../components/modal/video_modal') }
|
||||||
export function VideoStatsPopover() { return import(/* webpackChunkName: "components/video_stats_popover" */'../../../components/popover/video_stats_popover') }
|
export function VideoStatsPopover() { return import(/* webpackChunkName: "components/video_stats_popover" */'../../../components/popover/video_stats_popover') }
|
||||||
// export function UserSuggestionsInjection() { return import(/* webpackChunkName: "components/user_suggestions_injection" */'../../../components/timeline_injections/user_suggestions_injection') }
|
export function UserSuggestionsInjection() { return import(/* webpackChunkName: "components/user_suggestions_injection" */'../../../components/timeline_injections/user_suggestions_injection') }
|
||||||
export function UserSuggestionsPanel() { return import(/* webpackChunkName: "components/user_suggestions_panel" */'../../../components/panel/user_suggestions_panel') }
|
export function UserSuggestionsPanel() { return import(/* webpackChunkName: "components/user_suggestions_panel" */'../../../components/panel/user_suggestions_panel') }
|
@ -2,7 +2,17 @@ import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings'
|
|||||||
import { STORE_HYDRATE } from '../actions/store'
|
import { STORE_HYDRATE } from '../actions/store'
|
||||||
import { EMOJI_USE } from '../actions/emojis'
|
import { EMOJI_USE } from '../actions/emojis'
|
||||||
import { LIST_DELETE_SUCCESS, LIST_FETCH_FAIL } from '../actions/lists'
|
import { LIST_DELETE_SUCCESS, LIST_FETCH_FAIL } from '../actions/lists'
|
||||||
import { COMMENT_SORTING_TYPE_OLDEST } from '../constants'
|
import { TIMELINE_INJECTION_HIDE } from '../actions/timeline_injections'
|
||||||
|
import {
|
||||||
|
COMMENT_SORTING_TYPE_OLDEST,
|
||||||
|
TIMELINE_INJECTION_WEIGHT_DEFAULT,
|
||||||
|
TIMELINE_INJECTION_FEATURED_GROUPS,
|
||||||
|
TIMELINE_INJECTION_GROUP_CATEGORIES,
|
||||||
|
TIMELINE_INJECTION_PRO_UPGRADE,
|
||||||
|
TIMELINE_INJECTION_PWA,
|
||||||
|
TIMELINE_INJECTION_SHOP,
|
||||||
|
TIMELINE_INJECTION_USER_SUGGESTIONS,
|
||||||
|
} from '../constants'
|
||||||
import { Map as ImmutableMap, fromJS } from 'immutable'
|
import { Map as ImmutableMap, fromJS } from 'immutable'
|
||||||
import uuid from '../utils/uuid'
|
import uuid from '../utils/uuid'
|
||||||
|
|
||||||
@ -12,6 +22,16 @@ const initialState = ImmutableMap({
|
|||||||
skinTone: 1,
|
skinTone: 1,
|
||||||
commentSorting: COMMENT_SORTING_TYPE_OLDEST,
|
commentSorting: COMMENT_SORTING_TYPE_OLDEST,
|
||||||
|
|
||||||
|
// every dismiss reduces by half or set to zero for pwa, shop, pro
|
||||||
|
injections: ImmutableMap({
|
||||||
|
[TIMELINE_INJECTION_FEATURED_GROUPS]: TIMELINE_INJECTION_WEIGHT_DEFAULT,
|
||||||
|
[TIMELINE_INJECTION_GROUP_CATEGORIES]: TIMELINE_INJECTION_WEIGHT_DEFAULT,
|
||||||
|
[TIMELINE_INJECTION_PRO_UPGRADE]: TIMELINE_INJECTION_WEIGHT_DEFAULT,
|
||||||
|
[TIMELINE_INJECTION_PWA]: TIMELINE_INJECTION_WEIGHT_DEFAULT,
|
||||||
|
[TIMELINE_INJECTION_SHOP]: TIMELINE_INJECTION_WEIGHT_DEFAULT,
|
||||||
|
[TIMELINE_INJECTION_USER_SUGGESTIONS]: TIMELINE_INJECTION_WEIGHT_DEFAULT,
|
||||||
|
}),
|
||||||
|
|
||||||
displayOptions: ImmutableMap({
|
displayOptions: ImmutableMap({
|
||||||
fontSize: 'normal',
|
fontSize: 'normal',
|
||||||
radiusSmallDisabled: false,
|
radiusSmallDisabled: false,
|
||||||
|
27
app/javascript/gabsocial/reducers/timeline_injections.js
Normal file
27
app/javascript/gabsocial/reducers/timeline_injections.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import Immutable from 'immutable'
|
||||||
|
import {
|
||||||
|
TIMELINE_INJECTION_SHOW,
|
||||||
|
TIMELINE_INJECTION_HIDE,
|
||||||
|
} from '../actions/timeline_injections'
|
||||||
|
|
||||||
|
const initialState = Immutable.Map({
|
||||||
|
visibleIds: Immutable.List(),
|
||||||
|
hiddenIds: Immutable.List(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function timeline_injection(state = initialState, action) {
|
||||||
|
switch(action.type) {
|
||||||
|
case TIMELINE_INJECTION_SHOW:
|
||||||
|
return state.withMutations((map) => {
|
||||||
|
map.update('hiddenIds', (list) => list.filterNot((id) => id === action.injectionId))
|
||||||
|
map.update('visibleIds', (list) => list.push(action.injectionId))
|
||||||
|
})
|
||||||
|
case TIMELINE_INJECTION_HIDE:
|
||||||
|
return state.withMutations((map) => {
|
||||||
|
map.update('visibleIds', (list) => list.filterNot((id) => id === action.injectionId))
|
||||||
|
map.update('hiddenIds', (list) => list.push(action.injectionId))
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user