Skip to content

Commit

Permalink
Completes #54, #175, #179 #217
Browse files Browse the repository at this point in the history
The client fetches account info from the server, and uses this to
control the presentation of elements that require auth.
  • Loading branch information
osteele committed May 14, 2018
1 parent f8ea3de commit d3ab040
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 35 deletions.
3 changes: 2 additions & 1 deletion src/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ImportContainer from './containers/import-container';
import LabelsContainer from './containers/labels-container';
import SidebarContainer from './containers/sidebar-container';
import SubscriptionContainer from './containers/subscription-container';
import { fetchLabels, toggleSidebarCollapsed } from './data/actions';
import { fetchAccount, fetchLabels, toggleSidebarCollapsed } from './data/actions';
import setupStore from './data/setup-store';

// Remove the trailing slash, if present
Expand All @@ -27,6 +27,7 @@ const history = createHistory();
const store = setupStore(history);

// Fetch the labels
store.dispatch(fetchAccount());
store.dispatch(fetchLabels());

ReactDOM.render(
Expand Down
3 changes: 1 addition & 2 deletions src/containers/calendar-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import {
import CalendarPage from '../pages/calendar/calendar-page';
import withServerData from './with-server-data';

// const normalizeLabelName = (label, allLabels) => {};

const getVisibleEvents = (events, visibleLabels, allLabels) => {
// Filter out events that are not labeled with currently visible labels
if (!events || !visibleLabels || !allLabels) return null;
Expand All @@ -35,6 +33,7 @@ const getVisibleEvents = (events, visibleLabels, allLabels) => {

// This function passes values/objects from the Redux state to the React component as props
const mapStateToProps = state => ({
account: state.account,
events: getVisibleEvents(state.events.events, state.labels.visibleLabels, state.labels.labelList),
labels: state.labels,
...state.calendar,
Expand Down
5 changes: 3 additions & 2 deletions src/containers/sidebar-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import { push } from 'react-router-redux';
import ReactGA from 'react-ga';
import Sidebar from '../sidebar/sidebar';
import * as Actions from '../data/actions';
import { withAccountInfo } from './with-server-data';

// This function passes values/objects from the Redux state to the React component as props
const mapStateToProps = state => ({
general: state.general,
account: state.account,
currentEvent: state.events.current,
general: state.general,
isCollapsed: state.sidebar.isCollapsed,
sidebarMode: state.sidebar.mode,
possibleLabels: state.labels.labelList,
Expand Down Expand Up @@ -60,6 +61,6 @@ const mapDispatchToProps = dispatch => ({
});

// Connect props to Redux state and actions
const SidebarContainer = connect(mapStateToProps, mapDispatchToProps)(Sidebar);
const SidebarContainer = connect(mapStateToProps, mapDispatchToProps)(withAccountInfo(Sidebar));

export default SidebarContainer;
11 changes: 8 additions & 3 deletions src/containers/with-server-data.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import * as React from 'react';

// Guard the wrapped component behind a loading message, until the required
// server data has been loaded. Currently this is the set of labels. #175, #179
// and #217 will probably also check whether user auth data has been loaded.
// server data has been loaded. Currently this is the account info and the
// labels.
export const withAccountInfo = WrappedComponent => props =>
(props.account ? <WrappedComponent {...props} /> : <h1>Loading…</h1>);

// Guard the wrapped component behind a loading message, until the required
// server data has been loaded. Currently this is the labels.
const withServerData = WrappedComponent => props =>
(props.labels && props.labels.labelList ? <WrappedComponent {...props} /> : <h1>Loading</h1>);
(props.labels && props.labels.labelList ? <WrappedComponent {...props} /> : <h1>Loading</h1>);

export default withServerData;
34 changes: 31 additions & 3 deletions src/data/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const ActionTypes = {
SET_FILTER_LABEL_SELECTED: 'SET_FILTER_LABEL_SELECTED', // Sets whether or not a specific label is selected as part
// of the event filter
SET_VIEW_MODE: 'SET_VIEW_MODE', // Sets which view mode (month, week, day, etc) the calendar is in
SET_ACCOUNT: 'SET_ACCOUNT',
// Event data
SET_CURRENT_EVENT: 'SET_CURRENT_EVENT', // Keeps track of the data for the event currently being viewed or edited
FETCH_EVENTS_IF_NEEDED: 'FETCH_EVENTS_IF_NEEDED', // Triggers FETCH_EVENTS if no event data is loaded
Expand Down Expand Up @@ -157,7 +158,9 @@ export function page(direction) {
* what display mode (month, week, day, etc) the calendar is in
*/
function getPageDelta(state) {
return state.calendar.currentViewMode.daysVisible > 0 ? [state.calendar.currentViewMode.daysVisible, 'd'] : [1, 'M'];
return state.calendar.currentViewMode.daysVisible > 0
? [state.calendar.currentViewMode.daysVisible, 'd']
: [1, 'M'];
}

export function showToday() {
Expand Down Expand Up @@ -220,6 +223,27 @@ export function setViewMode(mode) {

// ########## End Calendar View Actions ########## //

/**
* Performs a server request to refresh the account info.
*/
export function fetchAccount() {
return dispatch =>
fetch(`${window.abe_url}/account`)
.then(response => response.json(), error => dispatch(displayError(error)))
.then(account => dispatch(setAccount(account)));
}

/**
* Updates the account in the Redux store.
*/
export function setAccount(account) {
const data = {
authenticated: account.authenticated,
permissions: new Set(account.permissions),
};
return { type: ActionTypes.SET_ACCOUNT, data };
}

// ########## Begin Event Data Actions ########## //

// ----- Begin general event actions ----- //
Expand All @@ -241,7 +265,9 @@ export function setCurrentEventById(id, recId) {
dispatch(setCurrentEventData(eventData));
} else {
// We don't have the data, so request it from the server
const url = recId ? `${window.abe_url}/events/${id}/${recId}` : `${window.abe_url}/events/${id}`;
const url = recId
? `${window.abe_url}/events/${id}/${recId}`
: `${window.abe_url}/events/${id}`;
axios
.get(url)
.then(response => dispatch(setCurrentEventData(response.data)))
Expand Down Expand Up @@ -525,7 +551,9 @@ export function labelVisibilityToggled(labelName) {
export function updateLabel(data) {
return () => {
// TODO: update model on success
axios.post(`${window.abe_url}/labels/${data.id}`, data).catch(error => alert(`Update label failed:\n${error}`));
axios
.post(`${window.abe_url}/labels/${data.id}`, data)
.catch(error => alert(`Update label failed:\n${error}`));
};
}

Expand Down
9 changes: 7 additions & 2 deletions src/data/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ export function general(state = {}, action) {
}
}

export function account(state = {}, _action) {
return state;
export function account(state = {}, action) {
switch (action.type) {
case ActionTypes.SET_ACCOUNT:
return action.data;
default:
return state;
}
}

export function calendar(state = {}, action) {
Expand Down
10 changes: 1 addition & 9 deletions src/data/setup-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,7 @@ export default function setupStore(history) {
},
},
},
// TODO: replace this by the response to GET /account, on implementaion of olinlibrary/ABE#214
account: {
authenticated: true,
permissions: {
add_events: true,
edit_events: true,
view_all_events: true,
},
},
account: null,
events: {
current: null,
events: null,
Expand Down
28 changes: 15 additions & 13 deletions src/sidebar/sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,22 @@ const Sidebar = (props) => {

const content = (
<div>
{!permissions.view_all_events && (
<p>
You are viewing the public calendar. Visit the calendar from on campus to see all events
and to add and edit events.
</p>
{!permissions.has('view_all_events') && (
<div>
<p>You are viewing the public calendar.</p>
<p>
<a
href="https://github.com/olinlibrary/abe-web/wiki/Calendar-Access#view-olin-community-events"
target="_black"
>
How do I see Olin Community events?
</a>
</p>
</div>
)}

{mode.LINK_PANE &&
permissions.add_events && (
permissions.has('add_events') && (
<LinkPane
addEventClicked={props.addEvent}
importICSClicked={props.importICSClicked}
Expand All @@ -38,9 +45,7 @@ const Sidebar = (props) => {
)}

{mode.EVENT_ACTIONS &&
permissions.edit_events && (
<EventActionsPane key="event-actions" className="sidebar-item" {...props} />
)}
permissions.has('edit_events') && <EventActionsPane key="event-actions" className="sidebar-item" {...props} />}

{mode.EVENT_LABELS_PANE && (
<SidebarItemContainer key="event-labels" header="Labels">
Expand Down Expand Up @@ -78,10 +83,7 @@ const Sidebar = (props) => {
return (
<div className={sidebarClasses}>
<div className="sidebar-container">
<SidebarHeader
homeClicked={props.homeClicked}
toggleSidebarCollapsed={props.toggleSidebarCollapsed}
/>
<SidebarHeader homeClicked={props.homeClicked} toggleSidebarCollapsed={props.toggleSidebarCollapsed} />
<div className="sidebar-content">{content}</div>
<Footer class="sidebar-footer" />
</div>
Expand Down

0 comments on commit d3ab040

Please sign in to comment.