From b43f34c1208ed39b603580d458dd5e6f7f349e5e Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Thu, 18 Jun 2015 17:41:37 +0930 Subject: [PATCH] Roughly implement routes and data preloading Only preloading data for basic requests w/o query params, at least for the moment - if we have to preload for something like /?q=test&sort=newest, we end up having to duplicate a whole lot of logic between JS/PHP. --- .../forum/src/components/discussion-list.js | 14 +++++--- .../forum/src/components/discussion-page.js | 33 +++++++++++++++---- .../js/forum/src/components/post-scrubber.js | 3 +- .../js/forum/src/components/search-box.js | 12 +++++-- .../core/js/forum/src/initializers/boot.js | 9 +++-- .../Api/Actions/Discussions/IndexAction.php | 2 +- .../Api/Actions/Discussions/ShowAction.php | 4 +-- .../src/Forum/Actions/DiscussionAction.php | 18 ++++++++++ .../core/src/Forum/Actions/IndexAction.php | 27 ++++++++++++++- .../core/src/Forum/ForumServiceProvider.php | 29 +++++++++++++--- framework/core/views/index.blade.php | 1 + 11 files changed, 126 insertions(+), 26 deletions(-) create mode 100644 framework/core/src/Forum/Actions/DiscussionAction.php diff --git a/framework/core/js/forum/src/components/discussion-list.js b/framework/core/js/forum/src/components/discussion-list.js index 3b091e20e..d6ba446f3 100644 --- a/framework/core/js/forum/src/components/discussion-list.js +++ b/framework/core/js/forum/src/components/discussion-list.js @@ -78,10 +78,16 @@ export default class DiscussionList extends Component { } loadResults(offset) { - var params = this.params(); - params.page = {offset}; - params.include = params.include.join(','); - return app.store.find('discussions', params); + if (app.preload.response) { + var discussions = app.store.pushPayload(app.preload.response); + app.preload.response = null; + return m.deferred().resolve(discussions).promise; + } else { + var params = this.params(); + params.page = {offset}; + params.include = params.include.join(','); + return app.store.find('discussions', params); + } } loadMore() { diff --git a/framework/core/js/forum/src/components/discussion-page.js b/framework/core/js/forum/src/components/discussion-page.js index cf29a599c..ed228dfe9 100644 --- a/framework/core/js/forum/src/components/discussion-page.js +++ b/framework/core/js/forum/src/components/discussion-page.js @@ -44,7 +44,21 @@ export default class DiscussionPage extends mixin(Component, evented) { var params = this.params(); params.include = params.include.join(','); - app.store.find('discussions', m.route.param('id'), params).then(this.setupDiscussion.bind(this)); + + var discussion; + if (app.preload.response) { + // We must wrap this in a setTimeout because if we are mounting this + // component for the first time on page load, then any calls to m.redraw + // will be ineffective and thus any configs (scroll code) will be run + // before stuff is drawn to the page. + setTimeout(() => { + var discussion = app.store.pushPayload(app.preload.response); + app.preload.response = null; + this.setupDiscussion(discussion); + }); + } else { + app.store.find('discussions', m.route.param('id'), params).then(this.setupDiscussion.bind(this)); + } // Trigger a redraw only if we're not already in a computation (e.g. route change) m.startComputation(); @@ -77,12 +91,19 @@ export default class DiscussionPage extends mixin(Component, evented) { app.setTitle(discussion.title()); var includedPosts = []; - discussion.payload.included && discussion.payload.included.forEach(record => { - if (record.type === 'posts' && (record.contentType !== 'comment' || record.contentHtml)) { - includedPosts.push(app.store.getById('posts', record.id)); + if (discussion.payload && discussion.payload.included) { + discussion.payload.included.forEach(record => { + if (record.type === 'posts' && (record.contentType !== 'comment' || record.contentHtml)) { + includedPosts.push(app.store.getById('posts', record.id)); + } + }); + includedPosts.sort((a, b) => a.id() - b.id()); + } else { + var posts = discussion.posts(); + if (posts) { + includedPosts = posts.filter(post => post); } - }); - includedPosts.sort((a, b) => a.id() - b.id()); + } this.stream = new PostStream({ discussion, includedPosts }); this.stream.on('positionChanged', this.positionChanged.bind(this)); diff --git a/framework/core/js/forum/src/components/post-scrubber.js b/framework/core/js/forum/src/components/post-scrubber.js index 362988523..506eaa6e6 100644 --- a/framework/core/js/forum/src/components/post-scrubber.js +++ b/framework/core/js/forum/src/components/post-scrubber.js @@ -199,9 +199,8 @@ export default class PostScrubber extends Component { if (isInitialized) { return; } - this.renderScrollbar(); - context.onunload = this.ondestroy.bind(this); + this.scrollListener.start(); // Whenever the window is resized, adjust the height of the scrollbar diff --git a/framework/core/js/forum/src/components/search-box.js b/framework/core/js/forum/src/components/search-box.js index 02d2b94aa..e53b9f3fb 100644 --- a/framework/core/js/forum/src/components/search-box.js +++ b/framework/core/js/forum/src/components/search-box.js @@ -23,7 +23,7 @@ export default class SearchBox extends Component { constructor(props) { super(props); - this.value = m.prop(this.getCurrentSearch() || ''); + this.value = m.prop(); this.hasFocus = m.prop(false); this.sources = this.sourceItems().toArray(); @@ -40,10 +40,16 @@ export default class SearchBox extends Component { } getCurrentSearch() { - return typeof app.current.searching === 'function' && app.current.searching(); + return app.current && typeof app.current.searching === 'function' && app.current.searching(); } view() { + // Initialize value in the view rather than the constructor so that we have + // access to app.current. + if (typeof this.value() === 'undefined') { + this.value(this.getCurrentSearch() || ''); + } + var currentSearch = this.getCurrentSearch(); return m('div.search-box.dropdown', { @@ -104,7 +110,7 @@ export default class SearchBox extends Component { case 13: // Return this.$('input').blur(); - this.getItem(this.index()).find('a')[0].dispatchEvent(new Event('click')); + m.route(this.getItem(this.index()).find('a').attr('href')); break; case 27: // Escape diff --git a/framework/core/js/forum/src/initializers/boot.js b/framework/core/js/forum/src/initializers/boot.js index 80bf9edb0..3b91061db 100644 --- a/framework/core/js/forum/src/initializers/boot.js +++ b/framework/core/js/forum/src/initializers/boot.js @@ -18,8 +18,11 @@ export default function(app) { app.history = new History(); app.pane = new Pane(id('page')); + app.search = new SearchBox(); app.cache = {}; + m.startComputation(); + m.mount(id('back-control'), BackButton.component({ className: 'back-control', drawer: true })); m.mount(id('back-button'), BackButton.component()); @@ -39,10 +42,12 @@ export default function(app) { app.modal = m.mount(id('modal'), Modal.component()); app.alerts = m.mount(id('alerts'), Alerts.component()); - m.route.mode = 'hash'; + m.route.mode = 'pathname'; m.route(id('content'), '/', mapRoutes(app.routes)); - app.search = new SearchBox(); + m.endComputation(); new ScrollListener(top => $('body').toggleClass('scrolled', top > 0)).start(); + + app.booted = true; } diff --git a/framework/core/src/Api/Actions/Discussions/IndexAction.php b/framework/core/src/Api/Actions/Discussions/IndexAction.php index 68dc70241..94042a0d5 100644 --- a/framework/core/src/Api/Actions/Discussions/IndexAction.php +++ b/framework/core/src/Api/Actions/Discussions/IndexAction.php @@ -100,7 +100,7 @@ class IndexAction extends SerializeCollectionAction static::addPaginationLinks( $document, $request, - $this->url->toRoute('flarum.api.discussions.index'), + $request->http ? $this->url->toRoute('flarum.api.discussions.index') : '', $results->areMoreResults() ); diff --git a/framework/core/src/Api/Actions/Discussions/ShowAction.php b/framework/core/src/Api/Actions/Discussions/ShowAction.php index d1e4704a5..95b05a6c6 100644 --- a/framework/core/src/Api/Actions/Discussions/ShowAction.php +++ b/framework/core/src/Api/Actions/Discussions/ShowAction.php @@ -32,8 +32,8 @@ class ShowAction extends SerializeResourceAction public static $include = [ 'startUser' => false, 'lastUser' => false, - 'startPost' => true, - 'lastPost' => true, + 'startPost' => false, + 'lastPost' => false, 'posts' => true, 'posts.user' => true, 'posts.user.groups' => true, diff --git a/framework/core/src/Forum/Actions/DiscussionAction.php b/framework/core/src/Forum/Actions/DiscussionAction.php new file mode 100644 index 000000000..9de02cc2d --- /dev/null +++ b/framework/core/src/Forum/Actions/DiscussionAction.php @@ -0,0 +1,18 @@ +apiClient->send('Flarum\Api\Actions\Discussions\ShowAction', [ + 'id' => $params['id'], + 'near' => $params['near'] + ]); + + // TODO: return an object instead of an array? + return [ + 'title' => $response->data->title, + 'response' => $response + ]; + } +} diff --git a/framework/core/src/Forum/Actions/IndexAction.php b/framework/core/src/Forum/Actions/IndexAction.php index 53ec01873..ffff02fe3 100644 --- a/framework/core/src/Forum/Actions/IndexAction.php +++ b/framework/core/src/Forum/Actions/IndexAction.php @@ -6,6 +6,7 @@ use Flarum\Assets\JsCompiler; use Flarum\Assets\LessCompiler; use Flarum\Core; use Flarum\Forum\Events\RenderView; +use Flarum\Forum\Loaders\LoaderInterface; use Flarum\Locale\JsCompiler as LocaleJsCompiler; use Flarum\Support\Actor; use Flarum\Support\HtmlAction; @@ -66,11 +67,18 @@ class IndexAction extends HtmlAction } } + $details = $this->getDetails($request, $params); + + $data = array_merge($data, array_get($details, 'data', [])); + $response = array_get($details, 'response'); + $title = array_get($details, 'title'); + $view = view('flarum.forum::index') - ->with('title', Core::config('forum_title')) + ->with('title', ($title ? $title.' - ' : '').Core::config('forum_title')) ->with('config', $config) ->with('layout', 'flarum.forum::forum') ->with('data', $data) + ->with('response', $response) ->with('session', $session) ->with('alert', $alert); @@ -112,6 +120,23 @@ class IndexAction extends HtmlAction ->with('scripts', [$assets->getJsFile(), $localeCompiler->getFile()]); } + protected function getDetails($request, $params) + { + $queryParams = $request->getQueryParams(); + + // Only preload data if we're viewing the default index with no filters, + // otherwise we have to do all kinds of crazy stuff + if (!count($queryParams) && $request->getUri()->getPath() === '/') { + $response = $this->apiClient->send('Flarum\Api\Actions\Discussions\IndexAction'); + + return [ + 'response' => $response + ]; + } + + return []; + } + protected static function filterTranslations($translations) { $filtered = []; diff --git a/framework/core/src/Forum/ForumServiceProvider.php b/framework/core/src/Forum/ForumServiceProvider.php index f79fa639d..2befecdc6 100644 --- a/framework/core/src/Forum/ForumServiceProvider.php +++ b/framework/core/src/Forum/ForumServiceProvider.php @@ -2,7 +2,6 @@ use Flarum\Http\RouteCollection; use Flarum\Http\UrlGenerator; -use Flarum\Support\AssetManager; use Flarum\Support\ServiceProvider; use Psr\Http\Message\ServerRequestInterface; @@ -49,16 +48,36 @@ class ForumServiceProvider extends ServiceProvider { $this->app->instance('flarum.forum.routes', $routes = new RouteCollection); - /** - * Route::group(['middleware' => 'Flarum\Forum\Middleware\LoginWithCookie'], function () use ($action) { - * For the two below - */ $routes->get( '/', 'flarum.forum.index', $this->action('Flarum\Forum\Actions\IndexAction') ); + $routes->get( + '/d/{id:\d+}/{slug}', + 'flarum.forum.discussion', + $this->action('Flarum\Forum\Actions\DiscussionAction') + ); + + $routes->get( + '/d/{id:\d+}/{slug}/{near}', + 'flarum.forum.discussion.near', + $this->action('Flarum\Forum\Actions\DiscussionAction') + ); + + $routes->get( + '/u/{username}', + 'flarum.forum.user', + $this->action('Flarum\Forum\Actions\IndexAction') + ); + + $routes->get( + '/settings', + 'flarum.forum.settings', + $this->action('Flarum\Forum\Actions\IndexAction') + ); + $routes->get( '/logout', 'flarum.forum.logout', diff --git a/framework/core/views/index.blade.php b/framework/core/views/index.blade.php index cc8773c15..d91108cdc 100644 --- a/framework/core/views/index.blade.php +++ b/framework/core/views/index.blade.php @@ -25,6 +25,7 @@ app.config = {!! json_encode($config) !!}; app.preload = { data: {!! json_encode($data) !!}, + response: {!! json_encode($response) !!}, session: {!! json_encode($session) !!} }; app.boot();