1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-26 15:30:58 +01:00

Add a "feed" filter to the home page; align things; allow browsing older stories

Summary:
Pretty straightforward; see title. Kind of gross but I have a bunch
more iterations in mind here (like filtering). Paging this is a little tricky
since we can't easily use AphrontPagerView, as it relies on OFFSET, and I think
that's sort of sketchy to use here for UX reasons (query performance and view
consistency as feed updates).

Test Plan: Looked at feed, paged through feed.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran, epriestley

Differential Revision: https://secure.phabricator.com/D1616
This commit is contained in:
epriestley 2012-02-15 17:48:14 -08:00
parent fce6a7089c
commit 29acc848c1
10 changed files with 153 additions and 121 deletions

View file

@ -1406,7 +1406,7 @@ celerity_register_resource_map(array(
),
'phabricator-feed-css' =>
array(
'uri' => '/res/7d1d0015/rsrc/css/application/feed/feed.css',
'uri' => '/res/e4bf27b5/rsrc/css/application/feed/feed.css',
'type' => 'css',
'requires' =>
array(

View file

@ -512,7 +512,6 @@ phutil_register_library_map(array(
'PhabricatorFeedStoryTypeConstants' => 'applications/feed/constants/story',
'PhabricatorFeedStoryUnknown' => 'applications/feed/story/unknown',
'PhabricatorFeedStoryView' => 'applications/feed/view/story',
'PhabricatorFeedStreamController' => 'applications/feed/controller/stream',
'PhabricatorFeedView' => 'applications/feed/view/base',
'PhabricatorFile' => 'applications/files/storage/file',
'PhabricatorFileController' => 'applications/files/controller/base',
@ -1242,7 +1241,6 @@ phutil_register_library_map(array(
'PhabricatorFeedStoryTypeConstants' => 'PhabricatorFeedConstants',
'PhabricatorFeedStoryUnknown' => 'PhabricatorFeedStory',
'PhabricatorFeedStoryView' => 'PhabricatorFeedView',
'PhabricatorFeedStreamController' => 'PhabricatorFeedController',
'PhabricatorFeedView' => 'AphrontView',
'PhabricatorFile' => 'PhabricatorFileDAO',
'PhabricatorFileController' => 'PhabricatorController',

View file

@ -32,9 +32,7 @@ class AphrontDefaultApplicationConfiguration
public function getURIMap() {
return $this->getResourceURIMapRules() + array(
'/' => array(
'$' => 'PhabricatorDirectoryMainController',
),
'/(?:(?P<filter>feed)/)?$' => 'PhabricatorDirectoryMainController',
'/directory/' => array(
'(?P<id>\d+)/$'
=> 'PhabricatorDirectoryCategoryViewController',
@ -359,10 +357,7 @@ class AphrontDefaultApplicationConfiguration
=> 'PhabricatorCountdownDeleteController'
),
'/feed/' => array(
'$' => 'PhabricatorFeedStreamController',
'public/$' => 'PhabricatorFeedPublicStreamController',
),
'/feed/public/$' => 'PhabricatorFeedPublicStreamController',
'/V(?P<id>\d+)$' => 'PhabricatorSlowvotePollController',
'/vote/' => array(

View file

@ -44,6 +44,7 @@ abstract class PhabricatorDirectoryController extends PhabricatorController {
$nav->addLabel('Phabricator');
$nav->addFilter('home', 'Tactical Command', '/');
$nav->addFilter('feed', 'Feed');
$nav->addSpacer();
$nav->addLabel('Applications');

View file

@ -21,6 +21,10 @@ class PhabricatorDirectoryMainController
private $filter;
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
}
public function shouldRequireAdmin() {
// These controllers are admin-only by default, but this one is public,
// so allow non-admin users to view it.
@ -30,18 +34,29 @@ class PhabricatorDirectoryMainController
public function processRequest() {
$user = $this->getRequest()->getUser();
$nav = $this->buildNav();
$this->filter = $nav->selectFilter($this->filter, 'home');
$project_query = new PhabricatorProjectQuery();
$project_query->setMembers(array($user->getPHID()));
$projects = $project_query->execute();
switch ($this->filter) {
case 'feed':
return $this->buildFeedResponse($nav, $projects);
default:
return $this->buildMainResponse($nav, $projects);
}
}
private function buildMainResponse($nav, $projects) {
$unbreak_panel = $this->buildUnbreakNowPanel();
$triage_panel = $this->buildNeedsTriagePanel($projects);
$revision_panel = $this->buildRevisionPanel();
$tasks_panel = $this->buildTasksPanel();
$feed_view = $this->buildFeedView($projects);
$feed_view = $this->buildFeedView($projects, $is_full = false);
$nav = $this->buildNav();
$this->filter = $nav->selectFilter($this->filter, 'home');
$content = array(
$unbreak_panel,
@ -56,8 +71,17 @@ class PhabricatorDirectoryMainController
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Directory',
'tab' => 'directory',
'title' => 'Phabricator',
));
}
private function buildFeedResponse($nav, $projects) {
$view = $this->buildFeedView($projects, $is_full = true);
$nav->appendChild($view);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Feed',
));
}
@ -252,8 +276,9 @@ class PhabricatorDirectoryMainController
return $view;
}
private function buildFeedView(array $projects) {
$user = $this->getRequest()->getUser();
private function buildFeedView(array $projects, $is_full) {
$request = $this->getRequest();
$user = $request->getUser();
$user_phid = $user->getPHID();
$feed_query = new PhabricatorFeedQuery();
@ -261,19 +286,77 @@ class PhabricatorDirectoryMainController
array_merge(
array($user_phid),
mpull($projects, 'getPHID')));
// TODO: All this limit stuff should probably be consolidated into the
// feed query?
$old_link = null;
$new_link = null;
if ($is_full) {
$feed_query->setAfter($request->getStr('after'));
$feed_query->setBefore($request->getStr('before'));
$limit = 500;
} else {
$limit = 100;
}
// Grab one more story than we intend to display so we can figure out
// if we need to render an "Older Posts" link or not (with reasonable
// accuracy, at least).
$feed_query->setLimit($limit + 1);
$feed = $feed_query->execute();
$extra_row = (count($feed) == $limit + 1);
if ($is_full) {
$have_new = ($request->getStr('before')) ||
($request->getStr('after') && $extra_row);
} else {
$have_new = false;
}
$have_old = ($request->getStr('after')) ||
($request->getStr('before') && $extra_row) ||
(!$request->getStr('before') &&
!$request->getStr('after') &&
$extra_row);
$feed = array_slice($feed, 0, $limit, $preserve_keys = true);
if ($have_old) {
$old_link = phutil_render_tag(
'a',
array(
'href' => '/feed/?before='.end($feed)->getChronologicalKey(),
'class' => 'phabricator-feed-older-link',
),
"Older Stories \xC2\xBB");
}
if ($have_new) {
$new_link = phutil_render_tag(
'a',
array(
'href' => '/feed/?after='.reset($feed)->getChronologicalKey(),
'class' => 'phabricator-feed-newer-link',
),
"\xC2\xAB Newer Stories");
}
$builder = new PhabricatorFeedBuilder($feed);
$builder->setUser($user);
$feed_view = $builder->buildView();
return
'<div style="padding: 1em 1em;">'.
'<h1 style="font-size: 18px; '.
'border-bottom: 1px solid #aaaaaa; '.
'margin: 0 1em;">Feed</h1>'.
'<div style="padding: 1em 3em;">'.
'<div style="margin: 0 1em;">'.
'<h1 style="font-size: 18px; '.
'border-bottom: 1px solid #aaaaaa; '.
'padding: 0;">Feed</h1>'.
'</div>'.
$feed_view->render().
'<div class="phabricator-feed-frame">'.
$new_link.
$old_link.
'</div>'.
'</div>';
}

View file

@ -1,74 +0,0 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorFeedStreamController extends PhabricatorFeedController {
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
if ($request->isFormPost()) {
$story = id(new PhabricatorFeedStoryPublisher())
->setRelatedPHIDs(array($viewer->getPHID()))
->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_STATUS)
->setStoryTime(time())
->setStoryAuthorPHID($viewer->getPHID())
->setStoryData(
array(
'content' => $request->getStr('status')
))
->publish();
return id(new AphrontRedirectResponse())->setURI(
$request->getRequestURI());
}
$query = new PhabricatorFeedQuery();
$stories = $query->execute();
$builder = new PhabricatorFeedBuilder($stories);
$builder->setUser($request->getUser());
$view = $builder->buildView();
$post_form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Pithy Wit')
->setName('status'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Publish'));
$post = new AphrontPanelView();
$post->setWidth(AphrontPanelView::WIDTH_FORM);
$post->setHeader('High Horse Soapbox');
$post->appendChild($post_form);
$page = array();
$page[] = $post;
$page[] = $view;
return $this->buildStandardPageResponse(
$page,
array(
'title' => 'Feed',
));
}
}

View file

@ -1,23 +0,0 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/feed/builder/feed');
phutil_require_module('phabricator', 'applications/feed/constants/story');
phutil_require_module('phabricator', 'applications/feed/controller/base');
phutil_require_module('phabricator', 'applications/feed/publisher');
phutil_require_module('phabricator', 'applications/feed/query');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/textarea');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFeedStreamController.php');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ final class PhabricatorFeedQuery {
private $filterPHIDs;
private $limit = 100;
private $after;
private $before;
public function setFilterPHIDs(array $phids) {
$this->filterPHIDs = $phids;
@ -37,6 +38,11 @@ final class PhabricatorFeedQuery {
return $this;
}
public function setBefore($before) {
$this->before = $before;
return $this;
}
public function execute() {
$ref_table = new PhabricatorFeedStoryReference();
@ -52,6 +58,29 @@ final class PhabricatorFeedQuery {
$this->filterPHIDs);
}
// For "before" queries, we can just add a constraint to the WHERE clause.
// For "after" queries, we must also reverse the result ordering, since
// otherwise we'll always grab the first page of results if there's a limit.
// After MySQL applies the limit, we reverse the page in PHP (below) to
// ensure consistent ordering.
$order = 'DESC';
if ($this->after) {
$where[] = qsprintf(
$conn,
'ref.chronologicalKey > %s',
$this->after);
$order = 'ASC';
}
if ($this->before) {
$where[] = qsprintf(
$conn,
'ref.chronologicalKey < %s',
$this->before);
}
if ($where) {
$where = 'WHERE ('.implode(') AND (', $where).')';
} else {
@ -64,12 +93,20 @@ final class PhabricatorFeedQuery {
JOIN %T story ON ref.chronologicalKey = story.chronologicalKey
%Q
GROUP BY story.chronologicalKey
ORDER BY story.chronologicalKey DESC
ORDER BY story.chronologicalKey %Q
LIMIT %d',
$ref_table->getTableName(),
$story_table->getTableName(),
$where,
$order,
$this->limit);
if ($order != 'DESC') {
// If we did order ASC to pull 'after' data, reverse the result set so
// that stories are returned in a consistent (descending) order.
$data = array_reverse($data);
}
$data = $story_table->loadAllFromArray($data);
$stories = array();

View file

@ -83,6 +83,10 @@ abstract class PhabricatorFeedStory {
return $this->getStoryData()->getEpoch();
}
final public function getChronologicalKey() {
return $this->getStoryData()->getChronologicalKey();
}
final protected function renderHandleList(array $phids) {
$list = array();
foreach ($phids as $phid) {

View file

@ -43,3 +43,14 @@
.phabricator-feed-story-date-separator {
margin-top: 2em;
}
.phabricator-feed-newer-link {
float: left;
font-weight: bold;
}
.phabricator-feed-older-link {
float: right;
font-weight: bold;
}