fix eslint for announcements
Change-Id: Id5ba0a2d5b1c09372a7b8de61ba0532d8f4a0760 Reviewed-on: https://gerrit.instructure.com/161137 Reviewed-by: Landon Gilbert-Bland <lbland@instructure.com> Tested-by: Jenkins Product-Review: Steven Burnett <sburnett@instructure.com> QA-Review: Steven Burnett <sburnett@instructure.com>
This commit is contained in:
parent
e48d3aaf87
commit
3e86f8b1b9
|
@ -93,7 +93,9 @@ module.exports = {
|
|||
// now to conform to prettier.
|
||||
files: [
|
||||
'app/jsx/permissions/**/*.js',
|
||||
'app/jsx/account_course_user_search/**/*.js'
|
||||
'app/jsx/account_course_user_search/**/*.js',
|
||||
'app/jsx/discussions/**/*.js',
|
||||
'app/jsx/announcements/**/*.js'
|
||||
],
|
||||
rules: {
|
||||
'prettier/prettier': 'error'
|
||||
|
|
|
@ -17,24 +17,27 @@
|
|||
*/
|
||||
|
||||
import I18n from 'i18n!announcements_v2'
|
||||
import { createActions } from 'redux-actions'
|
||||
import {createActions} from 'redux-actions'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import range from 'lodash/range'
|
||||
import $ from 'jquery'
|
||||
import 'compiled/jquery.rails_flash_notifications'
|
||||
import 'compiled/jquery.rails_flash_notifications' // eslint-disable-line
|
||||
|
||||
import * as apiClient from './apiClient'
|
||||
import { createPaginationActions } from '../shared/reduxPagination'
|
||||
import { notificationActions } from '../shared/reduxNotifications'
|
||||
import {createPaginationActions} from '../shared/reduxPagination'
|
||||
import {notificationActions} from '../shared/reduxNotifications'
|
||||
|
||||
function fetchAnnouncements(dispatch, getState, payload) {
|
||||
return (resolve, reject) => {
|
||||
apiClient.getAnnouncements(getState(), payload)
|
||||
apiClient
|
||||
.getAnnouncements(getState(), payload)
|
||||
.then(res => {
|
||||
$.screenReaderFlashMessageExclusive(I18n.t('%{count} announcements found.', { count: res.data.length }))
|
||||
$.screenReaderFlashMessageExclusive(
|
||||
I18n.t('%{count} announcements found.', {count: res.data.length})
|
||||
)
|
||||
resolve(res)
|
||||
})
|
||||
.catch(err => reject({ err, message: I18n.t('An error ocurred while loading announcements') }))
|
||||
.catch(err => reject({err, message: I18n.t('An error ocurred while loading announcements')}))
|
||||
}
|
||||
}
|
||||
const announcementActions = createPaginationActions('announcements', fetchAnnouncements)
|
||||
|
@ -62,12 +65,9 @@ const types = [
|
|||
'SET_ANNOUNCEMENTS_IS_LOCKING'
|
||||
]
|
||||
|
||||
const actions = Object.assign(
|
||||
createActions(...types),
|
||||
announcementActions.actionCreators,
|
||||
)
|
||||
const actions = Object.assign(createActions(...types), announcementActions.actionCreators)
|
||||
|
||||
actions.searchAnnouncements = function searchAnnouncements (searchOpts) {
|
||||
actions.searchAnnouncements = function searchAnnouncements(searchOpts) {
|
||||
return (dispatch, getState) => {
|
||||
const oldSearch = getState().announcementsSearch
|
||||
dispatch(actions.updateAnnouncementsSearch(searchOpts))
|
||||
|
@ -76,45 +76,52 @@ actions.searchAnnouncements = function searchAnnouncements (searchOpts) {
|
|||
|
||||
if (!isEqual(oldSearch, newSearch)) {
|
||||
// uncache pages if we change the search query
|
||||
dispatch(actions.clearAnnouncementsPage({ pages: range(1, state.announcements.lastPage + 1) }))
|
||||
dispatch(actions.getAnnouncements({ page: 1, select: true }))
|
||||
dispatch(actions.clearAnnouncementsPage({pages: range(1, state.announcements.lastPage + 1)}))
|
||||
dispatch(actions.getAnnouncements({page: 1, select: true}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actions.getExternalFeeds = function () {
|
||||
actions.getExternalFeeds = function() {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(actions.loadingExternalFeedStart())
|
||||
apiClient.getExternalFeeds(getState())
|
||||
apiClient
|
||||
.getExternalFeeds(getState())
|
||||
.then(resp => {
|
||||
dispatch(actions.loadingExternalFeedSuccess({ feeds: resp.data }))
|
||||
}).catch((err) => {
|
||||
dispatch(actions.loadingExternalFeedFail({
|
||||
message: I18n.t('Failed to Load External Feeds'),
|
||||
err
|
||||
}))
|
||||
dispatch(actions.loadingExternalFeedSuccess({feeds: resp.data}))
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(
|
||||
actions.loadingExternalFeedFail({
|
||||
message: I18n.t('Failed to Load External Feeds'),
|
||||
err
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
actions.deleteExternalFeed = function ({ feedId }) {
|
||||
actions.deleteExternalFeed = function({feedId}) {
|
||||
return (dispatch, getState) => {
|
||||
if(!getState().externalRssFeed.isDeleting) {
|
||||
if (!getState().externalRssFeed.isDeleting) {
|
||||
dispatch(actions.deleteExternalFeedStart())
|
||||
apiClient.deleteExternalFeed(getState(), feedId)
|
||||
apiClient
|
||||
.deleteExternalFeed(getState(), feedId)
|
||||
.then(() => {
|
||||
dispatch(actions.deleteExternalFeedSuccess({ feedId }))
|
||||
dispatch(actions.deleteExternalFeedSuccess({feedId}))
|
||||
const successMessage = I18n.t('External Feed deleted successfully')
|
||||
$.screenReaderFlashMessage(successMessage)
|
||||
dispatch(notificationActions.notifyInfo({ message: successMessage }))
|
||||
dispatch(notificationActions.notifyInfo({message: successMessage}))
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
const failMessage = I18n.t('Failed to delete external feed')
|
||||
$.screenReaderFlashMessage(failMessage)
|
||||
dispatch(actions.deleteExternalFeedFail({
|
||||
message: failMessage,
|
||||
err
|
||||
}))
|
||||
dispatch(
|
||||
actions.deleteExternalFeedFail({
|
||||
message: failMessage,
|
||||
err
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -122,88 +129,113 @@ actions.deleteExternalFeed = function ({ feedId }) {
|
|||
|
||||
actions.toggleAnnouncementsLock = (announcements, isLocking = true) => (dispatch, getState) => {
|
||||
dispatch(actions.lockAnnouncementsStart())
|
||||
apiClient.lockAnnouncements(getState(), [].concat(announcements), isLocking)
|
||||
apiClient
|
||||
.lockAnnouncements(getState(), [].concat(announcements), isLocking)
|
||||
.then(res => {
|
||||
if (res.successes.length) {
|
||||
dispatch(actions.lockAnnouncementsSuccess({ res, locked: isLocking }))
|
||||
dispatch(actions.lockAnnouncementsSuccess({res, locked: isLocking}))
|
||||
if (isLocking) {
|
||||
dispatch(notificationActions.notifyInfo({ message: I18n.t('Announcements locked successfully') }))
|
||||
dispatch(
|
||||
notificationActions.notifyInfo({message: I18n.t('Announcements locked successfully')})
|
||||
)
|
||||
} else {
|
||||
dispatch(notificationActions.notifyInfo({ message: I18n.t('Announcements unlocked successfully') }))
|
||||
dispatch(
|
||||
notificationActions.notifyInfo({message: I18n.t('Announcements unlocked successfully')})
|
||||
)
|
||||
}
|
||||
} else if (res.failures.length) {
|
||||
dispatch(actions.lockAnnouncementsFail({
|
||||
err: res.failures,
|
||||
message: I18n.t('An error occurred while updating announcements locked state.'),
|
||||
}))
|
||||
dispatch(
|
||||
actions.lockAnnouncementsFail({
|
||||
err: res.failures,
|
||||
message: I18n.t('An error occurred while updating announcements locked state.')
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(actions.lockAnnouncementsFail({ err, message: I18n.t('An error occurred while locking announcements.') }))
|
||||
dispatch(
|
||||
actions.lockAnnouncementsFail({
|
||||
err,
|
||||
message: I18n.t('An error occurred while locking announcements.')
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
actions.announcementSelectionChangeStart = ({ selected , id }) => (dispatch, getState) => {
|
||||
dispatch(actions.setAnnouncementSelection({ selected , id }))
|
||||
actions.announcementSelectionChangeStart = ({selected, id}) => (dispatch, getState) => {
|
||||
dispatch(actions.setAnnouncementSelection({selected, id}))
|
||||
const state = getState()
|
||||
const { announcements } = state
|
||||
const { items } = announcements.pages[announcements.currentPage]
|
||||
const {announcements} = state
|
||||
const {items} = announcements.pages[announcements.currentPage]
|
||||
|
||||
const selectedItems = items.filter(item =>
|
||||
state.selectedAnnouncements.includes(item.id))
|
||||
const selectedItems = items.filter(item => state.selectedAnnouncements.includes(item.id))
|
||||
|
||||
// if all the selected items are locked, we want to unlock
|
||||
// if any of the selected items are unlocked, we lock everything
|
||||
const hasUnlockedItems = selectedItems
|
||||
.reduce((hasAnyUnlocked, item) => hasAnyUnlocked || !item.locked, false)
|
||||
const hasUnlockedItems = selectedItems.reduce(
|
||||
(hasAnyUnlocked, item) => hasAnyUnlocked || !item.locked,
|
||||
false
|
||||
)
|
||||
|
||||
dispatch(actions.setAnnouncementsIsLocking(hasUnlockedItems))
|
||||
}
|
||||
|
||||
actions.toggleSelectedAnnouncementsLock = () => (dispatch, getState) => {
|
||||
const state = getState()
|
||||
const { announcements } = state
|
||||
const { items } = announcements.pages[announcements.currentPage]
|
||||
const {announcements} = state
|
||||
const {items} = announcements.pages[announcements.currentPage]
|
||||
|
||||
const selectedItems = items.filter(item =>
|
||||
state.selectedAnnouncements.includes(item.id))
|
||||
const selectedItems = items.filter(item => state.selectedAnnouncements.includes(item.id))
|
||||
|
||||
// if all the selected items are locked, we want to unlock
|
||||
// if any of the selected items are unlocked, we lock everything
|
||||
const hasUnlockedItems = selectedItems
|
||||
.reduce((hasAnyUnlocked, item) => hasAnyUnlocked || !item.locked, false)
|
||||
const hasUnlockedItems = selectedItems.reduce(
|
||||
(hasAnyUnlocked, item) => hasAnyUnlocked || !item.locked,
|
||||
false
|
||||
)
|
||||
|
||||
actions.toggleAnnouncementsLock(state.selectedAnnouncements, hasUnlockedItems)(dispatch, getState)
|
||||
dispatch(actions.setAnnouncementsIsLocking(!hasUnlockedItems)) // isLocking
|
||||
}
|
||||
|
||||
actions.deleteAnnouncements = (announcements) => (dispatch, getState) => {
|
||||
actions.deleteAnnouncements = announcements => (dispatch, getState) => {
|
||||
dispatch(actions.deleteAnnouncementsStart())
|
||||
apiClient.deleteAnnouncements(getState(), [].concat(announcements))
|
||||
apiClient
|
||||
.deleteAnnouncements(getState(), [].concat(announcements))
|
||||
.then(res => {
|
||||
if (res.successes.length) {
|
||||
const pageState = getState().announcements
|
||||
dispatch(actions.deleteAnnouncementsSuccess(res))
|
||||
|
||||
// uncache all pages after this page, as they are no longer correct once you delete items
|
||||
dispatch(actions.clearAnnouncementsPage({ pages: range(pageState.currentPage, pageState.lastPage + 1) }))
|
||||
dispatch(
|
||||
actions.clearAnnouncementsPage({
|
||||
pages: range(pageState.currentPage, pageState.lastPage + 1)
|
||||
})
|
||||
)
|
||||
|
||||
dispatch(notificationActions.notifyInfo({ message: I18n.t('Announcements deleted successfully') }))
|
||||
dispatch(
|
||||
notificationActions.notifyInfo({message: I18n.t('Announcements deleted successfully')})
|
||||
)
|
||||
|
||||
// reload current page after deleting items
|
||||
dispatch(actions.getAnnouncements({ page: pageState.currentPage, select: true }))
|
||||
dispatch(actions.getAnnouncements({page: pageState.currentPage, select: true}))
|
||||
} else if (res.failures.length) {
|
||||
dispatch(actions.deleteAnnouncementsFail({
|
||||
err: res.failures,
|
||||
message: I18n.t('An error occurred while deleting announcements.'),
|
||||
}))
|
||||
dispatch(
|
||||
actions.deleteAnnouncementsFail({
|
||||
err: res.failures,
|
||||
message: I18n.t('An error occurred while deleting announcements.')
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(actions.deleteAnnouncementsFail({
|
||||
err,
|
||||
message: I18n.t('An error occurred while deleting announcements.'),
|
||||
}))
|
||||
dispatch(
|
||||
actions.deleteAnnouncementsFail({
|
||||
err,
|
||||
message: I18n.t('An error occurred while deleting announcements.')
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -212,28 +244,33 @@ actions.deleteSelectedAnnouncements = () => (dispatch, getState) => {
|
|||
actions.deleteAnnouncements(state.selectedAnnouncements)(dispatch, getState)
|
||||
}
|
||||
|
||||
actions.addExternalFeed = function (payload) {
|
||||
actions.addExternalFeed = function(payload) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(actions.addExternalFeedStart())
|
||||
apiClient.addExternalFeed(getState(), payload)
|
||||
apiClient
|
||||
.addExternalFeed(getState(), payload)
|
||||
.then(resp => {
|
||||
dispatch(actions.addExternalFeedSuccess({ feed: resp.data}))
|
||||
dispatch(actions.addExternalFeedSuccess({feed: resp.data}))
|
||||
const successMessage = I18n.t('External feed successfully added')
|
||||
$.screenReaderFlashMessage(successMessage)
|
||||
dispatch(notificationActions.notifyInfo({ message: successMessage }))
|
||||
dispatch(notificationActions.notifyInfo({message: successMessage}))
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
const failMessage = I18n.t('Failed to add new feed')
|
||||
$.screenReaderFlashMessage(failMessage)
|
||||
dispatch(actions.addExternalFeedFail({
|
||||
message: failMessage,
|
||||
err
|
||||
}))
|
||||
dispatch(
|
||||
actions.addExternalFeedFail({
|
||||
message: failMessage,
|
||||
err
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const actionTypes = types.reduce((typesMap, actionType) =>
|
||||
Object.assign(typesMap, { [actionType]: actionType }), {})
|
||||
const actionTypes = types.reduce(
|
||||
(typesMap, actionType) => Object.assign(typesMap, {[actionType]: actionType}),
|
||||
{}
|
||||
)
|
||||
|
||||
export { actionTypes, actions as default }
|
||||
export {actionTypes, actions as default}
|
||||
|
|
|
@ -17,65 +17,70 @@
|
|||
*/
|
||||
|
||||
import axios from 'axios'
|
||||
import { encodeQueryString } from '../shared/queryString'
|
||||
import {encodeQueryString} from '../shared/queryString'
|
||||
import makePromisePool from '../shared/makePromisePool'
|
||||
|
||||
const MAX_CONCURRENT_REQS = 5
|
||||
|
||||
export function getAnnouncements ({ contextType, contextId, announcements, announcementsSearch }, { page }) {
|
||||
const { term, filter } = announcementsSearch
|
||||
export function getAnnouncements(
|
||||
{contextType, contextId, announcements, announcementsSearch},
|
||||
{page}
|
||||
) {
|
||||
const {term, filter} = announcementsSearch
|
||||
const params = [
|
||||
{ only_announcements: true },
|
||||
{ per_page: 40 },
|
||||
{ page: page || announcements.currentPage },
|
||||
{ search_term: term || null },
|
||||
{ filter_by: filter || null }
|
||||
{only_announcements: true},
|
||||
{per_page: 40},
|
||||
{page: page || announcements.currentPage},
|
||||
{search_term: term || null},
|
||||
{filter_by: filter || null}
|
||||
]
|
||||
|
||||
if (contextType === 'course') {
|
||||
params.push({ 'include[]': 'sections_user_count' })
|
||||
params.push({ 'include[]': 'sections' })
|
||||
params.push({'include[]': 'sections_user_count'})
|
||||
params.push({'include[]': 'sections'})
|
||||
}
|
||||
|
||||
const queryString = encodeQueryString(params)
|
||||
return axios.get(`/api/v1/${contextType}s/${contextId}/discussion_topics?${queryString}`)
|
||||
}
|
||||
|
||||
export function lockAnnouncements ({ contextType, contextId }, announcements, locked = true) {
|
||||
return makePromisePool(announcements, (annId) => {
|
||||
const url = `/api/v1/${contextType}s/${contextId}/discussion_topics/${annId}`
|
||||
return axios.put(url, { locked })
|
||||
}, {
|
||||
poolSize: MAX_CONCURRENT_REQS,
|
||||
})
|
||||
export function lockAnnouncements({contextType, contextId}, announcements, locked = true) {
|
||||
return makePromisePool(
|
||||
announcements,
|
||||
annId => {
|
||||
const url = `/api/v1/${contextType}s/${contextId}/discussion_topics/${annId}`
|
||||
return axios.put(url, {locked})
|
||||
},
|
||||
{
|
||||
poolSize: MAX_CONCURRENT_REQS
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function deleteAnnouncements ({ contextType, contextId }, announcements) {
|
||||
return makePromisePool(announcements, (annId) => {
|
||||
const url = `/api/v1/${contextType}s/${contextId}/discussion_topics/${annId}`
|
||||
return axios.delete(url)
|
||||
}, {
|
||||
poolSize: MAX_CONCURRENT_REQS,
|
||||
})
|
||||
export function deleteAnnouncements({contextType, contextId}, announcements) {
|
||||
return makePromisePool(
|
||||
announcements,
|
||||
annId => {
|
||||
const url = `/api/v1/${contextType}s/${contextId}/discussion_topics/${annId}`
|
||||
return axios.delete(url)
|
||||
},
|
||||
{
|
||||
poolSize: MAX_CONCURRENT_REQS
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function getExternalFeeds ({ contextType, contextId }) {
|
||||
const params = encodeQueryString([
|
||||
{ per_page: 100 }
|
||||
])
|
||||
export function getExternalFeeds({contextType, contextId}) {
|
||||
const params = encodeQueryString([{per_page: 100}])
|
||||
return axios.get(`/api/v1/${contextType}s/${contextId}/external_feeds?${params}`)
|
||||
}
|
||||
|
||||
export function deleteExternalFeed ({ contextType, contextId}, feedId) {
|
||||
export function deleteExternalFeed({contextType, contextId}, feedId) {
|
||||
return axios.delete(`/api/v1/${contextType}s/${contextId}/external_feeds/${feedId}`)
|
||||
}
|
||||
|
||||
export function addExternalFeed ({ contextType, contextId }, { url, verbosity, header_match }) {
|
||||
const params = encodeQueryString([
|
||||
{ url },
|
||||
{ verbosity },
|
||||
{ header_match: header_match || null }
|
||||
])
|
||||
export function addExternalFeed({contextType, contextId}, {url, verbosity, header_match}) {
|
||||
const params = encodeQueryString([{url}, {verbosity}, {header_match: header_match || null}])
|
||||
|
||||
return axios.post(`/api/v1/${contextType}s/${contextId}/external_feeds?${params}`)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import {bool, func} from 'prop-types'
|
|||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
import $ from 'jquery'
|
||||
import 'compiled/jquery.rails_flash_notifications'
|
||||
import 'compiled/jquery.rails_flash_notifications' // eslint-disable-line
|
||||
|
||||
import Button from '@instructure/ui-buttons/lib/components/Button'
|
||||
import View from '@instructure/ui-layout/lib/components/View'
|
||||
|
@ -33,7 +33,7 @@ import RadioInput from '@instructure/ui-forms/lib/components/RadioInput'
|
|||
import RadioInputGroup from '@instructure/ui-forms/lib/components/RadioInputGroup'
|
||||
import ScreenReaderContent from '@instructure/ui-a11y/lib/components/ScreenReaderContent'
|
||||
import ToggleDetails from '@instructure/ui-toggle-details/lib/components/ToggleDetails'
|
||||
import { ConnectedRSSFeedList } from './RSSFeedList'
|
||||
import {ConnectedRSSFeedList} from './RSSFeedList'
|
||||
|
||||
import actions from '../actions'
|
||||
import select from '../../shared/select'
|
||||
|
@ -47,7 +47,7 @@ const verbosityTypes = [
|
|||
export default class AddExternalFeed extends React.Component {
|
||||
static propTypes = {
|
||||
defaultOpen: bool,
|
||||
isSaving: bool.isRequired,
|
||||
isSaving: bool.isRequired, // eslint-disable-line
|
||||
addExternalFeed: func.isRequired
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ export default class AddExternalFeed extends React.Component {
|
|||
|
||||
focusOnToggleHeader = () => {
|
||||
setTimeout(() => {
|
||||
this.toggleBtn.focus();
|
||||
this.toggleBtn.focus()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -80,9 +80,12 @@ export default class AddExternalFeed extends React.Component {
|
|||
|
||||
toggleOpenState = (event, expanded) => {
|
||||
$.screenReaderFlashMessage(I18n.t('dropdown changed state to %{expanded}.', {expanded}))
|
||||
this.setState({
|
||||
isOpen: expanded
|
||||
}, this.focusOnToggleHeader)
|
||||
this.setState(
|
||||
{
|
||||
isOpen: expanded
|
||||
},
|
||||
this.focusOnToggleHeader
|
||||
)
|
||||
}
|
||||
|
||||
clearAddRSS = () => {
|
||||
|
@ -126,7 +129,7 @@ export default class AddExternalFeed extends React.Component {
|
|||
)
|
||||
}
|
||||
|
||||
toggleRef = (c) => {
|
||||
toggleRef = c => {
|
||||
this.toggleBtn = c && c.querySelector('button')
|
||||
}
|
||||
|
||||
|
@ -152,7 +155,7 @@ export default class AddExternalFeed extends React.Component {
|
|||
textAlign="start"
|
||||
className="announcements-tray__rss-feed-root"
|
||||
>
|
||||
<ConnectedRSSFeedList focusLastElement={this.focusOnToggleHeader}/>
|
||||
<ConnectedRSSFeedList focusLastElement={this.focusOnToggleHeader} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
@ -235,7 +238,11 @@ export default class AddExternalFeed extends React.Component {
|
|||
expanded={this.state.isOpen}
|
||||
name="external-rss-feed__toggle"
|
||||
>
|
||||
<Dialog open={this.state.isOpen} shouldReturnFocus defaultFocusElement={() => this.toggleBtn}>
|
||||
<Dialog
|
||||
open={this.state.isOpen}
|
||||
shouldReturnFocus
|
||||
defaultFocusElement={() => this.toggleBtn}
|
||||
>
|
||||
{this.renderTextInput(
|
||||
this.state.feedURL,
|
||||
I18n.t('Feed url'),
|
||||
|
@ -260,4 +267,7 @@ const connectState = state =>
|
|||
})
|
||||
const connectActions = dispatch =>
|
||||
bindActionCreators(select(actions, ['addExternalFeed']), dispatch)
|
||||
export const ConnectedAddExternalFeed = connect(connectState, connectActions)(AddExternalFeed)
|
||||
export const ConnectedAddExternalFeed = connect(
|
||||
connectState,
|
||||
connectActions
|
||||
)(AddExternalFeed)
|
||||
|
|
|
@ -31,7 +31,7 @@ const AnnouncementEmptyState = props => (
|
|||
<View margin="large auto" textAlign="center" display="block">
|
||||
<PresentationContent>
|
||||
<View margin="small auto" size="x-small" display="block">
|
||||
<SVGWrapper url="/images/announcements/announcements-airhorn.svg"/>
|
||||
<SVGWrapper url="/images/announcements/announcements-airhorn.svg" />
|
||||
</View>
|
||||
</PresentationContent>
|
||||
<Heading margin="x-small">{I18n.t('No Announcements')}</Heading>
|
||||
|
|
|
@ -55,11 +55,11 @@ export default class AnnouncementsIndex extends Component {
|
|||
masterCourseData: masterCourseDataShape,
|
||||
deleteAnnouncements: func.isRequired,
|
||||
toggleAnnouncementsLock: func.isRequired,
|
||||
announcementsLocked: bool.isRequired,
|
||||
announcementsLocked: bool.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
masterCourseData: null,
|
||||
masterCourseData: null
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -68,23 +68,25 @@ export default class AnnouncementsIndex extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
onManageAnnouncement = (e, { action, id, lock }) => {
|
||||
onManageAnnouncement = (e, {action, id, lock}) => {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
showConfirmDelete({
|
||||
selectedCount: 1,
|
||||
modalRef: (modal) => { this.deleteModal = modal },
|
||||
modalRef: modal => {
|
||||
this.deleteModal = modal
|
||||
},
|
||||
onConfirm: () => {
|
||||
this.props.deleteAnnouncements(id)
|
||||
if (this.searchInput) this.searchInput.focus()
|
||||
},
|
||||
}
|
||||
})
|
||||
break;
|
||||
break
|
||||
case 'lock':
|
||||
this.props.toggleAnnouncementsLock(id, lock)
|
||||
break;
|
||||
break
|
||||
default:
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,13 +96,7 @@ export default class AnnouncementsIndex extends Component {
|
|||
|
||||
renderEmptyAnnouncements() {
|
||||
if (this.props.hasLoadedAnnouncements && !this.props.announcements.length) {
|
||||
return (
|
||||
<AnnouncementEmptyState
|
||||
canCreate={
|
||||
this.props.permissions.create
|
||||
}
|
||||
/>
|
||||
)
|
||||
return <AnnouncementEmptyState canCreate={this.props.permissions.create} />
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
@ -154,12 +150,8 @@ export default class AnnouncementsIndex extends Component {
|
|||
onClick={this.selectPage(page)}
|
||||
current={page === this.props.announcementsPage}
|
||||
>
|
||||
<ScreenReaderContent>
|
||||
{I18n.t('Page %{pageNum}', {pageNum: page})}
|
||||
</ScreenReaderContent>
|
||||
<span aria-hidden="true">
|
||||
{page}
|
||||
</span>
|
||||
<ScreenReaderContent>{I18n.t('Page %{pageNum}', {pageNum: page})}</ScreenReaderContent>
|
||||
<span aria-hidden="true">{page}</span>
|
||||
</PaginationButton>
|
||||
)
|
||||
}
|
||||
|
@ -188,7 +180,11 @@ export default class AnnouncementsIndex extends Component {
|
|||
<ScreenReaderContent>
|
||||
<Heading level="h1">{I18n.t('Announcements')}</Heading>
|
||||
</ScreenReaderContent>
|
||||
<ConnectedIndexHeader searchInputRef={(c) => { this.searchInput = c }} />
|
||||
<ConnectedIndexHeader
|
||||
searchInputRef={c => {
|
||||
this.searchInput = c
|
||||
}}
|
||||
/>
|
||||
{this.renderSpinner(this.props.isLoadingAnnouncements, I18n.t('Loading Announcements'))}
|
||||
{this.renderEmptyAnnouncements()}
|
||||
{this.renderAnnouncements()}
|
||||
|
@ -201,14 +197,23 @@ export default class AnnouncementsIndex extends Component {
|
|||
const connectState = state =>
|
||||
Object.assign(
|
||||
{
|
||||
isCourseContext: state.contextType === 'course',
|
||||
isCourseContext: state.contextType === 'course'
|
||||
},
|
||||
selectPaginationState(state, 'announcements'),
|
||||
select(state, ['permissions', 'masterCourseData', 'announcementsLocked'])
|
||||
)
|
||||
const connectActions = dispatch =>
|
||||
bindActionCreators(select(actions,
|
||||
['getAnnouncements', 'announcementSelectionChangeStart', 'deleteAnnouncements', 'toggleAnnouncementsLock']
|
||||
), dispatch)
|
||||
bindActionCreators(
|
||||
select(actions, [
|
||||
'getAnnouncements',
|
||||
'announcementSelectionChangeStart',
|
||||
'deleteAnnouncements',
|
||||
'toggleAnnouncementsLock'
|
||||
]),
|
||||
dispatch
|
||||
)
|
||||
|
||||
export const ConnectedAnnouncementsIndex = connect(connectState, connectActions)(AnnouncementsIndex)
|
||||
export const ConnectedAnnouncementsIndex = connect(
|
||||
connectState,
|
||||
connectActions
|
||||
)(AnnouncementsIndex)
|
||||
|
|
|
@ -21,19 +21,22 @@ import React, {Component} from 'react'
|
|||
import ReactDOM from 'react-dom'
|
||||
import {func, number, node} from 'prop-types'
|
||||
|
||||
import Modal, { ModalBody, ModalFooter } from '../../shared/components/InstuiModal'
|
||||
import Modal, {ModalBody, ModalFooter} from '../../shared/components/InstuiModal'
|
||||
import Button from '@instructure/ui-buttons/lib/components/Button'
|
||||
|
||||
export function showConfirmDelete (props) {
|
||||
export function showConfirmDelete(props) {
|
||||
const parent = document.createElement('div')
|
||||
parent.setAttribute('class', 'confirm-delete-modal-container')
|
||||
document.body.appendChild(parent)
|
||||
|
||||
function showConfirmDeleteRef (modal) {
|
||||
function showConfirmDeleteRef(modal) {
|
||||
if (modal) modal.show()
|
||||
}
|
||||
|
||||
ReactDOM.render(<ConfirmDeleteModal {...props} parent={parent} ref={showConfirmDeleteRef} />, parent)
|
||||
ReactDOM.render(
|
||||
<ConfirmDeleteModal {...props} parent={parent} ref={showConfirmDeleteRef} />,
|
||||
parent
|
||||
)
|
||||
}
|
||||
|
||||
export default class ConfirmDeleteModal extends Component {
|
||||
|
@ -43,25 +46,25 @@ export default class ConfirmDeleteModal extends Component {
|
|||
onCancel: func,
|
||||
onHide: func,
|
||||
modalRef: func,
|
||||
parent: node,
|
||||
parent: node
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
onCancel: null,
|
||||
onHide: null,
|
||||
parent: null,
|
||||
modalRef: null,
|
||||
modalRef: null
|
||||
}
|
||||
|
||||
state = {
|
||||
show: false,
|
||||
show: false
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
if (this.props.modalRef) this.props.modalRef(this)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
if (this.props.modalRef) this.props.modalRef(null)
|
||||
}
|
||||
|
||||
|
@ -75,19 +78,18 @@ export default class ConfirmDeleteModal extends Component {
|
|||
this.hide()
|
||||
}
|
||||
|
||||
show () {
|
||||
this.setState({ show: true })
|
||||
show() {
|
||||
this.setState({show: true})
|
||||
}
|
||||
|
||||
hide () {
|
||||
this.setState({ show: false },
|
||||
() => {
|
||||
if (this.props.onHide) setTimeout(this.props.onHide)
|
||||
if (this.props.parent) ReactDOM.unmountComponentAtNode(this.props.parent)
|
||||
})
|
||||
hide() {
|
||||
this.setState({show: false}, () => {
|
||||
if (this.props.onHide) setTimeout(this.props.onHide)
|
||||
if (this.props.parent) ReactDOM.unmountComponentAtNode(this.props.parent)
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<Modal
|
||||
open={this.state.show}
|
||||
|
@ -96,24 +98,35 @@ export default class ConfirmDeleteModal extends Component {
|
|||
label={I18n.t('Confirm Delete')}
|
||||
>
|
||||
<ModalBody>
|
||||
{I18n.t({
|
||||
one: 'You are about to delete 1 announcement. Are you sure?',
|
||||
other: 'You are about to delete %{count} announcements. Are you sure?',
|
||||
}, { count: this.props.selectedCount })}
|
||||
{I18n.t(
|
||||
{
|
||||
one: 'You are about to delete 1 announcement. Are you sure?',
|
||||
other: 'You are about to delete %{count} announcements. Are you sure?'
|
||||
},
|
||||
{count: this.props.selectedCount}
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
ref={(c) => {this.cancelBtn = c}}
|
||||
ref={c => {
|
||||
this.cancelBtn = c
|
||||
}}
|
||||
onClick={this.onCancel}
|
||||
>{I18n.t('Cancel')}</Button>
|
||||
>
|
||||
{I18n.t('Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
ref={(c) => {this.confirmBtn = c}}
|
||||
ref={c => {
|
||||
this.confirmBtn = c
|
||||
}}
|
||||
id="confirm_delete_announcements"
|
||||
onClick={this.onConfirm}
|
||||
variant="danger">{I18n.t('Delete')}</Button>
|
||||
variant="danger"
|
||||
>
|
||||
{I18n.t('Delete')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
*/
|
||||
|
||||
import I18n from 'i18n!announcements_v2'
|
||||
import React, { Component } from 'react'
|
||||
import { string } from 'prop-types'
|
||||
import React, {Component} from 'react'
|
||||
import {string} from 'prop-types'
|
||||
|
||||
import Tray from '@instructure/ui-overlays/lib/components/Tray'
|
||||
import Link from '@instructure/ui-elements/lib/components/Link'
|
||||
|
@ -28,7 +28,7 @@ import View from '@instructure/ui-layout/lib/components/View'
|
|||
import IconRssLine from '@instructure/ui-icons/lib/Line/IconRss'
|
||||
import Text from '@instructure/ui-elements/lib/components/Text'
|
||||
|
||||
import { ConnectedAddExternalFeed } from './AddExternalFeed'
|
||||
import {ConnectedAddExternalFeed} from './AddExternalFeed'
|
||||
import propTypes from '../propTypes'
|
||||
|
||||
export default class ExternalFeedsTray extends Component {
|
||||
|
@ -38,11 +38,11 @@ export default class ExternalFeedsTray extends Component {
|
|||
}
|
||||
|
||||
static defaultProps = {
|
||||
atomFeedUrl: null,
|
||||
atomFeedUrl: null
|
||||
}
|
||||
|
||||
state = {
|
||||
open: false,
|
||||
open: false
|
||||
}
|
||||
|
||||
renderTrayContent() {
|
||||
|
@ -57,12 +57,10 @@ export default class ExternalFeedsTray extends Component {
|
|||
|
||||
renderHeader() {
|
||||
return (
|
||||
<View
|
||||
margin="0 0 0 large"
|
||||
as="div"
|
||||
textAlign="start"
|
||||
>
|
||||
<Heading margin="small 0 0 small" level="h3" as="h2">{I18n.t('External feeds')}</Heading>
|
||||
<View margin="0 0 0 large" as="div" textAlign="start">
|
||||
<Heading margin="small 0 0 small" level="h3" as="h2">
|
||||
{I18n.t('External feeds')}
|
||||
</Heading>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
@ -70,15 +68,14 @@ export default class ExternalFeedsTray extends Component {
|
|||
renderRssFeedLink() {
|
||||
if (this.props.atomFeedUrl) {
|
||||
return (
|
||||
<View
|
||||
margin="medium"
|
||||
as="div"
|
||||
textAlign="start"
|
||||
>
|
||||
<View margin="medium" as="div" textAlign="start">
|
||||
<Link
|
||||
id="rss-feed-link"
|
||||
linkRef={(link) => {this.rssFeedLink = link}}
|
||||
href={this.props.atomFeedUrl}>
|
||||
linkRef={link => {
|
||||
this.rssFeedLink = link
|
||||
}}
|
||||
href={this.props.atomFeedUrl}
|
||||
>
|
||||
<IconRssLine />
|
||||
<View margin="0 0 0 x-small">{I18n.t('RSS Feed')}</View>
|
||||
</Link>
|
||||
|
@ -97,28 +94,31 @@ export default class ExternalFeedsTray extends Component {
|
|||
textAlign="start"
|
||||
className="announcements-tray__add-rss-root"
|
||||
>
|
||||
<Text size="medium" as="h2" weight="bold">{I18n.t("Feeds")}</Text>
|
||||
<Text size="medium" as="h2" weight="bold">
|
||||
{I18n.t('Feeds')}
|
||||
</Text>
|
||||
<div className="announcements-tray-row">
|
||||
<View
|
||||
margin="small 0 0"
|
||||
display="block"
|
||||
textAlign="start"
|
||||
>
|
||||
<ConnectedAddExternalFeed defaultOpen={false}/>
|
||||
<View margin="small 0 0" display="block" textAlign="start">
|
||||
<ConnectedAddExternalFeed defaultOpen={false} />
|
||||
</View>
|
||||
</div>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<View display="block" textAlign="end">
|
||||
<Button
|
||||
id="external_feed"
|
||||
buttonRef={(link) => {this.externalFeedRef = link}}
|
||||
onClick={() => { this.setState({ open: !this.state.open }) }}
|
||||
variant="link">
|
||||
buttonRef={link => {
|
||||
this.externalFeedRef = link
|
||||
}}
|
||||
onClick={() => {
|
||||
this.setState({open: !this.state.open})
|
||||
}}
|
||||
variant="link"
|
||||
>
|
||||
{I18n.t('External feeds')}
|
||||
</Button>
|
||||
<Tray
|
||||
|
@ -127,7 +127,7 @@ export default class ExternalFeedsTray extends Component {
|
|||
open={this.state.open}
|
||||
size="small"
|
||||
onDismiss={() => {
|
||||
this.setState({ open: false })
|
||||
this.setState({open: false})
|
||||
}}
|
||||
placement="end"
|
||||
>
|
||||
|
|
|
@ -17,16 +17,16 @@
|
|||
*/
|
||||
|
||||
import I18n from 'i18n!announcements_v2'
|
||||
import React, { Component } from 'react'
|
||||
import { string, func, bool, number } from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { debounce } from 'lodash'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import React, {Component} from 'react'
|
||||
import {string, func, bool, number} from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {debounce} from 'lodash'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import Button from '@instructure/ui-buttons/lib/components/Button'
|
||||
import TextInput from '@instructure/ui-forms/lib/components/TextInput'
|
||||
import Select from '@instructure/ui-core/lib/components/Select'
|
||||
import Grid, { GridCol, GridRow } from '@instructure/ui-layout/lib/components/Grid'
|
||||
import Grid, {GridCol, GridRow} from '@instructure/ui-layout/lib/components/Grid'
|
||||
import View from '@instructure/ui-layout/lib/components/View'
|
||||
import ScreenReaderContent from '@instructure/ui-a11y/lib/components/ScreenReaderContent'
|
||||
import PresentationContent from '@instructure/ui-a11y/lib/components/PresentationContent'
|
||||
|
@ -62,46 +62,52 @@ export default class IndexHeader extends Component {
|
|||
toggleSelectedAnnouncementsLock: func.isRequired,
|
||||
deleteSelectedAnnouncements: func.isRequired,
|
||||
searchInputRef: func,
|
||||
announcementsLocked: bool.isRequired,
|
||||
announcementsLocked: bool.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
isBusy: false,
|
||||
atomFeedUrl: null,
|
||||
selectedCount: 0,
|
||||
searchInputRef: null,
|
||||
searchInputRef: null
|
||||
}
|
||||
|
||||
onSearch = debounce(() => {
|
||||
const term = this.searchInput.value
|
||||
this.props.searchAnnouncements({ term })
|
||||
}, SEARCH_TIME_DELAY, {
|
||||
leading: false,
|
||||
trailing: true,
|
||||
})
|
||||
onSearch = debounce(
|
||||
() => {
|
||||
const term = this.searchInput.value
|
||||
this.props.searchAnnouncements({term})
|
||||
},
|
||||
SEARCH_TIME_DELAY,
|
||||
{
|
||||
leading: false,
|
||||
trailing: true
|
||||
}
|
||||
)
|
||||
|
||||
onDelete = () => {
|
||||
showConfirmDelete({
|
||||
modalRef: (modal) => { this.deleteModal = modal },
|
||||
modalRef: modal => {
|
||||
this.deleteModal = modal
|
||||
},
|
||||
selectedCount: this.props.selectedCount,
|
||||
onConfirm: () => this.props.deleteSelectedAnnouncements(),
|
||||
onHide: () => {
|
||||
const { deleteBtn, searchInput } = this
|
||||
const {deleteBtn, searchInput} = this
|
||||
if (deleteBtn && deleteBtn._button && !deleteBtn._button.disabled) {
|
||||
deleteBtn.focus()
|
||||
} else if (searchInput) {
|
||||
searchInput.focus()
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
searchInputRef = (input) => {
|
||||
searchInputRef = input => {
|
||||
this.searchInput = input
|
||||
if (this.props.searchInputRef) this.props.searchInputRef(input)
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<View margin="0 0 medium" display="block">
|
||||
|
@ -110,17 +116,24 @@ export default class IndexHeader extends Component {
|
|||
<GridCol width={2}>
|
||||
<Select
|
||||
name="filter-dropdown"
|
||||
onChange={(e) => this.props.searchAnnouncements({filter: e.target.value})}
|
||||
onChange={e => this.props.searchAnnouncements({filter: e.target.value})}
|
||||
size="medium"
|
||||
label={<ScreenReaderContent>{I18n.t('Announcement Filter')}</ScreenReaderContent>}>
|
||||
{
|
||||
Object.keys(filters).map((filter) => (<option key={filter} value={filter}>{filters[filter]}</option>))
|
||||
}
|
||||
label={<ScreenReaderContent>{I18n.t('Announcement Filter')}</ScreenReaderContent>}
|
||||
>
|
||||
{Object.keys(filters).map(filter => (
|
||||
<option key={filter} value={filter}>
|
||||
{filters[filter]}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</GridCol>
|
||||
<GridCol width={4}>
|
||||
<TextInput
|
||||
label={<ScreenReaderContent>{I18n.t('Search announcements by title')}</ScreenReaderContent>}
|
||||
label={
|
||||
<ScreenReaderContent>
|
||||
{I18n.t('Search announcements by title')}
|
||||
</ScreenReaderContent>
|
||||
}
|
||||
placeholder={I18n.t('Search')}
|
||||
icon={() => <IconSearchLine />}
|
||||
ref={this.searchInputRef}
|
||||
|
@ -129,8 +142,7 @@ export default class IndexHeader extends Component {
|
|||
/>
|
||||
</GridCol>
|
||||
<GridCol width={6} textAlign="end">
|
||||
{
|
||||
this.props.permissions.manage_content &&
|
||||
{this.props.permissions.manage_content &&
|
||||
!this.props.announcementsLocked &&
|
||||
(this.props.isToggleLocking ? (
|
||||
<Button
|
||||
|
@ -141,7 +153,9 @@ export default class IndexHeader extends Component {
|
|||
onClick={this.props.toggleSelectedAnnouncementsLock}
|
||||
>
|
||||
<IconLock />
|
||||
<ScreenReaderContent>{I18n.t('Lock Selected Announcements')}</ScreenReaderContent>
|
||||
<ScreenReaderContent>
|
||||
{I18n.t('Lock Selected Announcements')}
|
||||
</ScreenReaderContent>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
|
@ -152,44 +166,70 @@ export default class IndexHeader extends Component {
|
|||
onClick={this.props.toggleSelectedAnnouncementsLock}
|
||||
>
|
||||
<IconUnlock />
|
||||
<ScreenReaderContent>{I18n.t('Unlock Selected Announcements')}</ScreenReaderContent>
|
||||
<ScreenReaderContent>
|
||||
{I18n.t('Unlock Selected Announcements')}
|
||||
</ScreenReaderContent>
|
||||
</Button>
|
||||
))
|
||||
}
|
||||
{this.props.permissions.manage_content &&
|
||||
))}
|
||||
{this.props.permissions.manage_content && (
|
||||
<Button
|
||||
disabled={this.props.isBusy || this.props.selectedCount === 0}
|
||||
size="medium"
|
||||
margin="0 small 0 0"
|
||||
id="delete_announcements"
|
||||
onClick={this.onDelete}
|
||||
ref={(c) => { this.deleteBtn = c }}
|
||||
><IconTrash /><ScreenReaderContent>{I18n.t('Delete Selected Announcements')}</ScreenReaderContent></Button>}
|
||||
{this.props.permissions.create &&
|
||||
ref={c => {
|
||||
this.deleteBtn = c
|
||||
}}
|
||||
>
|
||||
<IconTrash />
|
||||
<ScreenReaderContent>
|
||||
{I18n.t('Delete Selected Announcements')}
|
||||
</ScreenReaderContent>
|
||||
</Button>
|
||||
)}
|
||||
{this.props.permissions.create && (
|
||||
<Button
|
||||
href={`/${this.props.contextType}s/${this.props.contextId}/discussion_topics/new?is_announcement=true`}
|
||||
href={`/${this.props.contextType}s/${
|
||||
this.props.contextId
|
||||
}/discussion_topics/new?is_announcement=true`}
|
||||
variant="primary"
|
||||
id="add_announcement"
|
||||
><IconPlus />
|
||||
>
|
||||
<IconPlus />
|
||||
<ScreenReaderContent>{I18n.t('Add announcement')}</ScreenReaderContent>
|
||||
<PresentationContent>{I18n.t('Announcement')}</PresentationContent>
|
||||
</Button>
|
||||
}
|
||||
)}
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
</Grid>
|
||||
</View>
|
||||
<ExternalFeedsTray atomFeedUrl={this.props.atomFeedUrl} permissions={this.props.permissions} />
|
||||
<ExternalFeedsTray
|
||||
atomFeedUrl={this.props.atomFeedUrl}
|
||||
permissions={this.props.permissions}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const connectState = state => Object.assign({
|
||||
isBusy: state.isLockingAnnouncements || state.isDeletingAnnouncements,
|
||||
selectedCount: state.selectedAnnouncements.length,
|
||||
isToggleLocking: state.isToggleLocking,
|
||||
}, select(state, ['contextType', 'contextId', 'permissions', 'atomFeedUrl', 'announcementsLocked']))
|
||||
const selectedActions = ['searchAnnouncements', 'toggleSelectedAnnouncementsLock', 'deleteSelectedAnnouncements']
|
||||
const connectState = state =>
|
||||
Object.assign(
|
||||
{
|
||||
isBusy: state.isLockingAnnouncements || state.isDeletingAnnouncements,
|
||||
selectedCount: state.selectedAnnouncements.length,
|
||||
isToggleLocking: state.isToggleLocking
|
||||
},
|
||||
select(state, ['contextType', 'contextId', 'permissions', 'atomFeedUrl', 'announcementsLocked'])
|
||||
)
|
||||
const selectedActions = [
|
||||
'searchAnnouncements',
|
||||
'toggleSelectedAnnouncementsLock',
|
||||
'deleteSelectedAnnouncements'
|
||||
]
|
||||
const connectActions = dispatch => bindActionCreators(select(actions, selectedActions), dispatch)
|
||||
export const ConnectedIndexHeader = connect(connectState, connectActions)(IndexHeader)
|
||||
export const ConnectedIndexHeader = connect(
|
||||
connectState,
|
||||
connectActions
|
||||
)(IndexHeader)
|
||||
|
|
|
@ -56,8 +56,12 @@ export default class RSSFeedList extends React.Component {
|
|||
|
||||
deleteExternalFeed = (id, index) => {
|
||||
this.props.deleteExternalFeed({feedId: id})
|
||||
const previousIndex = index - 1;
|
||||
const elFocus = index ? () => { document.getElementById(`feed-row-${previousIndex}`).focus() } : this.props.focusLastElement
|
||||
const previousIndex = index - 1
|
||||
const elFocus = index
|
||||
? () => {
|
||||
document.getElementById(`feed-row-${previousIndex}`).focus()
|
||||
}
|
||||
: this.props.focusLastElement
|
||||
|
||||
setTimeout(() => {
|
||||
elFocus()
|
||||
|
@ -75,11 +79,17 @@ export default class RSSFeedList extends React.Component {
|
|||
)
|
||||
}
|
||||
|
||||
renderFeedRow({ display_name, id, external_feed_entries_count = 0, url }, index) {
|
||||
renderFeedRow({display_name, id, external_feed_entries_count = 0, url}, index) {
|
||||
return (
|
||||
<div key={id} className="announcements-tray-feed-row">
|
||||
<View margin="small 0" display="block">
|
||||
<Grid startAt="medium" vAlign="middle" colSpacing="small" hAlign="space-around" rowSpacing="small">
|
||||
<Grid
|
||||
startAt="medium"
|
||||
vAlign="middle"
|
||||
colSpacing="small"
|
||||
hAlign="space-around"
|
||||
rowSpacing="small"
|
||||
>
|
||||
<GridRow>
|
||||
<GridCol>
|
||||
<Link margin="0 small" href={url}>
|
||||
|
@ -101,7 +111,7 @@ export default class RSSFeedList extends React.Component {
|
|||
size="small"
|
||||
placement="end"
|
||||
>
|
||||
<IconXLine title={I18n.t('Delete %{feedName}', { feedName: display_name })} />
|
||||
<IconXLine title={I18n.t('Delete %{feedName}', {feedName: display_name})} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
|
@ -121,9 +131,7 @@ export default class RSSFeedList extends React.Component {
|
|||
} else {
|
||||
return (
|
||||
<View id="external_rss_feed__rss-list" display="block" textAlign="start">
|
||||
{this.props.feeds.map((feed, index) =>
|
||||
this.renderFeedRow(feed, index)
|
||||
)}
|
||||
{this.props.feeds.map((feed, index) => this.renderFeedRow(feed, index))}
|
||||
<div className="announcements-tray-row" />
|
||||
</View>
|
||||
)
|
||||
|
@ -141,4 +149,7 @@ const connectActions = dispatch =>
|
|||
Object.assign(select(actions, ['getExternalFeeds', 'deleteExternalFeed'])),
|
||||
dispatch
|
||||
)
|
||||
export const ConnectedRSSFeedList = connect(connectState, connectActions)(RSSFeedList)
|
||||
export const ConnectedRSSFeedList = connect(
|
||||
connectState,
|
||||
connectActions
|
||||
)(RSSFeedList)
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
|
||||
import '@instructure/ui-themes/lib/canvas'
|
||||
import React from 'react'
|
||||
import { shallow } from 'enzyme'
|
||||
import {shallow} from 'enzyme'
|
||||
import AddExternalFeed from '../AddExternalFeed'
|
||||
|
||||
const defaultProps = () => ({
|
||||
defaultOpen: false,
|
||||
isSaving: false,
|
||||
addExternalFeed: () => {},
|
||||
addExternalFeed: () => {}
|
||||
})
|
||||
|
||||
test('renders the AddExternalFeed component', () => {
|
||||
|
@ -76,7 +76,11 @@ test('submits the AddExternalFeed with correct arguments', () => {
|
|||
})
|
||||
tree.instance().handleRadioSelectionSetVerbosity('full')
|
||||
tree.instance().addRssSelection()
|
||||
expect(addFeedSpy.mock.calls[0][0]).toMatchObject({"header_match": "phrase", "url": "url", "verbosity": "full"})
|
||||
expect(addFeedSpy.mock.calls[0][0]).toMatchObject({
|
||||
header_match: 'phrase',
|
||||
url: 'url',
|
||||
verbosity: 'full'
|
||||
})
|
||||
})
|
||||
|
||||
test('isDoneSelecting correctly returns true when all arguments are set', () => {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import '@instructure/ui-themes/lib/canvas'
|
||||
import React from 'react'
|
||||
import { mount } from 'enzyme'
|
||||
import {mount} from 'enzyme'
|
||||
import AnnouncementEmptyState from '../AnnouncementEmptyState'
|
||||
|
||||
const defaultProps = () => ({
|
||||
|
|
|
@ -18,12 +18,12 @@
|
|||
|
||||
import '@instructure/ui-themes/lib/canvas'
|
||||
import React from 'react'
|
||||
import { mount, shallow } from 'enzyme'
|
||||
import {mount, shallow} from 'enzyme'
|
||||
import ExternalFeedsTray from '../ExternalFeedsTray'
|
||||
import { ConnectedRSSFeedList } from '../RSSFeedList'
|
||||
import {ConnectedRSSFeedList} from '../RSSFeedList'
|
||||
|
||||
const defaultProps = () => ({
|
||||
atomFeedUrl: "www.test.com",
|
||||
atomFeedUrl: 'www.test.com',
|
||||
permissions: {
|
||||
create: false,
|
||||
manage_content: false,
|
||||
|
@ -37,7 +37,7 @@ test('renders the ExternalFeedsTray component', () => {
|
|||
})
|
||||
|
||||
test('renders the AddExternalFeed component when user has permissions', () => {
|
||||
const props = defaultProps();
|
||||
const props = defaultProps()
|
||||
props.permissions = {
|
||||
create: true,
|
||||
manage_content: false,
|
||||
|
@ -49,7 +49,7 @@ test('renders the AddExternalFeed component when user has permissions', () => {
|
|||
})
|
||||
|
||||
test('does not render the AddExternalFeed component when user is student', () => {
|
||||
const props = defaultProps();
|
||||
const props = defaultProps()
|
||||
props.permissions = {
|
||||
create: false,
|
||||
manage_content: false,
|
||||
|
@ -61,7 +61,7 @@ test('does not render the AddExternalFeed component when user is student', () =>
|
|||
})
|
||||
|
||||
test('does not render the RSSFeedList component when user is student', () => {
|
||||
const props = defaultProps();
|
||||
const props = defaultProps()
|
||||
props.permissions = {
|
||||
create: false,
|
||||
manage_content: false,
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import '@instructure/ui-themes/lib/canvas'
|
||||
import React from 'react'
|
||||
import { mount } from 'enzyme'
|
||||
import {mount} from 'enzyme'
|
||||
import RSSFeedList from '../RSSFeedList'
|
||||
|
||||
const defaultProps = () => ({
|
||||
|
@ -58,7 +58,7 @@ const defaultFeeds = () => [
|
|||
id: '55',
|
||||
external_feed_entries_count: 5,
|
||||
url: 'donotcare.com'
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
test('renders the RSSFeedList component', () => {
|
||||
|
@ -129,5 +129,5 @@ test('calls deleteExternalFeed with correct feed ID when deleting feed', () => {
|
|||
const instance = tree.instance()
|
||||
instance.deleteExternalFeed('22')
|
||||
expect(props.deleteExternalFeed.mock.calls).toHaveLength(1)
|
||||
expect(props.deleteExternalFeed.mock.calls[0][0]).toEqual({ feedId: '22'})
|
||||
expect(props.deleteExternalFeed.mock.calls[0][0]).toEqual({feedId: '22'})
|
||||
})
|
||||
|
|
|
@ -18,20 +18,20 @@
|
|||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Provider } from 'react-redux'
|
||||
import {Provider} from 'react-redux'
|
||||
|
||||
import { subscribeFlashNotifications } from '../shared/reduxNotifications'
|
||||
import { ConnectedAnnouncementsIndex } from './components/AnnouncementsIndex'
|
||||
import {subscribeFlashNotifications} from '../shared/reduxNotifications'
|
||||
import {ConnectedAnnouncementsIndex} from './components/AnnouncementsIndex'
|
||||
import createStore from './store'
|
||||
|
||||
export default function createAnnouncementsIndex (root, data = {}) {
|
||||
export default function createAnnouncementsIndex(root, data = {}) {
|
||||
const store = createStore(data)
|
||||
|
||||
function unmount () {
|
||||
function unmount() {
|
||||
ReactDOM.unmountComponentAtNode(root)
|
||||
}
|
||||
|
||||
function render () {
|
||||
function render() {
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<ConnectedAnnouncementsIndex />
|
||||
|
@ -42,5 +42,5 @@ export default function createAnnouncementsIndex (root, data = {}) {
|
|||
|
||||
subscribeFlashNotifications(store)
|
||||
|
||||
return { unmount, render }
|
||||
return {unmount, render}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { shape, bool, string, number } from 'prop-types'
|
||||
import {shape, bool, string, number} from 'prop-types'
|
||||
|
||||
const propTypes = {}
|
||||
|
||||
propTypes.permissions = shape({
|
||||
create: bool.isRequired,
|
||||
manage_content: bool.isRequired,
|
||||
moderate: bool.isRequired,
|
||||
moderate: bool.isRequired
|
||||
})
|
||||
|
||||
propTypes.rssFeed = shape({
|
||||
|
|
|
@ -18,49 +18,47 @@
|
|||
|
||||
import uniq from 'lodash/uniq'
|
||||
import without from 'lodash/without'
|
||||
import { combineReducers } from 'redux'
|
||||
import { handleActions } from 'redux-actions'
|
||||
import { actionTypes } from './actions'
|
||||
import { reduceNotifications } from '../shared/reduxNotifications'
|
||||
import { createPaginatedReducer } from '../shared/reduxPagination'
|
||||
import {combineReducers} from 'redux'
|
||||
import {handleActions} from 'redux-actions'
|
||||
import {actionTypes} from './actions'
|
||||
import {reduceNotifications} from '../shared/reduxNotifications'
|
||||
import {createPaginatedReducer} from '../shared/reduxPagination'
|
||||
|
||||
const MIN_SEATCH_LENGTH = 3
|
||||
|
||||
const identity = (defaultState = null) => (
|
||||
state => (state === undefined ? defaultState : state)
|
||||
)
|
||||
const identity = (defaultState = null) => state => (state === undefined ? defaultState : state)
|
||||
|
||||
const reduceAnnouncementsPagination = createPaginatedReducer('announcements')
|
||||
|
||||
const reduceItems = handleActions({
|
||||
[actionTypes.LOCK_ANNOUNCEMENTS_SUCCESS]: (state, action) => {
|
||||
const successIds = action.payload.res.successes.map(success => success.data)
|
||||
return state.map(item => {
|
||||
return successIds.includes(item.id)
|
||||
? ({ ...item, locked: action.payload.locked })
|
||||
: item
|
||||
})
|
||||
const reduceItems = handleActions(
|
||||
{
|
||||
[actionTypes.LOCK_ANNOUNCEMENTS_SUCCESS]: (state, action) => {
|
||||
const successIds = action.payload.res.successes.map(success => success.data)
|
||||
return state.map(
|
||||
item => (successIds.includes(item.id) ? {...item, locked: action.payload.locked} : item)
|
||||
)
|
||||
}
|
||||
},
|
||||
}, [])
|
||||
[]
|
||||
)
|
||||
|
||||
function reducePage (page = {}, action) {
|
||||
return ({ ...page, items: reduceItems(page.items, action) })
|
||||
function reducePage(page = {}, action) {
|
||||
return {...page, items: reduceItems(page.items, action)}
|
||||
}
|
||||
|
||||
function reduceCurrentPage (currentPage) {
|
||||
return (announcements = {}, action) =>
|
||||
({
|
||||
...announcements,
|
||||
pages: {
|
||||
...announcements.pages,
|
||||
[currentPage]: reducePage(announcements.pages[currentPage], action),
|
||||
},
|
||||
})
|
||||
function reduceCurrentPage(currentPage) {
|
||||
return (announcements = {}, action) => ({
|
||||
...announcements,
|
||||
pages: {
|
||||
...announcements.pages,
|
||||
[currentPage]: reducePage(announcements.pages[currentPage], action)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function reduceAnnouncements (announcements, action) {
|
||||
const { currentPage, pages } = announcements
|
||||
let newState = { ...announcements }
|
||||
function reduceAnnouncements(announcements, action) {
|
||||
const {currentPage, pages} = announcements
|
||||
let newState = {...announcements}
|
||||
|
||||
if (currentPage && pages && pages[currentPage]) {
|
||||
newState = reduceCurrentPage(currentPage)(announcements, action)
|
||||
|
@ -76,106 +74,137 @@ export default combineReducers({
|
|||
permissions: identity({}),
|
||||
masterCourseData: identity(null),
|
||||
atomFeedUrl: identity(null),
|
||||
isToggleLocking: handleActions({
|
||||
[actionTypes.SET_ANNOUNCEMENTS_IS_LOCKING] : (state, action) => action.payload
|
||||
}, false),
|
||||
isToggleLocking: handleActions(
|
||||
{
|
||||
[actionTypes.SET_ANNOUNCEMENTS_IS_LOCKING]: (state, action) => action.payload
|
||||
},
|
||||
false
|
||||
),
|
||||
announcements: (state, action) => {
|
||||
const paginatedState = reduceAnnouncementsPagination(state, action)
|
||||
const newState = reduceAnnouncements(paginatedState, action)
|
||||
return newState
|
||||
},
|
||||
announcementsSearch: combineReducers({
|
||||
term: handleActions({
|
||||
[actionTypes.UPDATE_ANNOUNCEMENTS_SEARCH]: (state, action) => {
|
||||
const term = action.payload && action.payload.term
|
||||
if (term === undefined) {
|
||||
return state
|
||||
} else if (term.length < MIN_SEATCH_LENGTH) {
|
||||
return ''
|
||||
} else {
|
||||
return term
|
||||
term: handleActions(
|
||||
{
|
||||
[actionTypes.UPDATE_ANNOUNCEMENTS_SEARCH]: (state, action) => {
|
||||
const term = action.payload && action.payload.term
|
||||
if (term === undefined) {
|
||||
return state
|
||||
} else if (term.length < MIN_SEATCH_LENGTH) {
|
||||
return ''
|
||||
} else {
|
||||
return term
|
||||
}
|
||||
}
|
||||
} }, ''),
|
||||
filter: handleActions({
|
||||
[actionTypes.UPDATE_ANNOUNCEMENTS_SEARCH]: (state, action) => {
|
||||
const filter = action.payload && action.payload.filter
|
||||
if (filter === undefined) {
|
||||
return state
|
||||
} else {
|
||||
return filter
|
||||
},
|
||||
''
|
||||
),
|
||||
filter: handleActions(
|
||||
{
|
||||
[actionTypes.UPDATE_ANNOUNCEMENTS_SEARCH]: (state, action) => {
|
||||
const filter = action.payload && action.payload.filter
|
||||
if (filter === undefined) {
|
||||
return state
|
||||
} else {
|
||||
return filter
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 'all'),
|
||||
},
|
||||
'all'
|
||||
)
|
||||
}),
|
||||
selectedAnnouncements: handleActions({
|
||||
[actionTypes.SET_ANNOUNCEMENT_SELECTION]: (state, action) => {
|
||||
if (action.payload.selected) {
|
||||
return uniq([...state, action.payload.id])
|
||||
} else {
|
||||
return without(state, action.payload.id)
|
||||
}
|
||||
selectedAnnouncements: handleActions(
|
||||
{
|
||||
[actionTypes.SET_ANNOUNCEMENT_SELECTION]: (state, action) => {
|
||||
if (action.payload.selected) {
|
||||
return uniq([...state, action.payload.id])
|
||||
} else {
|
||||
return without(state, action.payload.id)
|
||||
}
|
||||
},
|
||||
[actionTypes.CLEAR_ANNOUNCEMENT_SELECTIONS]: () => [],
|
||||
[actionTypes.DELETE_ANNOUNCEMENTS_SUCCESS]: () => []
|
||||
},
|
||||
[actionTypes.CLEAR_ANNOUNCEMENT_SELECTIONS]: () => [],
|
||||
[actionTypes.DELETE_ANNOUNCEMENTS_SUCCESS]: () => [],
|
||||
}, []),
|
||||
isLockingAnnouncements: handleActions({
|
||||
[actionTypes.LOCK_ANNOUNCEMENTS_START]: () => true,
|
||||
[actionTypes.LOCK_ANNOUNCEMENTS_SUCCESS]: () => false,
|
||||
[actionTypes.LOCK_ANNOUNCEMENTS_FAIL]: () => false,
|
||||
}, false),
|
||||
isDeletingAnnouncements: handleActions({
|
||||
[actionTypes.DELETE_ANNOUNCEMENTS_START]: () => true,
|
||||
[actionTypes.DELETE_ANNOUNCEMENTS_SUCCESS]: () => false,
|
||||
[actionTypes.DELETE_ANNOUNCEMENTS_FAIL]: () => false,
|
||||
}, false),
|
||||
[]
|
||||
),
|
||||
isLockingAnnouncements: handleActions(
|
||||
{
|
||||
[actionTypes.LOCK_ANNOUNCEMENTS_START]: () => true,
|
||||
[actionTypes.LOCK_ANNOUNCEMENTS_SUCCESS]: () => false,
|
||||
[actionTypes.LOCK_ANNOUNCEMENTS_FAIL]: () => false
|
||||
},
|
||||
false
|
||||
),
|
||||
isDeletingAnnouncements: handleActions(
|
||||
{
|
||||
[actionTypes.DELETE_ANNOUNCEMENTS_START]: () => true,
|
||||
[actionTypes.DELETE_ANNOUNCEMENTS_SUCCESS]: () => false,
|
||||
[actionTypes.DELETE_ANNOUNCEMENTS_FAIL]: () => false
|
||||
},
|
||||
false
|
||||
),
|
||||
externalRssFeed: combineReducers({
|
||||
isSaving: handleActions({
|
||||
[actionTypes.ADD_EXTERNAL_FEED_START]: () => true,
|
||||
[actionTypes.ADD_EXTERNAL_FEED_FAIL]: () => false,
|
||||
[actionTypes.ADD_EXTERNAL_FEED_SUCCESS]: () => false
|
||||
}, false),
|
||||
isDeleting: handleActions({
|
||||
[actionTypes.DELETE_EXTERNAL_FEED_START]: () => true,
|
||||
[actionTypes.DELETE_EXTERNAL_FEED_FAIL]: () => false,
|
||||
[actionTypes.DELETE_EXTERNAL_FEED_SUCCESS]: () => false
|
||||
}, false),
|
||||
feeds: handleActions({
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_SUCCESS]: (state, action) => {
|
||||
const feeds = action.payload && action.payload.feeds
|
||||
if (feeds === undefined || !Array.isArray(feeds)) {
|
||||
return state
|
||||
}
|
||||
return feeds
|
||||
isSaving: handleActions(
|
||||
{
|
||||
[actionTypes.ADD_EXTERNAL_FEED_START]: () => true,
|
||||
[actionTypes.ADD_EXTERNAL_FEED_FAIL]: () => false,
|
||||
[actionTypes.ADD_EXTERNAL_FEED_SUCCESS]: () => false
|
||||
},
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_FAIL]: () => [],
|
||||
[actionTypes.ADD_EXTERNAL_FEED_SUCCESS]: (state, action) => {
|
||||
const feed = action.payload && action.payload.feed
|
||||
if (feed === undefined || !feed.id) {
|
||||
return state
|
||||
}
|
||||
|
||||
const newState = state.slice();
|
||||
newState.push(feed)
|
||||
return newState
|
||||
false
|
||||
),
|
||||
isDeleting: handleActions(
|
||||
{
|
||||
[actionTypes.DELETE_EXTERNAL_FEED_START]: () => true,
|
||||
[actionTypes.DELETE_EXTERNAL_FEED_FAIL]: () => false,
|
||||
[actionTypes.DELETE_EXTERNAL_FEED_SUCCESS]: () => false
|
||||
},
|
||||
[actionTypes.DELETE_EXTERNAL_FEED_SUCCESS]: (state, action) => {
|
||||
const feedId = action.payload && action.payload.feedId
|
||||
if (feedId === undefined) {
|
||||
return state
|
||||
}
|
||||
false
|
||||
),
|
||||
feeds: handleActions(
|
||||
{
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_SUCCESS]: (state, action) => {
|
||||
const feeds = action.payload && action.payload.feeds
|
||||
if (feeds === undefined || !Array.isArray(feeds)) {
|
||||
return state
|
||||
}
|
||||
return feeds
|
||||
},
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_FAIL]: () => [],
|
||||
[actionTypes.ADD_EXTERNAL_FEED_SUCCESS]: (state, action) => {
|
||||
const feed = action.payload && action.payload.feed
|
||||
if (feed === undefined || !feed.id) {
|
||||
return state
|
||||
}
|
||||
|
||||
const removedState = state.filter(el => el.id !== feedId )
|
||||
if (removedState.length === state.length) {
|
||||
return state
|
||||
const newState = state.slice()
|
||||
newState.push(feed)
|
||||
return newState
|
||||
},
|
||||
[actionTypes.DELETE_EXTERNAL_FEED_SUCCESS]: (state, action) => {
|
||||
const feedId = action.payload && action.payload.feedId
|
||||
if (feedId === undefined) {
|
||||
return state
|
||||
}
|
||||
|
||||
const removedState = state.filter(el => el.id !== feedId)
|
||||
if (removedState.length === state.length) {
|
||||
return state
|
||||
}
|
||||
return removedState
|
||||
}
|
||||
return removedState
|
||||
}
|
||||
}, []),
|
||||
hasLoadedFeed: handleActions({
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_START]: () => false,
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_SUCCESS]: () => true,
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_FAIL]: () => true
|
||||
}, false)
|
||||
},
|
||||
[]
|
||||
),
|
||||
hasLoadedFeed: handleActions(
|
||||
{
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_START]: () => false,
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_SUCCESS]: () => true,
|
||||
[actionTypes.LOADING_EXTERNAL_FEED_FAIL]: () => true
|
||||
},
|
||||
false
|
||||
)
|
||||
}),
|
||||
notifications: reduceNotifications,
|
||||
announcementsLocked: identity(false)
|
||||
|
|
|
@ -16,16 +16,16 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { createStore, applyMiddleware } from 'redux'
|
||||
import {createStore, applyMiddleware} from 'redux'
|
||||
import ReduxThunk from 'redux-thunk'
|
||||
import rootReducer from './reducer'
|
||||
|
||||
export default function configStore (initialState) {
|
||||
export default function configStore(initialState) {
|
||||
const middleware = [
|
||||
ReduxThunk,
|
||||
|
||||
// this is so redux-logger is not included in the production webpack bundle
|
||||
(process.env.NODE_ENV !== 'production') && require('redux-logger')() // eslint-disable-line global-require
|
||||
process.env.NODE_ENV !== 'production' && require('redux-logger')() // eslint-disable-line global-require
|
||||
].filter(Boolean)
|
||||
return applyMiddleware(...middleware)(createStore)(rootReducer, initialState)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue