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_EXPIRATION_OPTIONS,
|
||||
POPOVER_STATUS_VISIBILITY,
|
||||
POPOVER_TIMELINE_INJECTION_OPTIONS,
|
||||
POPOVER_USER_INFO,
|
||||
POPOVER_VIDEO_STATS,
|
||||
} from '../../constants'
|
||||
@ -32,6 +33,7 @@ import {
|
||||
StatusExpirationOptionsPopover,
|
||||
StatusOptionsPopover,
|
||||
StatusVisibilityPopover,
|
||||
TimelineInjectionOptionsPopover,
|
||||
UserInfoPopover,
|
||||
VideoStatsPopover,
|
||||
} 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_EXPIRATION_OPTIONS] = StatusExpirationOptionsPopover
|
||||
POPOVER_COMPONENTS[POPOVER_STATUS_VISIBILITY] = StatusVisibilityPopover
|
||||
POPOVER_COMPONENTS[POPOVER_TIMELINE_INJECTION_OPTIONS] = TimelineInjectionOptionsPopover
|
||||
POPOVER_COMPONENTS[POPOVER_USER_INFO] = UserInfoPopover
|
||||
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_EXPIRATION_OPTIONS = 'STATUS_EXPIRATION_OPTIONS'
|
||||
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_VIDEO_STATS = 'VIDEO_STATS'
|
||||
|
||||
@ -136,4 +137,16 @@ export const GROUP_TIMELINE_SORTING_TYPE_TOP_OPTION_YEARLY = 'yearly'
|
||||
export const GROUP_TIMELINE_SORTING_TYPE_TOP_OPTION_ALL_TIME = 'all_time'
|
||||
|
||||
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 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 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 Following() { return import(/* webpackChunkName: "features/following" */'../../following') }
|
||||
export function FollowRequests() { return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests') }
|
||||
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 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 GroupAbout() { return import(/* webpackChunkName: "features/group_about" */'../../group_about.js') }
|
||||
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 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 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 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 Search() { return import(/*webpackChunkName: "features/search" */'../../search') }
|
||||
export function Shortcuts() { return import(/*webpackChunkName: "features/shortcuts" */'../../shortcuts') }
|
||||
export function Status() { return import(/* webpackChunkName: "components/status" */'../../../components/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 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 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') }
|
||||
@ -110,7 +112,7 @@ export function StatusVisibilityPopover() { return import(/* webpackChunkName: "
|
||||
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 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 UnauthorizedModal() { return import(/* webpackChunkName: "components/unauthorized_modal" */'../../../components/modal/unauthorized_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 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 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') }
|
@ -2,7 +2,17 @@ import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings'
|
||||
import { STORE_HYDRATE } from '../actions/store'
|
||||
import { EMOJI_USE } from '../actions/emojis'
|
||||
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 uuid from '../utils/uuid'
|
||||
|
||||
@ -12,6 +22,16 @@ const initialState = ImmutableMap({
|
||||
skinTone: 1,
|
||||
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({
|
||||
fontSize: 'normal',
|
||||
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