-
Notifications
You must be signed in to change notification settings - Fork 15
RSS configuration and a simple admin panel #96
Changes from 19 commits
2f81e7a
7bf104e
86d2f8f
a7560d1
5cf456f
ea4b8ce
5a8c678
d377be7
248ced6
3bc0a34
b9b4920
7c5dbef
1663421
60be559
adff6cf
7ed397e
a1a8f2d
e8fe92b
0fddb27
67ddd35
caf94c9
7ee4cb4
fbdaa59
d80755b
7c1bc26
615dcd1
e62cbc6
8fecf48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import React from 'react'; | ||
import { Paper } from 'material-ui'; | ||
import Switch from 'material-ui/Switch'; | ||
import { FormControlLabel, FormGroup } from 'material-ui/Form'; | ||
|
||
const parentSiteName = require('config').default.parentSiteName; | ||
const logo = require('../images/logo.png'); | ||
const jQuery = require('jquery'); | ||
|
||
require('styles/Admin.scss'); | ||
|
||
class AdminComponent extends React.PureComponent { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
eps: props.eps, | ||
switches: [] | ||
}; | ||
this.apiEndpoint = props.apiEndpoint; | ||
} | ||
|
||
componentDidMount() { | ||
jQuery.when( | ||
// get our stored list of what episodes are enabled/disabled | ||
jQuery.ajax({ | ||
url: `${this.apiEndpoint}/admin/getEpisodes`, | ||
xhrFields: { withCredentials: true }, | ||
}), | ||
// get our list of all episodes, unfiltered since this is the admin pane | ||
jQuery.ajax({ | ||
url: `${this.apiEndpoint}/recent?filter=0`, | ||
}) | ||
).done(function (episodeStateData, allEpisodeData) { | ||
let tempSwitches = allEpisodeData[0].map(episode => { | ||
let foundElement = episodeStateData[0].find(el => el.value === episode.guid); | ||
episode.checked = foundElement ? foundElement.enabled : false; | ||
episode.value = episode.guid; | ||
return episode; | ||
}); | ||
this.setState({ | ||
switches: tempSwitches | ||
}); | ||
}.bind(this)); | ||
} | ||
|
||
renderSwitches() { | ||
return this.state.switches | ||
.map((el, index) => | ||
<FormControlLabel | ||
control={ | ||
<Switch | ||
checked={el.checked} | ||
value={el.value} | ||
onChange={this.handleClick.bind(this,index)} | ||
/> | ||
} | ||
key={el.value} | ||
label={el.title} | ||
/> | ||
); | ||
} | ||
|
||
handleClick(index) { | ||
let switches = this.state.switches; | ||
switches[index].checked = !switches[index].checked; | ||
this.setState({ | ||
switches, | ||
}, () => { | ||
jQuery.ajax({ | ||
type: 'POST', | ||
url: `${this.apiEndpoint}/admin/setEpisode`, | ||
xhrFields: { withCredentials: true }, | ||
data: { | ||
guid: switches[index].guid, | ||
enabled: switches[index].checked | ||
} | ||
}); | ||
this.forceUpdate() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we do this without There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately I was unable to figure out the React lifecycle stuff to make an update happen in a way that made sense here without just doing a |
||
}); | ||
} | ||
|
||
render() { | ||
return( | ||
<div> | ||
<Paper> | ||
<div className="hero-space"> | ||
<div className="hero-content"> | ||
<img src={logo} className="logo" alt={parentSiteName}/> | ||
<h2 className="tagline">Admin Panel</h2> | ||
</div> | ||
</div> | ||
<div className="content episodes"> | ||
<h3 className="recent-episodes">Enable/Disable Episodes</h3> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Users can see this before authenticating. Maybe that's ok cuz why would they be on this page in the first place? But we could wait until the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doing exactly this, good call. |
||
<FormGroup> | ||
{this.renderSwitches.call(this)} | ||
</FormGroup> | ||
</div> | ||
</Paper> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default AdminComponent; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ const Store = require('store'); // localStorage | |
const isSecure = window.location.protocol == 'https:'; | ||
const apiEndpoint_default = isSecure ? require('config').default.apiEndpointSsl : require('config').default.apiEndpoint; | ||
const dataBucket = require('config').default.dataBucket; | ||
const useRSS = require('config').default.useRSS; | ||
const maxClipSeconds = require('config').default.maxClipSeconds; | ||
const minClipSeconds = require('config').default.minClipSeconds; | ||
const env = require('config').default.appEnv; | ||
|
@@ -41,6 +42,7 @@ import NavBar from 'components/NavBarComponent'; | |
import Loader from 'components/LoadingAnimationComponent'; | ||
import ShareContainer from 'components/ShareContainerComponent'; | ||
import Landing from 'components/Landing'; | ||
import Admin from 'components/Admin'; | ||
import Helpers from '../helpers'; | ||
|
||
/** The root React component */ | ||
|
@@ -52,6 +54,7 @@ class AppComponent extends React.Component { | |
constructor(props) { | ||
super(props); | ||
let localState, defaultState, userState; | ||
let apiEndpoint = apiEndpoint_default; | ||
|
||
// Required for the Material UI theme | ||
injectTapEventPlugin(); | ||
|
@@ -105,11 +108,12 @@ class AppComponent extends React.Component { | |
|
||
// get episode data rendered by server | ||
this.state.eps = window.__latestEpisodes || props.eps; | ||
// BUT, if we're in a development environment, just grab the JSON file of all episodes and overwrite | ||
// BUT, if we're in a development environment, just grab the JSON/RSS file of all episodes and overwrite | ||
if (env === 'dev') { | ||
let devUrl = useRSS ? apiEndpoint + '/rss' : apiEndpoint + '/recent'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that the /rss and /recent endpoints return the same result. I'm probably missing something, but why do we need to be able to access both from the client (and are we only hitting the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call. I had originally envisioned the Re: only hitting that endpoint in dev, that's technically true but what is really happening is in production, we render the front page using the marko template, which loads the episodes using the |
||
jQuery.ajax({ | ||
method: 'GET', | ||
url: dataBucket + 'episodes.json', | ||
url: devUrl, | ||
crossDomain : true, | ||
headers: {'X-Requested-With': 'XMLHttpRequest'}, | ||
success: (data) => { | ||
|
@@ -123,7 +127,7 @@ class AppComponent extends React.Component { | |
}); | ||
window.console.error(this.props.url, status, err.toString()); | ||
}.bind(this) | ||
}) | ||
}); | ||
} | ||
|
||
this.state.episodesWithProblems = window.__inactiveEpisodes || require('config').default.episodesWithProblems; | ||
|
@@ -832,6 +836,13 @@ class AppComponent extends React.Component { | |
let content; | ||
|
||
switch(this.state.view) { | ||
case 'admin': | ||
content = | ||
<Admin | ||
eps={this.state.eps} | ||
apiEndpoint={apiEndpoint_default} | ||
/>; | ||
break; | ||
case 'about': | ||
content = | ||
<div className="content"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,3 +40,4 @@ node_modules | |
_notes | ||
|
||
public/ | ||
adminData.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,9 +2,9 @@ | |
|
||
Please refer to [the wiki](https://github.com/FeelTrainCoop/shortcut/wiki) instead. This README will soon be updated to reflect the current state of the project. | ||
|
||
# tal-server | ||
# shortcut-server | ||
A node/express server that | ||
- serves a single page application ("tal-client") | ||
- serves a single page application ("shortcut-client") | ||
- provides an API for handling social authentication for the client. Currently Twitter and Facebook are supported. | ||
- provides web hooks for updating individual episodes and the list of available episodes | ||
- ensures that only authorized requests can access episode data | ||
|
@@ -16,7 +16,6 @@ A node/express server that | |
##### Install dependencies | ||
``` | ||
npm install | ||
npm install -g nodemon | ||
``` | ||
|
||
##### Env Variables | ||
|
@@ -28,8 +27,8 @@ Copy `.env-template` to `.env` and fill in environment variables. | |
|
||
### Build & run with Docker: | ||
- Mac only: eval "$(docker-machine env default)" | ||
- `docker build -t tal-server .` | ||
- `docker run -p 3000:3000 -d tal-server` | ||
- `docker build -t shortcut-server .` | ||
- `docker run -p 3000:3000 -d shortcut-server` | ||
- server will be running at docker-machine ip, i.e. `http://192.168.99.100:3000/` | ||
|
||
### Run with Node: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think some of the es6 node requires 6.x or higher? It says 4.4.3 down here so we should update and maybe add engines to package.json There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I deleted the line in question to avoid confusion |
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe set "authenticated" here?