Added promotions to redux and added selector for setting promotions if not PRO
• Added: - promotions to redux - selector for setting promotions if not PRO • Updated: - StatusList, SidebarPanelGroup to use promotions from redux
This commit is contained in:
parent
f806fddb5f
commit
21937d9e09
10
app/controllers/api/v1/promotions_controller.rb
Normal file
10
app/controllers/api/v1/promotions_controller.rb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::PromotionsController < EmptyController
|
||||||
|
|
||||||
|
def index
|
||||||
|
data = ActiveModelSerializers::SerializableResource.new(Promotion.active, each_serializer: REST::PromotionSerializer)
|
||||||
|
render json: data.to_json, content_type: 'application/json'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
34
app/javascript/gabsocial/actions/promotions.js
Normal file
34
app/javascript/gabsocial/actions/promotions.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import api from '../api'
|
||||||
|
import { me } from '../initial_state'
|
||||||
|
|
||||||
|
export const PROMOTIONS_FETCH_REQUEST = 'PROMOTIONS_FETCH_REQUEST'
|
||||||
|
export const PROMOTIONS_FETCH_SUCCESS = 'PROMOTIONS_FETCH_SUCCESS'
|
||||||
|
export const PROMOTIONS_FETCH_FAIL = 'PROMOTIONS_FETCH_FAIL'
|
||||||
|
|
||||||
|
export const fetchPromotions = () => {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
if (!me) return
|
||||||
|
|
||||||
|
dispatch(fetchPromotionsRequest())
|
||||||
|
|
||||||
|
api(getState).get('/api/v1/promotions').then((response) => {
|
||||||
|
dispatch(fetchPromotionsSuccess(response.data))
|
||||||
|
}).catch((error) => {
|
||||||
|
dispatch(fetchPromotionsFail(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchPromotionsRequest = () => ({
|
||||||
|
type: PROMOTIONS_FETCH_REQUEST,
|
||||||
|
})
|
||||||
|
|
||||||
|
const fetchPromotionsSuccess = (items) => ({
|
||||||
|
type: PROMOTIONS_FETCH_SUCCESS,
|
||||||
|
items,
|
||||||
|
})
|
||||||
|
|
||||||
|
const fetchPromotionsFail = (error, listType) => ({
|
||||||
|
type: PROMOTIONS_FETCH_FAIL,
|
||||||
|
error,
|
||||||
|
})
|
@ -1,6 +1,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { me, promotions } from '../initial_state'
|
import { connect } from 'react-redux'
|
||||||
|
import { me } from '../initial_state'
|
||||||
|
import { getPromotions } from '../selectors'
|
||||||
import Bundle from '../features/ui/util/bundle'
|
import Bundle from '../features/ui/util/bundle'
|
||||||
import WrappedBundle from '../features/ui/util/wrapped_bundle'
|
import WrappedBundle from '../features/ui/util/wrapped_bundle'
|
||||||
import {
|
import {
|
||||||
@ -10,16 +12,20 @@ import {
|
|||||||
class SidebarPanelGroup extends React.PureComponent {
|
class SidebarPanelGroup extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { layout, page } = this.props
|
const {
|
||||||
|
layout,
|
||||||
|
page,
|
||||||
|
promotions,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
if (Array.isArray(promotions) && Array.isArray(layout) && !!me) {
|
if (!!promotions && promotions.count() > 0 && Array.isArray(layout) && !!me) {
|
||||||
const sidebarPromotionPageId = `${page}.sidebar`
|
const sidebarPromotionPageId = `${page}.sidebar`
|
||||||
const promotion = promotions.find(p => p.timeline_id === sidebarPromotionPageId)
|
const promotion = promotions.find((p) => p.get('timeline_id') === sidebarPromotionPageId)
|
||||||
|
|
||||||
if (!!promotion) {
|
if (!!promotion) {
|
||||||
const correctedPosition = promotion.position - 1 > layout.length ? layout.length - 1 : promotion.position
|
const correctedPosition = promotion.get('position') - 1 > layout.length ? layout.length - 1 : promotion.get('position')
|
||||||
if (!layout.find(p => p.key === 'status-promotion-panel')) {
|
if (!layout.find(p => p.key === 'status-promotion-panel')) {
|
||||||
layout.splice(correctedPosition, 0, <WrappedBundle key='status-promotion-panel' component={StatusPromotionPanel} componentParams={{ statusId: promotion.status_id }} />)
|
layout.splice(correctedPosition, 0, <WrappedBundle key='status-promotion-panel' component={StatusPromotionPanel} componentParams={{ statusId: promotion.get('status_id') }} />)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,10 +62,14 @@ class SidebarPanelGroup extends React.PureComponent {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
promotions: getPromotions()(state)
|
||||||
|
})
|
||||||
|
|
||||||
SidebarPanelGroup.propTypes = {
|
SidebarPanelGroup.propTypes = {
|
||||||
layout: PropTypes.array.isRequired,
|
layout: PropTypes.array.isRequired,
|
||||||
page: PropTypes.string.isRequired,
|
page: PropTypes.string.isRequired,
|
||||||
promotion: PropTypes.object,
|
promotion: PropTypes.object,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SidebarPanelGroup
|
export default connect(mapStateToProps)(SidebarPanelGroup)
|
@ -6,7 +6,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
|
|||||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import { createSelector } from 'reselect'
|
import { createSelector } from 'reselect'
|
||||||
import debounce from 'lodash.debounce'
|
import debounce from 'lodash.debounce'
|
||||||
import { me, promotions } from '../initial_state'
|
import { me } from '../initial_state'
|
||||||
|
import { getPromotions } from '../selectors'
|
||||||
import {
|
import {
|
||||||
TIMELINE_INJECTION_FEATURED_GROUPS,
|
TIMELINE_INJECTION_FEATURED_GROUPS,
|
||||||
TIMELINE_INJECTION_GROUP_CATEGORIES,
|
TIMELINE_INJECTION_GROUP_CATEGORIES,
|
||||||
@ -45,15 +46,16 @@ class StatusList extends ImmutablePureComponent {
|
|||||||
promotedStatuses,
|
promotedStatuses,
|
||||||
timelineId,
|
timelineId,
|
||||||
statusIds,
|
statusIds,
|
||||||
|
promotions,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
if (Array.isArray(promotions)) {
|
if (!!promotions && promotions.count() > 0) {
|
||||||
promotions.forEach((promotionBlock) => {
|
promotions.forEach((promotion) => {
|
||||||
|
|
||||||
if (promotionBlock.timeline_id === timelineId &&
|
if (promotion.get('timeline_id') === timelineId &&
|
||||||
statusIds.count() >= promotionBlock.position &&
|
statusIds.count() >= promotion.get('position') &&
|
||||||
!promotedStatuses[promotionBlock.status_id]) {
|
!promotedStatuses[promotion.get('status_id')]) {
|
||||||
onFetchStatus(promotionBlock.status_id)
|
onFetchStatus(promotion.get('status_id'))
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -158,9 +160,12 @@ class StatusList extends ImmutablePureComponent {
|
|||||||
emptyMessage,
|
emptyMessage,
|
||||||
onScrollToTop,
|
onScrollToTop,
|
||||||
onScroll,
|
onScroll,
|
||||||
|
promotions,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { fetchedContext, isRefreshing } = this.state
|
const { fetchedContext, isRefreshing } = this.state
|
||||||
|
|
||||||
|
console.log("promotions:", promotions)
|
||||||
|
|
||||||
if (isPartial || (isLoading && statusIds.size === 0)) {
|
if (isPartial || (isLoading && statusIds.size === 0)) {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@ -200,13 +205,13 @@ class StatusList extends ImmutablePureComponent {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if (Array.isArray(promotions)) {
|
if (!!promotions && promotions.count() > 0) {
|
||||||
const promotionBlock = promotions.find(p => (p.position === i && p.timeline_id === timelineId))
|
const promotion = promotions.find((p) => (p.get('position') === i && p.get('timeline_id') === timelineId))
|
||||||
if (promotionBlock) {
|
if (promotion) {
|
||||||
scrollableContent.push(
|
scrollableContent.push(
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
key={`promotion-${i}-${promotionBlock.status_id}`}
|
key={`promotion-${i}-${promotion.get('status_id')}`}
|
||||||
id={promotionBlock.status_id}
|
id={promotion.get('status_id')}
|
||||||
onMoveUp={this.handleMoveUp}
|
onMoveUp={this.handleMoveUp}
|
||||||
onMoveDown={this.handleMoveDown}
|
onMoveDown={this.handleMoveDown}
|
||||||
contextType={timelineId}
|
contextType={timelineId}
|
||||||
@ -340,21 +345,24 @@ const mapStateToProps = (state, { timelineId }) => {
|
|||||||
if (!timelineId) return {}
|
if (!timelineId) return {}
|
||||||
|
|
||||||
const getStatusIds = makeGetStatusIds()
|
const getStatusIds = makeGetStatusIds()
|
||||||
|
const promotions = getPromotions()(state)
|
||||||
|
|
||||||
const statusIds = getStatusIds(state, {
|
const statusIds = getStatusIds(state, {
|
||||||
type: timelineId.substring(0, 5) === 'group' ? 'group' : timelineId,
|
type: timelineId.substring(0, 5) === 'group' ? 'group' : timelineId,
|
||||||
id: timelineId
|
id: timelineId
|
||||||
})
|
})
|
||||||
|
|
||||||
const promotedStatuses = Array.isArray(promotions) ?
|
const promotedStatuses = (!!promotions && promotions.count() > 0) ?
|
||||||
promotions.map((block) => {
|
promotions.map((promotion) => {
|
||||||
const s = {}
|
const s = {}
|
||||||
s[block.status_id] = state.getIn(['statuses', block.status_id])
|
s[promotion.get('status_id')] = state.getIn(['statuses', promotion.get('status_id')])
|
||||||
return s
|
return s
|
||||||
}) : []
|
}) : []
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusIds,
|
statusIds,
|
||||||
|
promotions,
|
||||||
promotedStatuses,
|
promotedStatuses,
|
||||||
isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
|
isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
|
||||||
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
|
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
|
||||||
|
@ -10,6 +10,7 @@ import moment from 'moment-mini'
|
|||||||
import { ScrollContext } from 'react-router-scroll-4'
|
import { ScrollContext } from 'react-router-scroll-4'
|
||||||
import { IntlProvider, addLocaleData } from 'react-intl'
|
import { IntlProvider, addLocaleData } from 'react-intl'
|
||||||
import { fetchCustomEmojis } from '../actions/custom_emojis'
|
import { fetchCustomEmojis } from '../actions/custom_emojis'
|
||||||
|
import { fetchPromotions } from '../actions/promotions'
|
||||||
import { hydrateStore } from '../actions/store'
|
import { hydrateStore } from '../actions/store'
|
||||||
import { MIN_ACCOUNT_CREATED_AT_ONBOARDING } from '../constants'
|
import { MIN_ACCOUNT_CREATED_AT_ONBOARDING } from '../constants'
|
||||||
import {
|
import {
|
||||||
@ -32,6 +33,7 @@ const hydrateAction = hydrateStore(initialState)
|
|||||||
|
|
||||||
store.dispatch(hydrateAction)
|
store.dispatch(hydrateAction)
|
||||||
store.dispatch(fetchCustomEmojis())
|
store.dispatch(fetchCustomEmojis())
|
||||||
|
store.dispatch(fetchPromotions())
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => ({
|
||||||
accountCreatedAt: !!me ? state.getIn(['accounts', me, 'created_at']) : undefined,
|
accountCreatedAt: !!me ? state.getIn(['accounts', me, 'created_at']) : undefined,
|
||||||
|
@ -25,6 +25,7 @@ import news from './news'
|
|||||||
import notifications from './notifications'
|
import notifications from './notifications'
|
||||||
import polls from './polls'
|
import polls from './polls'
|
||||||
import popover from './popover'
|
import popover from './popover'
|
||||||
|
import promotions from './promotions'
|
||||||
import push_notifications from './push_notifications'
|
import push_notifications from './push_notifications'
|
||||||
import relationships from './relationships'
|
import relationships from './relationships'
|
||||||
import reports from './reports'
|
import reports from './reports'
|
||||||
@ -70,6 +71,7 @@ const reducers = {
|
|||||||
notifications,
|
notifications,
|
||||||
polls,
|
polls,
|
||||||
popover,
|
popover,
|
||||||
|
promotions,
|
||||||
push_notifications,
|
push_notifications,
|
||||||
relationships,
|
relationships,
|
||||||
reports,
|
reports,
|
||||||
|
23
app/javascript/gabsocial/reducers/promotions.js
Normal file
23
app/javascript/gabsocial/reducers/promotions.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import {
|
||||||
|
List as ImmutableList,
|
||||||
|
fromJS,
|
||||||
|
}from 'immutable'
|
||||||
|
import {
|
||||||
|
PROMOTIONS_FETCH_REQUEST,
|
||||||
|
PROMOTIONS_FETCH_SUCCESS,
|
||||||
|
PROMOTIONS_FETCH_FAIL,
|
||||||
|
} from '../actions/promotions'
|
||||||
|
|
||||||
|
const initialState = ImmutableList()
|
||||||
|
|
||||||
|
export default function promotions(state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case PROMOTIONS_FETCH_REQUEST:
|
||||||
|
case PROMOTIONS_FETCH_FAIL:
|
||||||
|
return initialState
|
||||||
|
case PROMOTIONS_FETCH_SUCCESS:
|
||||||
|
return fromJS(action.items)
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
@ -38,6 +38,16 @@ const toServerSideType = columnType => {
|
|||||||
|
|
||||||
export const getFilters = (state, { contextType }) => state.get('filters', ImmutableList()).filter(filter => contextType && filter.get('context').includes(toServerSideType(contextType)) && (filter.get('expires_at') === null || Date.parse(filter.get('expires_at')) > (new Date())));
|
export const getFilters = (state, { contextType }) => state.get('filters', ImmutableList()).filter(filter => contextType && filter.get('context').includes(toServerSideType(contextType)) && (filter.get('expires_at') === null || Date.parse(filter.get('expires_at')) > (new Date())));
|
||||||
|
|
||||||
|
export const getPromotions = () => {
|
||||||
|
return createSelector([
|
||||||
|
(state) => state,
|
||||||
|
(state) => state.getIn(['accounts', me, 'is_pro']),
|
||||||
|
(state) => state.get('promotions'),
|
||||||
|
], (state, isPro, promotions) => {
|
||||||
|
return !isPro ? promotions : ImmutableList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const escapeRegExp = string =>
|
const escapeRegExp = string =>
|
||||||
string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||||
|
|
||||||
|
@ -345,6 +345,7 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
|
|
||||||
get '/search', to: 'search#index', as: :search
|
get '/search', to: 'search#index', as: :search
|
||||||
|
resources :promotions, only: [:index]
|
||||||
|
|
||||||
get '/account_by_username/:username', to: 'account_by_username#show', username: username_regex
|
get '/account_by_username/:username', to: 'account_by_username#show', username: username_regex
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user