{"action":"created","issue":{"url":"https://api.github.com/repos/Automattic/wp-calypso/issues/3065","repository_url":"https://api.github.com/repos/Automattic/wp-calypso","labels_url":"https://api.github.com/repos/Automattic/wp-calypso/issues/3065/labels{/name}","comments_url":"https://api.github.com/repos/Automattic/wp-calypso/issues/3065/comments","events_url":"https://api.github.com/repos/Automattic/wp-calypso/issues/3065/events","html_url":"https://github.com/Automattic/wp-calypso/pull/3065","id":131212159,"number":3065,"title":"Themes: Isomorphic routing for `/design` [WIP/Experimental]","user":{"login":"mcsf","id":150562,"avatar_url":"https://avatars.githubusercontent.com/u/150562?v=3","gravatar_id":"","url":"https://api.github.com/users/mcsf","html_url":"https://github.com/mcsf","followers_url":"https://api.github.com/users/mcsf/followers","following_url":"https://api.github.com/users/mcsf/following{/other_user}","gists_url":"https://api.github.com/users/mcsf/gists{/gist_id}","starred_url":"https://api.github.com/users/mcsf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mcsf/subscriptions","organizations_url":"https://api.github.com/users/mcsf/orgs","repos_url":"https://api.github.com/users/mcsf/repos","events_url":"https://api.github.com/users/mcsf/events{/privacy}","received_events_url":"https://api.github.com/users/mcsf/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Automattic/wp-calypso/labels/Server%20Side%20Rendering","name":"Server Side Rendering","color":"006b75"},{"url":"https://api.github.com/repos/Automattic/wp-calypso/labels/Themes","name":"Themes","color":"006b75"},{"url":"https://api.github.com/repos/Automattic/wp-calypso/labels/[Status]%20In%20Progress","name":"[Status] In Progress","color":"207de5"},{"url":"https://api.github.com/repos/Automattic/wp-calypso/labels/[Type]%20Question","name":"[Type] Question","color":"cc317c"}],"state":"open","locked":false,"assignee":{"login":"mcsf","id":150562,"avatar_url":"https://avatars.githubusercontent.com/u/150562?v=3","gravatar_id":"","url":"https://api.github.com/users/mcsf","html_url":"https://github.com/mcsf","followers_url":"https://api.github.com/users/mcsf/followers","following_url":"https://api.github.com/users/mcsf/following{/other_user}","gists_url":"https://api.github.com/users/mcsf/gists{/gist_id}","starred_url":"https://api.github.com/users/mcsf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mcsf/subscriptions","organizations_url":"https://api.github.com/users/mcsf/orgs","repos_url":"https://api.github.com/users/mcsf/repos","events_url":"https://api.github.com/users/mcsf/events{/privacy}","received_events_url":"https://api.github.com/users/mcsf/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/Automattic/wp-calypso/milestones/53","html_url":"https://github.com/Automattic/wp-calypso/milestones/Themes:%20Showcase%20M4-ThemeSheets","labels_url":"https://api.github.com/repos/Automattic/wp-calypso/milestones/53/labels","id":1530833,"number":53,"title":"Themes: Showcase M4-ThemeSheets","description":"Implementing the single theme page with screenshots, description and support (if any).","creator":{"login":"folletto","id":4389,"avatar_url":"https://avatars.githubusercontent.com/u/4389?v=3","gravatar_id":"","url":"https://api.github.com/users/folletto","html_url":"https://github.com/folletto","followers_url":"https://api.github.com/users/folletto/followers","following_url":"https://api.github.com/users/folletto/following{/other_user}","gists_url":"https://api.github.com/users/folletto/gists{/gist_id}","starred_url":"https://api.github.com/users/folletto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/folletto/subscriptions","organizations_url":"https://api.github.com/users/folletto/orgs","repos_url":"https://api.github.com/users/folletto/repos","events_url":"https://api.github.com/users/folletto/events{/privacy}","received_events_url":"https://api.github.com/users/folletto/received_events","type":"User","site_admin":false},"open_issues":17,"closed_issues":2,"state":"open","created_at":"2016-01-21T11:26:19Z","updated_at":"2016-02-09T11:27:02Z","due_on":null,"closed_at":null},"comments":4,"created_at":"2016-02-04T01:41:28Z","updated_at":"2016-02-09T18:12:19Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/Automattic/wp-calypso/pulls/3065","html_url":"https://github.com/Automattic/wp-calypso/pull/3065","diff_url":"https://github.com/Automattic/wp-calypso/pull/3065.diff","patch_url":"https://github.com/Automattic/wp-calypso/pull/3065.patch"},"body":"The idea is to:\r\n- Have the same base code for client and server.\r\n- Once we have full SSR: have just one React factory, in which\r\n  client-side effects like analytics can be encapsulated safely.\r\n- Once we have full SSR: remove server handlers that default to\r\n  renderLoggedInRoute, for more code reuse.\r\n- Take these abstractions further and have less and less code in\r\n  server/pages/index.js. A mechanism similar to that using sections.js\r\n  on the client is one to consider.\r\n- I'm not totally set on the custom 'memo' approach (vs. simple\r\n  memoize), but it's something I'll be exploring some more.\r\n- Probably other things I'm forgetting to mention, and I'm sure you lot\r\n  will ask me about. :)\r\n\r\n### TODO\r\n\r\n- [ ] Converge to more middleware-based solution, vs. having a separate `selectFactory` concept\r\n- [ ] Fix caching\r\n- [ ] Remove webpack hacks\r\n- [ ] Tests\r\n- [ ] Work with #3053 \r\n\r\nThe initial commit includes a lot of garbage commented code just so I feel more comfortable deleting it afterwards."},"comment":{"url":"https://api.github.com/repos/Automattic/wp-calypso/issues/comments/181987522","html_url":"https://github.com/Automattic/wp-calypso/pull/3065#issuecomment-181987522","issue_url":"https://api.github.com/repos/Automattic/wp-calypso/issues/3065","id":181987522,"user":{"login":"mcsf","id":150562,"avatar_url":"https://avatars.githubusercontent.com/u/150562?v=3","gravatar_id":"","url":"https://api.github.com/users/mcsf","html_url":"https://github.com/mcsf","followers_url":"https://api.github.com/users/mcsf/followers","following_url":"https://api.github.com/users/mcsf/following{/other_user}","gists_url":"https://api.github.com/users/mcsf/gists{/gist_id}","starred_url":"https://api.github.com/users/mcsf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mcsf/subscriptions","organizations_url":"https://api.github.com/users/mcsf/orgs","repos_url":"https://api.github.com/users/mcsf/repos","events_url":"https://api.github.com/users/mcsf/events{/privacy}","received_events_url":"https://api.github.com/users/mcsf/received_events","type":"User","site_admin":false},"created_at":"2016-02-09T18:12:19Z","updated_at":"2016-02-09T18:12:19Z","body":"How can we recycle existing Calypso concepts? We have \"middleware\" (route handlers w/ continuation via `next()`); can't we just use that? Right now, on the client, we have:\r\n- 2 optional (logged-in-only) middleware/controllers (`navigation`, `siteSelection`)\r\n- 1 catch-all middleware that calls `selectFactory`, `selectProps`, and renders\r\n\r\nA separate catch-all handler is used on the server, that reuses `select*` and adds caching.\r\n\r\n**What if** we could have everything defined in terms of middleware that may have effects and may populate (read: mutate) the `context` object of that request/route change? Along the middleware chain, `context` could be filled with the necessary data for rendering. For instance:\r\n\r\n```js\r\npage( 'cool-path', navigation, siteSelection, prepareLayout, render )\r\n```\r\n\r\n_Example middleware and its effects_:\r\n- [client-only] **`controller.navigation` & `controller.siteSelection`**: (set up nav and site sel)\r\n- [iso] **`prepareLayout`**: `context.layoutFactory = …; context.props = …`\r\n- [server-only] **`maybeCache`**: `if ( serverSide && cacheHit ) context.layout = …`\r\n- [iso or not] **`render`**: `if ( ! context.layout ) context.layout = …` (can be markup or nodes)\r\n\r\n```js\r\nconst makeServerRouteHandler = ( … ) => ( req, res ) => {\r\n  const ctx = getEnhancedContext( req )\r\n  const mws = getMiddleware( ctx )\r\n  applyMiddleware( mws )( ctx )\r\n  res.render( 'index.jade', context ) // or: abstracted into middleware chain if we want\r\n}\r\n\r\n// this would be the equivalent to the current `getRouting`, filtering\r\n// for the current conditions (e.g. server-side? logged in?)\r\nconst getMiddleware = ( ctx ) => {\r\n  // magic\r\n  return filterMatching( _middleware, ctx )\r\n}\r\n\r\nconst makeClientRouteHandler = ( … ) => ( ctx ) => {\r\n  applyMiddleware( getMiddleware( ctx ) )( ctx )\r\n  render( ctx.layout, domNode ) // or abstracted, or other rendering method\r\n}\r\n\r\n// example middleware\r\nconst prepareLayout = ( ctx, next ) => {\r\n  let matches\r\n  let layoutFactory = ThemesMain\r\n  let props = {}\r\n  const { path } = ctx\r\n  // use `path-parser` or something\r\n  const p = new Path( path )\r\n\r\n  if ( matches = p.match( '/design/type/:tier' ) ) {\r\n    props = { ...props, matches }\r\n  }\r\n\r\n  // etc.\r\n\r\n  // then, unless something went wrong,\r\n  Object.assign( ctx, { layoutFactory, props } )\r\n  next()\r\n}\r\n```\r\n\r\nI felt like trying this on paper first (headache-induced screen avoidance). I'd like to hear thoughts on this."}}