├── .gitignore
├── DEVELOPMENT.md
├── LICENSE
├── NOTES.txt
├── README.md
├── components
├── application.jsx
├── col.jsx
├── flash-list.jsx
├── row.jsx
└── spinner.jsx
├── css
└── midgard.css
├── fonts
├── titilliumweb-extralight-webfont.eot
├── titilliumweb-extralight-webfont.ttf
├── titilliumweb-extralight-webfont.woff
├── titilliumweb-extralight-webfont.woff2
├── titilliumweb-light-webfont.eot
├── titilliumweb-light-webfont.ttf
├── titilliumweb-light-webfont.woff
├── titilliumweb-light-webfont.woff2
├── titilliumweb-lightitalic-webfont.eot
├── titilliumweb-lightitalic-webfont.ttf
├── titilliumweb-lightitalic-webfont.woff
├── titilliumweb-lightitalic-webfont.woff2
├── titilliumweb-semibold-webfont.eot
├── titilliumweb-semibold-webfont.ttf
├── titilliumweb-semibold-webfont.woff
├── titilliumweb-semibold-webfont.woff2
├── titilliumweb-semibolditalic-webfont.eot
├── titilliumweb-semibolditalic-webfont.ttf
├── titilliumweb-semibolditalic-webfont.woff
└── titilliumweb-semibolditalic-webfont.woff2
├── img
├── criss-cross.svg
├── error.jpg
├── favicon-updates.png
├── favicon.png
├── honeycomb.svg
├── spinner.svg
└── w3c.svg
├── index.html
├── js
├── actions
│ ├── configuration.js
│ ├── last-seen.js
│ ├── mailbox.js
│ ├── messages.js
│ └── user.js
├── dispatcher.js
├── event-list.jsx
├── filter-list.jsx
├── filter-selector.jsx
├── filter-toggle.jsx
├── login.jsx
├── logout-button.jsx
├── midgard.jsx
├── show-github.jsx
├── stores
│ ├── configuration.js
│ ├── filter.js
│ ├── gh-user.js
│ ├── last-seen.js
│ ├── login.js
│ ├── mailbox.js
│ └── message.js
├── toolbar.jsx
└── utils.js
├── package-lock.json
├── package.json
└── w3c.json
/.gitignore:
--------------------------------------------------------------------------------
1 | config.json
2 | config.js
3 | node_modules/
4 | npm-debug.log
5 | js/*.min.js
6 | css/*.min.js
7 |
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 |
2 | # How to develop and deploy Pheme and Midgard
3 |
4 | [Pheme][Pheme] can be deployed on its own, it can be used for things other than [Midgard][Midgard].
5 | Midgard, however, requires Pheme.
6 |
7 | This document describes what one needs to know in order to hack on Pheme and Midgard. If you are
8 | familiar with Node, CouchDB, and React you are already on sane territory but I recommend you at
9 | least skim this document as the local specificities are laid out as well.
10 |
11 | ## IMPORTANT WARNING
12 |
13 | If you are rebuilding the Midgard code on a Mac, you are likely to get an incomprehensible
14 | error from Browserify of the type `Error: EMFILE, open '/some/path'`. That is because the number of
15 | simultaneously open files is bizarrely low on OSX, and Browserify opens a bizarrely high number
16 | of resources concurrently.
17 |
18 | In order to do that, in the environment that runs the build, you will need to run:
19 |
20 | ulimit -n 2560
21 |
22 | If you don't know that, you can waste quite some time.
23 |
24 | ## Overall Architecture
25 |
26 | The Pheme repository is a purely server-side application. It exposes a JSON API over the Web but
27 | nothing user-consumable. It is written in Node and uses [Express][Express] as well as the typical
28 | Express middleware for sessions, logging, etc.
29 |
30 | The database system is [CouchDB][CouchDB]. It is also used in a straightforward manner, with no
31 | reliance on CouchDB specificities. If needed, it could be ported to another system. The only thing
32 | that is worth knowing is that the filters that provide views on the data are used to generate actual
33 | CouchDB views. This gives them huge performance (since they're basically pre-indexed), but it means
34 | you have to remember to run the DB updater when you change the filters. If a UI were made to create
35 | filters (which might be a good idea at some point) this could be done live.
36 |
37 | Midgard is, on its side, a purely client-side application. It consumes the JSON API that Pheme
38 | exposes and simply renders it. It can be served by pretty much any Web server.
39 |
40 | It is written using [React][React], making lightweight use of the [Flux][Flux] architecture, and is
41 | built using [Browserify][Browserify]. React is its own way of thinking about Web applications that
42 | has its own learning curve (and can require a little bit of retooling of one's editor for the
43 | [JSX][JSX] part) but once you start using it it is hard to go back. It's the first framework I find
44 | to be worth the hype since jQuery (and for completely different reasons).
45 |
46 | No CSS framework is used; but the CSS does get built too using [cleancss][cleancss] (for modularity
47 | and minification).
48 |
49 | ## Installing Pheme
50 |
51 | It's pretty straightforward:
52 |
53 | git clone https://github.com/w3c/pheme
54 | cd pheme
55 | npm install -d
56 |
57 | You now need to configure the system so that it can find various bits and pieces. For this create a
58 | `config.json` at the root, with the following content:
59 |
60 | ```
61 | {
62 | // This is the port you want to run on; it can be 80 but I run it behind an nginx proxy.
63 | "port": 3042
64 | // This is the list of data sources. The keys correspond to source modules (under `sources/`).
65 | // Each source module accepts an array of instances each of which can get its own configuration.
66 | , "sources": {
67 | // The "rss" source can take an arbitrary number of RSS/Atom sources. Each of those needs to
68 | // have a `name` (which is has to be unique in the list and ismapped in the event filters),
69 | // a `url` to the RSS/Atom to poll, and an `acl` which can be `public` or `team` (and
70 | // should eventually include `member` too; unless we decide that all that's in the
71 | // dashboard is public).
72 | // Here there are two RSS sources, the official W3C news from W3C Memes, and the party line
73 | // from the W3C itself.
74 | "rss": [
75 | {
76 | "name": "W3CMemes"
77 | , "url": "http://w3cmemes.tumblr.com/rss"
78 | , "acl": "public"
79 | }
80 | , {
81 | "name": "W3C News"
82 | , "url": "http://www.w3.org/blog/news/feed/atom"
83 | , "acl": "public"
84 | }
85 | ]
86 | // The "github" source can take an arbitrary number of hook locations. The value in having
87 | // more than one is because Web hooks need to have a secret (so that people can't send you
88 | // spurious content), and it's good practice to have different secrets for different places.
89 | // Note that you can set up an organisation-wide hook (there's one for W3C).
90 | // The secret below isn't the real one for W3C. It's probably a good idea to get the real
91 | // one from @darobin if you need it.
92 | // If you have several hooks, they need to have unique names and unique paths.
93 | , "github": [
94 | {
95 | "name": "GitHub W3C Repositories"
96 | , "secret": "Some magical phrase"
97 | , "path": "/hook"
98 | }
99 | ]
100 | }
101 | // Configuration for the store, probably self-explanatory
102 | , "store": {
103 | "auth": {
104 | "username": "robin"
105 | , "password": "wickEdCo0lPasswr.D"
106 | }
107 | }
108 | // The logging. `console` turns logging to the console on or off (likely off in production); and
109 | // `file` (if present) is the absolute path to, yes, a log file to log logs into.
110 | , "logs": {
111 | "console": true
112 | , "file": "/some/absolute/path/all.log"
113 | }
114 | }
115 | ```
116 | Now, with CouchDB is already up and running, you want to run:
117 |
118 | node lib/store.js
119 |
120 | This installs all the design documents that Couch needs. Whenever you change the design documents,
121 | or **whenever you update `lib/filters/events.js`** just run `lib/store.js` again.
122 |
123 | Running the server is as simple as:
124 |
125 | node bin/server.js
126 |
127 | If you are going to develop however, that isn't the best way of running the server. When developing
128 | the server code, you want to run:
129 |
130 | npm run watch
131 |
132 | This will start a [nodemon][nodemon] instance that will monitor the changes you make to the Pheme
133 | code, and restart it for you.
134 |
135 | One of the issues with developing on one's box is that it is not typically accessible over the Web
136 | for outside services to interact with. If you are trying to get events from repositories on GitHub,
137 | you will need to expose yourself to the Web. You may already have your preferred way of doing that,
138 | but in case you don't you can use [ngrok][ngrok] (which is what I do). In order to expose your
139 | service through ngrok, just run
140 |
141 | npm run expose
142 |
143 | Note that you don't need that for regular development, you only need to be exposed if you want to
144 | receive GitHub events.
145 |
146 | ## Deploying Pheme in Production
147 |
148 | You will want a slightly different `config.json`; the one in hatchery is serviceable (it notably has
149 | the right secret for the W3C hook).
150 |
151 | You don't want to use `npm run` in production; instead use [pm2][pm2]. A configuration is provided
152 | for it in `pm2-production.json` (it's what's used on hatchery).
153 |
154 | ## Installing Midgard
155 |
156 | It's pretty straightforward:
157 |
158 | git clone https://github.com/w3c/midgard
159 | cd midgard
160 | npm install -d
161 |
162 | Note that even though this is client-side code you *must* install the dependencies *even just to
163 | deploy it*.
164 |
165 | You now need to configure the system so that it can find various bits and pieces. For this create a
166 | `config.json` at the root, with the following content:
167 |
168 | ```
169 | {
170 | // This is the URL to the root of the API server, with trailing /.
171 | "api": "http://pheme.bast/"
172 | // If Midgard is not running at the root of its host, this is the path to it; otherwise /.
173 | , "pathPrefix": "/"
174 | }
175 | ```
176 |
177 | You then need to run:
178 |
179 | npm run build
180 |
181 | (Technically, you only need to run `npm run build-js` as the `config.json` only affects that; but
182 | given how the JS build dominates the build time it makes no difference.)
183 |
184 | That's it, you have a working Midgard, ready to be served. ***IMPORTANT NOTE***: whenever you udpate
185 | the code, you need to run the build again. That's because the built version is not under version
186 | control, because it depends on the small configuration.
187 |
188 | When developing the code, you absolutely *do not want to run `npm run build` yourself for every
189 | change*. The reason for that is that a full Browserify build can be quite slow. Instead we have a
190 | [Watchify][Watchify]-based command that does incremental building whenever it detects a change. On
191 | my laptop that's the different between insufferable 5 seconds build time and tolerable 0.2s build
192 | time. Just:
193 |
194 | npm run watch
195 |
196 | This will build both the CSS and JS/JSX whenever needed.
197 |
198 | ## Production deployment
199 |
200 | You will want a slightly different `config.json` as the Pheme server might be elsewhere (that said
201 | if you're doing pure-client changes nothing keeps you from having the client talk to the live
202 | production Pheme instance).
203 |
204 |
205 | ## The CouchDB Design
206 |
207 | Just two design documents are used in CouchDB, they're very simple. They are basic
208 | maps to index the data. You can find them all under `store.js` in `setupDDocs()`. There are:
209 |
210 | * users, that can be queried by username;
211 | * events (with one view per filter), queried by date.
212 |
213 |
214 | ## Pheme Code Layout
215 |
216 | The server makes use of several files.
217 |
218 | ### `bin/server.js`
219 |
220 | This is the executable. All it does is load up the Pheme library and run it.
221 |
222 | ### `lib/pheme.js`
223 |
224 | This is the main library that binds the rest together.
225 |
226 | It sets up the store and server, but the core of its work is to set up the sources correctly based
227 | on the configuration. This involves:
228 |
229 | * Loading the source modules that match the configuration.
230 | * Handling them differently depending on whether they declare being push or poll sources.
231 | * Configuring each source instance.
232 | * Exposing the push sources through the web server, and notifying the poll sources that they need to
233 | poll at their preferred interval.
234 |
235 | ### `lib/store.js`
236 |
237 | This is a very straightforward access point to CouchDB, built atop the [cradle][cradle] library.
238 | When ran directly it creates the DB and sets up the design documents; otherwise it's a library that
239 | can be used to access the content of the DB.
240 |
241 | It has simple setup methods that are just used to configure the database. `setupDDocs` can look a
242 | little confusing because it is generating view filters based on what's specified in
243 | `filters/events.js`, but the resulting code is all pretty simple (filtering views on type and
244 | source, and indexing them by date).
245 |
246 | It has a few simple methods to access the data that should be self-explanatory.
247 |
248 | ### `lib/server.js`
249 |
250 | A basic set of Express endpoints. It manages sessions, CORS, and logging.
251 |
252 | You can get the current user (if logged in) and update her preferences for the filters. The login
253 | endpoint is of some interest: it receives genuine W3C credentials and does a `HEAD` against a
254 | Member and a Team endpoint to determine if the login works and which ACL level to grant the user
255 | (note that at this point there is no content in the DB that isn't public, so this isn't all that
256 | useful). This should probably be replaced by LDAP at some point. You can also logout.
257 |
258 | Other endpoints have to do with the event filters. You can list all those that are available (to
259 | use in the configuration dialog); you can get a bunch of recent events for a given filter (right now
260 | there is no paging and it's limited to the more recent 30, but that would be easy to add — there are
261 | comments to that effect in the store code). Finally you can `POST` a JSON object with filter names
262 | as its keys and date specifications for values being the date of the most recent event seen for
263 | that filter. The server will respond with the same keys and for each a number of events more recent
264 | than the given date. The client uses that by tracking which most recent documents have been seen
265 | for each filter, and uses the response to mark the event mailboxes as having new content (the client
266 | polls for that every minute).
267 |
268 | ### `lib/log.js`
269 |
270 | This is a simple wrapper that exposes an already-built instance of [Winston][Winston], configured to
271 | log to the console, file, or both. It's easy to add other logging targets if need be.
272 |
273 | ### `sources/*`
274 |
275 | There are currently two sources, one pull and one push, but it is easy to add more.
276 |
277 | All sources must expose a `method` field that is either `push` or `poll` so that Pheme knows how to
278 | handle them. They also must expose a `createSource(conf, pheme)` method that they use to return an
279 | object. It gets the instance-specific configuration and an instance of Pheme.
280 |
281 | The object returned for poll sources must have a `poll()` method. It can do whatever it wants (see
282 | the RSS source for an example), and it is expected to use `pheme.store` in order to store the
283 | events it finds.
284 |
285 | The object returned for push sources must have a `handle(req, res, next)` method. This behaves
286 | basically like an Express middleware. The source can decline to process it by calling `next()`, and
287 | `req` and `res` are the usual Express request and response objects. The expectation is that the
288 | source knows how to respond to whatever hook calls it. It also uses `pheme.store` to add new events
289 | to the database. See the GitHub source for an example.
290 |
291 | ### `lib/filters/events.js`
292 |
293 | The structure hints that there could be multiple filter types, other than events, even though now
294 | that's all there is. The idea is indeed that at some point there could be other filters on the
295 | dashboard content, for instance for things that aren't events like currently open WBS polls for a
296 | given person.
297 |
298 | Right now that module just exports a big map of filters on the events. Each has a key name (which
299 | identifies it across the system), a human `name` and `description`. It must have an `origin` which
300 | maps to what a given source produces as the origin of its events (RSS uses the RSS feed name given
301 | in the configuration so that different RSS feeds have different origins, GitHub uses "github" for
302 | all its content because it makes sense as a source).
303 |
304 | Filters with a `github` origin are expected to have an array of repositories that are sources to be
305 | included in the filter.
306 |
307 | At this point there is no way to union multiple origins (say, those two GitHub repos and that RSS
308 | feed together) even though it will almost certainly make sense. That's a limitation that relatively
309 | easy to lift just by making the keys accept arrays of filters, and having the view-generation
310 | code generator in `setupDDocs()` handle that.
311 |
312 |
313 | ## Midgard Code Layout
314 |
315 | ### `index.html`
316 |
317 | A very basic bare bones HTML page that loads the style and script.
318 |
319 | ### `css/midgard.css`
320 |
321 | A pretty basic CSS file. It just loads up [normalize][normalize] and [ungrid][ungrid], and then
322 | styles the various controls in a pretty general manner.
323 |
324 | There is no magic and no framework. The complete built CSS is ~7K.
325 |
326 | ### `components/*.jsx`
327 |
328 | The JSX files under `components/` are simple, reusable components. At some point they should probably be extracted into a shared library that can be reused across W3C applications.
329 |
330 | Most of them are extremely simple and largely there to keep the JSX readable, without having to rely
331 | excessively on `div`s and classes.
332 |
333 | #### `application.jsx`
334 |
335 | A simple layout wrapper, with a title, that just renders its children. Used to render routed
336 | components into.
337 |
338 | #### `col.jsx` and `row.jsx`
339 |
340 | Very simple row and column items that use ungrid. Nothing fancy.
341 |
342 | #### `spinner.jsx`
343 |
344 | This is a simple loading/progress spinner with built-in SVG, no dependencies, and CSS animation so
345 | that when Chrome drops SMIL support this will work. It takes a few options.
346 |
347 | #### `flash-list.jsx`
348 |
349 | This just renders the list of success/error messages that are stored in the message store.
350 |
351 | It has a magical mode, but only the initiated can turn it on.
352 |
353 | ### `js/midgard.jsx`
354 |
355 | This is the entry point for the JS application. Most of what it does is to import things and get
356 | them set up.
357 |
358 | It does not do much apart from rendering either a spinner (while loading), a login form (if not
359 | logged in), or the application itself. There is routing support in place, but it is not currently
360 | wired in. Doing so would be relatively easy.
361 |
362 | ### `stores/*.js` and `actions/*.js`
363 |
364 | One architectural approach that works well with React is known as Flux. At its heart it is a simple
365 | idea to handle events and data in an application, in such a manner that avoids tangled-up messes.
366 |
367 | The application (typically driven by the user) can trigger an **action**, usually with attached
368 | data. An example from the code are error messages that can be emitted pretty much anywhere in the
369 | application (ditto success messages).
370 |
371 | Actions are all sent towards the **dispatcher** (which we reuse from the basic Flux implementation).
372 | The dispatcher makes these available to whoever wants to listen. This is similar to pub/sub, except that an event's full trip is taken into consideration, and it only ever travels in one direction.
373 |
374 | Stores listen to actions, and keep any data that the application might need handy (either locally or
375 | by accessing it when needed). For the error/success messages, the store just keeps them around until
376 | they are dismissed, which means that navigation across components will still render the messages in
377 | the store.
378 |
379 | Finally, components can listen to changes in stores, and react to them so as to update their
380 | rendering.
381 |
382 | This application uses actions and stores relatively extensively but data management could probably
383 | be refactored some to make it a little bit clearer. One promising approach being developed is
384 | Redux; its ideas would seem to match this type of application really well, but I estimated that it
385 | was still too early days to apply that.
386 |
387 | #### `actions/messages.js` and `actions/user.js`
388 |
389 | These are actions. These modules can just be imported by any component that wishes to carry out such
390 | actions, without having to know anything about whether or how the result gets stored, or how it
391 | might influence the rest of the application (it's completely fire-and-forget).
392 |
393 | The `messages.js` action module supports `error()` and `success()` messages, and can `dismiss()` a
394 | given message.
395 |
396 | The `user.js` action module supports `login()` and `logout()` actions corresponding to what the user
397 | does, it can `loadUser()` to get the user's information (after login has completed), and can
398 | manipulate the filters that the user has configured using `addFilter()` and `removeFilter()`.
399 |
400 | #### `stores/login.js` and `stores/message.js`
401 |
402 | The `login` store keeps information about whether the user is logged in, what their information is,
403 | and handles the logging out when requested. The `message` store keeps a list of error and success
404 | messages that haven't been dismissed.
405 |
406 | #### `actions/mailbox.js`, `actions/last-seen.js` and `actions/configuration.js`
407 |
408 | These actions will select a filter mailbox (which enables various parts of the app to stay in sync
409 | with that), trigger the loading of the configuration (currently just the list of filters the server
410 | has available), and `last-seen.js` can both indicate the date of the last message seen in a box and
411 | initiate polling for updates to mailbox filters.
412 |
413 | #### `store/mailbox.js`, `store/last-seen.js`, `store/configuration.js`, and `store/filter.js`
414 |
415 | The configuration store just lists the filters available on the server. If the server's list
416 | changes, the application currently needs to be reloaded. This could be changed (but it's not a very
417 | frequent situation).
418 |
419 | The last-seen store handles polling the server regularly by sending it a map of the most recently
420 | seen message in a filter mailbox (which it tracks) and receiving a count of new events since each
421 | of those dates. The date structure is an array that is basically (where `d` is a date):
422 |
423 | ```js
424 | [
425 | d.getUTCFullYear()
426 | , d.getUTCMonth() + 1
427 | , d.getUTCDate()
428 | , d.getUTCHours()
429 | , d.getUTCMinutes()
430 | , d.getUTCSeconds()
431 | , d.getUTCMilliseconds()
432 | ]
433 | ```
434 |
435 | The reason for this unusual structure (apart from the fact that JSON doesn't do dates) is that it is
436 | the same structure used as key for the event views in CouchDB. The advantage is that it allows for
437 | other queries, for instance `[2015, 3, 15]` will match everything on March 15, 2015 irrespective of
438 | the rest.
439 |
440 | By default last-seen polls every minute. This could be made configurable.
441 |
442 | The mailbox store just stores the current mailbox. It is a very good example of why there is
443 | currently too much boilerplate in stores that could be extracted relatively easily.
444 |
445 | The filter store keeps track of the user's preference in terms of which filters she wants to have
446 | active as mailboxes. The data is stored on the server so as to persist across devices, but it's
447 | handled not through saving the user object directly (which could be problematic given that such
448 | objects tend to have ACL information and the such) but by talking to a special endpoint that just
449 | enables saving the filters. They are saved immediately with every edit, it's fast enough for that.
450 |
451 | ### The `js/*.jsx` components
452 |
453 | These are non-reusable components that are specific to this applications.
454 |
455 | #### `login.jsx`
456 |
457 | A simple component that displays a login form and triggers an action to log in.
458 |
459 | #### `logout-button.jsx`
460 |
461 | A button that can be used (and reused) anywhere (in our case, it's part of the navigation). When
462 | clicked it dispatches a `logout` action.
463 |
464 | #### `toolbar.jsx`
465 |
466 | A simple component that lists the actions available from the toolbar, and handles toggling the
467 | visibility of the setting.
468 |
469 | #### `filter-toggle.jsx`
470 |
471 | A small component that is instantiated with an available filter description and knows how to toggle
472 | it by dispatching add/remove actions.
473 |
474 | #### `filter-list.jsx`
475 |
476 | Shows the list of filter mailboxes that the user has configured, reacting to changes in that
477 | configuration. It also manages picking which one is actually selected, and stores that information
478 | locally so that reloading returns to the same place.
479 |
480 | #### `filter-selector.jsx`
481 |
482 | Essentially a button and/or tab that can be clicked to select a filter mailbox, sitting in the list
483 | of filters. It also knows how to render the unread count.
484 |
485 | #### `event-list.jsx`
486 |
487 | The list of events for the selected mailbox. It simply tracks changes to the currently selected
488 | mailbox and fetches the events that match it. It will use different rendering for different types of
489 | events. RSS rendering is built-in (though it could be farmed out); GitHub rendering is complex
490 | enough to justify its own component.
491 |
492 | #### `show-github.jsx`
493 |
494 | A component that knows how to render a GitHub-related event. This gets relatively convoluted because
495 | there are many different types of those.
496 |
497 | One important aspect of this component is `remoteRenderURL()`. Upon instantiation, for certain types
498 | of events, it will actually contact GitHub's API in order to obtain an HTML rendering of the content
499 | of the event. This is typically true of any event that can include Markdown — we don't want to
500 | render that ourselves.
501 |
502 | The actual rendering is basically a big bunch of if branches that depend on event type and possibly
503 | other information bits to pick the right rendering for a given event.
504 |
505 | It is known at this time that not all events have rendering. If you see a JSON dump, that's where
506 | it's coming from. Adding the rendering for a new event type is easy.
507 |
508 | ## Suggested Improvements
509 |
510 | The Flux usage was grown rather than architected. It could use a bit of fine-tuning now that the
511 | application has taken shape. Also, it's worth looking at Redux.
512 |
513 | The components and much of the style can probably be extracted so that that can be reused in other
514 | W3C applications (see what's similar with Ash-Nazg, noting that the component may have been
515 | tweaked between the two).
516 |
517 | [CouchDB]: http://couchdb.apache.org/
518 | [Express]: http://expressjs.com/
519 | [Pheme]: https://github.com/w3c/pheme
520 | [Midgard]: https://github.com/w3c/midgard
521 | [React]: https://facebook.github.io/react/docs/getting-started.html
522 | [Flux]: http://facebook.github.io/flux/
523 | [Browserify]: http://browserify.org/
524 | [JSX]: https://facebook.github.io/react/docs/displaying-data.html
525 | [cleancss]: https://github.com/jakubpawlowicz/clean-css
526 | [nodemon]: https://github.com/remy/nodemon
527 | [ngrok]: https://ngrok.com/
528 | [pm2]: https://github.com/Unitech/pm2
529 | [Watchify]: https://github.com/substack/watchify
530 | [cradle]: https://github.com/flatiron/cradle
531 | [Winston]: http://github.com/flatiron/winston
532 | [normalize]: http://necolas.github.com/normalize.css/
533 | [ungrid]: http://chrisnager.github.io/ungrid/
534 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Robin Berjon
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/NOTES.txt:
--------------------------------------------------------------------------------
1 |
2 | REACTIFY:
3 | ✓ move all the existing JS into an "OLD" directory, to be ported one by one
4 | ✓ rewrite the css to have an entry point, use ungrid and normalize, reuse the same stuff as AN
5 | ✓ rewrite index.html to just load the real dependencies
6 | ✓ have the root render as a react component
7 | ✓ get the login logic from AN and port it to the model we have here
8 | ✓ render the login
9 | ✓ start porting over the rest piece by piece
10 |
11 | MAILBOX:
12 | - keep track of last seen
13 | - maybe a single POST with keys all being the filter names, and values the last-seen
14 | date stamp. Does a count on the DB, returns the count for each.
15 | - use that to poll the DB every ~5 minutes and update the UI. The currently shown view
16 | gets some sort of button? Use notification API?
17 | - show badges
18 | ✓ show list of mailboxes that depends on configured filters
19 | ✓ store/load filter preferences from user
20 | ✓ drop the widgets ideas
21 | ✓ merge configuration of sources and event filters list, they really should be edited in just
22 | the one place
23 |
24 | DB:
25 | ✓ kill all the old DBs, and just have a store that's for Couch
26 | ✓ have the DB be configured when run, not on load
27 |
28 | PHEME:
29 | ✓ add support for minutes using https://cvs.w3.org/recent-commits?user=swick
30 | ✓ add the W3C RSS
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Midgard
2 |
3 | A dashboard for the dwellers of our world.
4 |
5 | This is a dashboard that knows how to display the event information stored in
6 | [Pheme](https://github.com/w3c/pheme).
7 |
8 | There is development/deployment documentation covering both Midgard and Pheme in the [`DEVELOPMENT.md` document](https://github.com/w3c/midgard/blob/master/DEVELOPMENT.md) here.
9 |
--------------------------------------------------------------------------------
/components/application.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from "react";
3 | import DocumentTitle from "react-document-title";
4 |
5 | export default class Application extends React.Component {
6 | render () {
7 | return
8 |
9 |
{this.props.title}
10 |
{this.props.children}
11 |
12 |
13 |
14 | ;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/components/col.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from "react";
3 |
4 | // this is basically ungrid in a box
5 | export default class Col extends React.Component {
6 | render () {
7 | return