1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 02:02:41 +01:00

Modernize Countdown somewhat

Summary: Far from perfect, but better?

Test Plan:
{F51153}
{F51154}
{F51155}

Reviewers: btrahan, chad

Reviewed By: chad

CC: aran

Differential Revision: https://secure.phabricator.com/D6533
This commit is contained in:
epriestley 2013-07-23 06:16:19 -07:00
parent 5f78a6953f
commit 974997f6bc
15 changed files with 424 additions and 230 deletions

View file

@ -1407,7 +1407,7 @@ celerity_register_resource_map(array(
), ),
'javelin-behavior-countdown-timer' => 'javelin-behavior-countdown-timer' =>
array( array(
'uri' => '/res/3c52aac2/rsrc/js/application/countdown/timer.js', 'uri' => '/res/13d40efa/rsrc/js/application/countdown/timer.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -3100,7 +3100,7 @@ celerity_register_resource_map(array(
), ),
'phabricator-countdown-css' => 'phabricator-countdown-css' =>
array( array(
'uri' => '/res/0f646281/rsrc/css/application/countdown/timer.css', 'uri' => '/res/c4a30296/rsrc/css/application/countdown/timer.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(

View file

@ -1000,6 +1000,8 @@ phutil_register_library_map(array(
'PhabricatorCountdownPHIDTypeCountdown' => 'applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php', 'PhabricatorCountdownPHIDTypeCountdown' => 'applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php',
'PhabricatorCountdownQuery' => 'applications/countdown/query/PhabricatorCountdownQuery.php', 'PhabricatorCountdownQuery' => 'applications/countdown/query/PhabricatorCountdownQuery.php',
'PhabricatorCountdownRemarkupRule' => 'applications/countdown/remarkup/PhabricatorCountdownRemarkupRule.php', 'PhabricatorCountdownRemarkupRule' => 'applications/countdown/remarkup/PhabricatorCountdownRemarkupRule.php',
'PhabricatorCountdownSearchEngine' => 'applications/countdown/query/PhabricatorCountdownSearchEngine.php',
'PhabricatorCountdownView' => 'applications/countdown/view/PhabricatorCountdownView.php',
'PhabricatorCountdownViewController' => 'applications/countdown/controller/PhabricatorCountdownViewController.php', 'PhabricatorCountdownViewController' => 'applications/countdown/controller/PhabricatorCountdownViewController.php',
'PhabricatorCountedToggleButtonsExample' => 'applications/uiexample/examples/PhabricatorCountedToggleButtonsExample.php', 'PhabricatorCountedToggleButtonsExample' => 'applications/uiexample/examples/PhabricatorCountedToggleButtonsExample.php',
'PhabricatorCrumbView' => 'view/layout/PhabricatorCrumbView.php', 'PhabricatorCrumbView' => 'view/layout/PhabricatorCrumbView.php',
@ -2995,10 +2997,16 @@ phutil_register_library_map(array(
'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO', 'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO',
'PhabricatorCountdownDeleteController' => 'PhabricatorCountdownController', 'PhabricatorCountdownDeleteController' => 'PhabricatorCountdownController',
'PhabricatorCountdownEditController' => 'PhabricatorCountdownController', 'PhabricatorCountdownEditController' => 'PhabricatorCountdownController',
'PhabricatorCountdownListController' => 'PhabricatorCountdownController', 'PhabricatorCountdownListController' =>
array(
0 => 'PhabricatorCountdownController',
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
),
'PhabricatorCountdownPHIDTypeCountdown' => 'PhabricatorPHIDType', 'PhabricatorCountdownPHIDTypeCountdown' => 'PhabricatorPHIDType',
'PhabricatorCountdownQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorCountdownQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorCountdownRemarkupRule' => 'PhabricatorRemarkupRuleObject', 'PhabricatorCountdownRemarkupRule' => 'PhabricatorRemarkupRuleObject',
'PhabricatorCountdownSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorCountdownView' => 'AphrontTagView',
'PhabricatorCountdownViewController' => 'PhabricatorCountdownController', 'PhabricatorCountdownViewController' => 'PhabricatorCountdownController',
'PhabricatorCountedToggleButtonsExample' => 'PhabricatorUIExample', 'PhabricatorCountedToggleButtonsExample' => 'PhabricatorUIExample',
'PhabricatorCrumbView' => 'AphrontView', 'PhabricatorCrumbView' => 'AphrontView',

View file

@ -38,7 +38,7 @@ final class PhabricatorApplicationCountdown extends PhabricatorApplication {
public function getRoutes() { public function getRoutes() {
return array( return array(
'/countdown/' => array( '/countdown/' => array(
'' '(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhabricatorCountdownListController', => 'PhabricatorCountdownListController',
'(?P<id>[1-9]\d*)/' '(?P<id>[1-9]\d*)/'
=> 'PhabricatorCountdownViewController', => 'PhabricatorCountdownViewController',

View file

@ -5,22 +5,27 @@
*/ */
abstract class PhabricatorCountdownController extends PhabricatorController { abstract class PhabricatorCountdownController extends PhabricatorController {
public function buildSideNavView() { public function buildSideNavView($for_app = false) {
$user = $this->getRequest()->getUser(); $user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView(); $nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI())); $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$nav->addFilter('', pht('All Countdowns'), if ($for_app) {
$this->getApplicationURI('')); $nav->addFilter('create', pht('Create Countdown'));
$nav->addFilter('', pht('Create Countdown'), }
$this->getApplicationURI('edit/'));
id(new PhabricatorCountdownSearchEngine())
->setViewer($user)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav; return $nav;
} }
public function buildApplicationMenu() { public function buildApplicationMenu() {
return $this->buildSideNavView()->getMenu(); return $this->buildSideNavView($for_app = true)->getMenu();
} }
public function buildApplicationCrumbs() { public function buildApplicationCrumbs() {

View file

@ -20,6 +20,11 @@ final class PhabricatorCountdownDeleteController
$countdown = id(new PhabricatorCountdownQuery()) $countdown = id(new PhabricatorCountdownQuery())
->setViewer($user) ->setViewer($user)
->withIDs(array($this->id)) ->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne(); ->executeOne();
if (!$countdown) { if (!$countdown) {

View file

@ -15,25 +15,22 @@ final class PhabricatorCountdownEditController
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
$action_label = pht('Create Countdown');
if ($this->id) { if ($this->id) {
$countdown = id(new PhabricatorCountdownQuery()) $countdown = id(new PhabricatorCountdownQuery())
->setViewer($user) ->setViewer($user)
->withIDs(array($this->id)) ->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne(); ->executeOne();
// If no countdown is found // If no countdown is found
if (!$countdown) { if (!$countdown) {
return new Aphront404Response(); return new Aphront404Response();
} }
if (($countdown->getAuthorPHID() != $user->getPHID())
&& $user->getIsAdmin() == false) {
return new Aphront403Response();
}
$action_label = pht('Update Countdown');
} else { } else {
$countdown = new PhabricatorCountdown(); $countdown = new PhabricatorCountdown();
$countdown->setEpoch(time()); $countdown->setEpoch(time());
@ -87,9 +84,31 @@ final class PhabricatorCountdownEditController
$display_epoch = $request->getStr('epoch'); $display_epoch = $request->getStr('epoch');
} }
$crumbs = $this->buildApplicationCrumbs();
$cancel_uri = '/countdown/';
if ($countdown->getID()) {
$cancel_uri = '/countdown/'.$countdown->getID().'/';
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName('C'.$countdown->getID())
->setHref($cancel_uri));
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Edit')));
$submit_label = pht('Save Changes');
} else {
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Create Countdown')));
$submit_label = pht('Create Countdown');
}
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($user) ->setUser($user)
->setAction($request->getRequestURI()->getPath()) ->setAction($request->getRequestURI()->getPath())
->setFlexible(true)
->appendChild( ->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setLabel(pht('Title')) ->setLabel(pht('Title'))
@ -97,7 +116,7 @@ final class PhabricatorCountdownEditController
->setName('title')) ->setName('title'))
->appendChild( ->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setLabel(pht('End date')) ->setLabel(pht('End Date'))
->setValue($display_epoch) ->setValue($display_epoch)
->setName('epoch') ->setName('epoch')
->setCaption(pht('Examples: '. ->setCaption(pht('Examples: '.
@ -105,31 +124,20 @@ final class PhabricatorCountdownEditController
'June 8 2011, 5 PM.'))) 'June 8 2011, 5 PM.')))
->appendChild( ->appendChild(
id(new AphrontFormSubmitControl()) id(new AphrontFormSubmitControl())
->addCancelButton('/countdown/') ->addCancelButton($cancel_uri)
->setValue($action_label)); ->setValue($submit_label));
$panel = id(new AphrontPanelView())
->setWidth(AphrontPanelView::WIDTH_FORM)
->setHeader($action_label)
->setNoBackground()
->appendChild($form);
$crumbs = $this
->buildApplicationCrumbs()
->addCrumb(
id(new PhabricatorCrumbView())
->setName($action_label)
->setHref($this->getApplicationURI('edit/')));
return $this->buildApplicationPage( return $this->buildApplicationPage(
array( array(
$crumbs, $crumbs,
$error_view, $error_view,
$panel, $form,
), ),
array( array(
'title' => pht('Edit Countdown'), 'title' => pht('Edit Countdown'),
'device' => true, 'device' => true,
'dust' => true,
)); ));
} }
} }

View file

@ -1,107 +1,69 @@
<?php <?php
/**
* @group countdown
*/
final class PhabricatorCountdownListController final class PhabricatorCountdownListController
extends PhabricatorCountdownController { extends PhabricatorCountdownController
implements PhabricatorApplicationSearchResultsControllerInterface {
private $queryKey;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->queryKey = idx($data, 'queryKey');
}
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $controller = id(new PhabricatorApplicationSearchController($request))
->setQueryKey($this->queryKey)
->setSearchEngine(new PhabricatorCountdownSearchEngine())
->setNavigation($this->buildSideNavView());
$pager = new AphrontCursorPagerView(); return $this->delegateToController($controller);
$pager->readFromRequest($request); }
$query = id(new PhabricatorCountdownQuery()) public function renderResultsList(
->setViewer($user); array $countdowns,
PhabricatorSavedQuery $query) {
assert_instances_of($countdowns, 'PhabricatorCountdown');
$countdowns = $query->executeWithCursorPager($pager); $viewer = $this->getRequest()->getUser();
$phids = mpull($countdowns, 'getAuthorPHID'); $this->loadHandles(mpull($countdowns, 'getAuthorPHID'));
$handles = $this->loadViewerHandles($phids);
$rows = array();
foreach ($countdowns as $timer) {
$edit_button = null;
$delete_button = null;
if ($user->getIsAdmin() ||
($user->getPHID() == $timer->getAuthorPHID())) {
$edit_button = phutil_tag(
'a',
array(
'class' => 'small button grey',
'href' => '/countdown/edit/'.$timer->getID().'/'
),
pht('Edit'));
$delete_button = javelin_tag( $list = new PhabricatorObjectItemListView();
'a', $list->setUser($viewer);
array( foreach ($countdowns as $countdown) {
'class' => 'small button grey', $id = $countdown->getID();
'href' => '/countdown/delete/'.$timer->getID().'/',
'sigil' => 'workflow' $item = id(new PhabricatorObjectItemView())
), ->setObjectName("C{$id}")
pht('Delete')); ->setHeader($countdown->getTitle())
->setHref($this->getApplicationURI("{$id}/"))
->addByline(
pht(
'Created by %s',
$this->getHandle($countdown->getAuthorPHID())->renderLink()));
$epoch = $countdown->getEpoch();
if ($epoch >= time()) {
$item->addIcon(
'none',
pht('Ends %s', phabricator_datetime($epoch, $viewer)));
} else {
$item->addIcon(
'delete',
pht('Ended %s', phabricator_datetime($epoch, $viewer)));
$item->setDisabled(true);
} }
$rows[] = array(
$timer->getID(), $list->addItem($item);
$handles[$timer->getAuthorPHID()]->renderLink(),
phutil_tag(
'a',
array(
'href' => '/countdown/'.$timer->getID().'/',
),
$timer->getTitle()),
phabricator_datetime($timer->getEpoch(), $user),
$edit_button,
$delete_button,
);
} }
$table = new AphrontTableView($rows); return $list;
$table->setHeaders(
array(
pht('ID'),
pht('Author'),
pht('Title'),
pht('End Date'),
'',
''
));
$table->setColumnClasses(
array(
null,
null,
'wide pri',
null,
'action',
'action',
));
$panel = id(new AphrontPanelView())
->appendChild($table)
->setHeader(pht('Countdowns'))
->setNoBackground()
->appendChild($pager);
$crumbs = $this
->buildApplicationCrumbs()
->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('All Countdowns'))
->setHref($this->getApplicationURI()));
return $this->buildApplicationPage(
array(
$crumbs,
$panel
),
array(
'title' => pht('Countdown'),
'device' => true,
));
} }
} }

View file

@ -27,72 +27,90 @@ final class PhabricatorCountdownViewController
return new Aphront404Response(); return new Aphront404Response();
} }
require_celerity_resource('phabricator-countdown-css'); $countdown_view = id(new PhabricatorCountdownView())
->setUser($user)
->setCountdown($countdown)
->setHeadless(true);
$chrome_visible = $request->getBool('chrome', true); $id = $countdown->getID();
$chrome_new = $chrome_visible ? false : null; $title = $countdown->getTitle();
$chrome_link = phutil_tag(
'a',
array(
'href' => $request->getRequestURI()->alter('chrome', $chrome_new),
'class' => 'phabricator-timer-chrome-link',
),
$chrome_visible ? pht('Disable Chrome') : pht('Enable Chrome'));
$container = celerity_generate_unique_node_id();
$content = hsprintf(
'<div class="phabricator-timer" id="%s">
<h1 class="phabricator-timer-header">%s &middot; %s</h1>
<div class="phabricator-timer-pane">
<table class="phabricator-timer-table">
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
<tr>%s%s%s%s</tr>
</table>
</div>
%s
</div>',
$container,
$countdown->getTitle(),
phabricator_datetime($countdown->getEpoch(), $user),
pht('Days'),
pht('Hours'),
pht('Minutes'),
pht('Seconds'),
javelin_tag('td', array('sigil' => 'phabricator-timer-days'), ''),
javelin_tag('td', array('sigil' => 'phabricator-timer-hours'), ''),
javelin_tag('td', array('sigil' => 'phabricator-timer-minutes'), ''),
javelin_tag('td', array('sigil' => 'phabricator-timer-seconds'), ''),
$chrome_link);
Javelin::initBehavior('countdown-timer', array(
'timestamp' => $countdown->getEpoch(),
'container' => $container,
));
$panel = $content;
$crumbs = $this $crumbs = $this
->buildApplicationCrumbs() ->buildApplicationCrumbs()
->addCrumb( ->addCrumb(
id(new PhabricatorCrumbView()) id(new PhabricatorCrumbView())
->setName($countdown->getTitle()) ->setName("C{$id}"));
->setHref($this->getApplicationURI($this->id.'/')));
$header = id(new PhabricatorHeaderView())
->setHeader($title);
$actions = $this->buildActionListView($countdown);
$properties = $this->buildPropertyListView($countdown);
$content = array(
$crumbs,
$header,
$actions,
$properties,
$countdown_view,
);
return $this->buildApplicationPage( return $this->buildApplicationPage(
$content,
array( array(
($chrome_visible ? $crumbs : ''), 'title' => $title,
$panel,
),
array(
'title' => pht('Countdown: %s', $countdown->getTitle()),
'chrome' => $chrome_visible,
'device' => true, 'device' => true,
'dust' => true,
)); ));
} }
private function buildActionListView(PhabricatorCountdown $countdown) {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $countdown->getID();
$view = id(new PhabricatorActionListView())
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$countdown,
PhabricatorPolicyCapability::CAN_EDIT);
$view->addAction(
id(new PhabricatorActionView())
->setIcon('edit')
->setName(pht('Edit Countdown'))
->setHref($this->getApplicationURI("edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$view->addAction(
id(new PhabricatorActionView())
->setIcon('delete')
->setName(pht('Delete Countdown'))
->setHref($this->getApplicationURI("delete/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
return $view;
}
private function buildPropertyListView(PhabricatorCountdown $countdown) {
$request = $this->getRequest();
$viewer = $request->getUser();
$this->loadHandles(array($countdown->getAuthorPHID()));
$view = id(new PhabricatorPropertyListView())
->setUser($viewer);
$view->addProperty(
pht('Author'),
$this->getHandle($countdown->getAuthorPHID())->renderLink());
return $view;
}
} }

View file

@ -9,6 +9,7 @@ final class PhabricatorCountdownQuery
private $ids; private $ids;
private $phids; private $phids;
private $authorPHIDs; private $authorPHIDs;
private $upcoming;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
@ -25,6 +26,11 @@ final class PhabricatorCountdownQuery
return $this; return $this;
} }
public function withUpcoming($upcoming) {
$this->upcoming = true;
return $this;
}
protected function loadPage() { protected function loadPage() {
$table = new PhabricatorCountdown(); $table = new PhabricatorCountdown();
$conn_r = $table->establishConnection('r'); $conn_r = $table->establishConnection('r');
@ -69,6 +75,13 @@ final class PhabricatorCountdownQuery
$this->authorPHIDs); $this->authorPHIDs);
} }
if ($this->upcoming) {
$where[] = qsprintf(
$conn_r,
'epoch >= %d',
time());
}
return $this->formatWhereClause($where); return $this->formatWhereClause($where);
} }

View file

@ -0,0 +1,96 @@
<?php
final class PhabricatorCountdownSearchEngine
extends PhabricatorApplicationSearchEngine {
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'authorPHIDs',
array_values($request->getArr('authors')));
$saved->setParameter('upcoming', $request->getBool('upcoming'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorCountdownQuery());
$author_phids = $saved->getParameter('authorPHIDs', array());
if ($author_phids) {
$query->withAuthorPHIDs($author_phids);
}
if ($saved->getParameter('upcoming')) {
$query->withUpcoming(true);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$phids = $saved_query->getParameter('authorPHIDs', array());
$handles = id(new PhabricatorObjectHandleData($phids))
->setViewer($this->requireViewer())
->loadHandles();
$author_tokens = mpull($handles, 'getFullName', 'getPHID');
$upcoming = $saved_query->getParameter('upcoming');
$form
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setName('authors')
->setLabel(pht('Authors'))
->setValue($author_tokens))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'upcoming',
1,
pht('Show only countdowns that are still counting down.'),
$upcoming));
}
protected function getURI($path) {
return '/countdown/'.$path;
}
public function getBuiltinQueryNames() {
$names = array(
'upcoming' => pht('Upcoming'),
'all' => pht('All'),
);
if ($this->requireViewer()->getPHID()) {
$names['authored'] = pht('Authored');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'authored':
return $query->setParameter(
'authorPHIDs',
array($this->requireViewer()->getPHID()));
case 'upcoming':
return $query->setParameter('upcoming', true);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
}

View file

@ -19,27 +19,11 @@ final class PhabricatorCountdownRemarkupRule
} }
protected function renderObjectEmbed($object, $handle, $options) { protected function renderObjectEmbed($object, $handle, $options) {
require_celerity_resource('javelin-behavior-countdown-timer'); $viewer = $this->getEngine()->getConfig('viewer');
$prefix = 'phabricator-timer-'; return id(new PhabricatorCountdownView())
$counter = phutil_tag( ->setCountdown($object)
'span', ->setUser($viewer);
array(
'id' => $object->getPHID(),
),
array(
javelin_tag('span', array('sigil' => $prefix.'days'), ''), 'd',
javelin_tag('span', array('sigil' => $prefix.'hours'), ''), 'h',
javelin_tag('span', array('sigil' => $prefix.'minutes'), ''), 'm',
javelin_tag('span', array('sigil' => $prefix.'seconds'), ''), 's',
));
Javelin::initBehavior('countdown-timer', array(
'timestamp' => $object->getEpoch(),
'container' => $object->getPHID(),
));
return $counter;
} }
} }

View file

@ -29,11 +29,14 @@ final class PhabricatorCountdown
return PhabricatorPolicies::POLICY_USER; return PhabricatorPolicies::POLICY_USER;
} }
/* -( PhabricatorPolicyInterface Implementation )------------------------- */
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() { public function getCapabilities() {
return array( return array(
PhabricatorPolicyCapability::CAN_VIEW PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
); );
} }
@ -41,6 +44,8 @@ final class PhabricatorCountdown
switch ($capability) { switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW: case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy(); return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return PhabricatorPolicies::POLICY_NOONE;
} }
} }

View file

@ -0,0 +1,78 @@
<?php
final class PhabricatorCountdownView extends AphrontTagView {
private $countdown;
private $headless;
public function setHeadless($headless) {
$this->headless = $headless;
return $this;
}
public function setCountdown(PhabricatorCountdown $countdown) {
$this->countdown = $countdown;
return $this;
}
public function getTagContent() {
$countdown = $this->countdown;
require_celerity_resource('phabricator-countdown-css');
$header = null;
if (!$this->headless) {
$header = phutil_tag(
'div',
array(
'class' => 'phabricator-timer-header',
),
array(
"C".$countdown->getID(),
' ',
phutil_tag(
'a',
array(
'href' => '/countdown/'.$countdown->getID(),
),
$countdown->getTitle()),
));
}
$container = celerity_generate_unique_node_id();
$content = hsprintf(
'<div class="phabricator-timer" id="%s">
%s
<table class="phabricator-timer-table">
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>
<tr>%s%s%s%s</tr>
</table>
</div>',
$container,
$header,
pht('Days'),
pht('Hours'),
pht('Minutes'),
pht('Seconds'),
javelin_tag('td', array('sigil' => 'phabricator-timer-days'), '-'),
javelin_tag('td', array('sigil' => 'phabricator-timer-hours'), '-'),
javelin_tag('td', array('sigil' => 'phabricator-timer-minutes'), '-'),
javelin_tag('td', array('sigil' => 'phabricator-timer-seconds'), '-'));
Javelin::initBehavior('countdown-timer', array(
'timestamp' => $countdown->getEpoch(),
'container' => $container,
));
return $content;
}
}

View file

@ -3,29 +3,33 @@
*/ */
.phabricator-timer { .phabricator-timer {
width: 80%; width: 90%;
margin: 0 auto; margin: 12px auto;
margin-top: 2em; border: 1px solid #666666;
margin-bottom: 2em; border-radius: 4px;
background: #efefef; background: #ffffff;
border-top: 1px solid #cccccc; box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25);
}
.phabricator-remarkup .phabricator-timer {
width: 300px;
} }
.phabricator-timer-header { .phabricator-timer-header {
padding: 8px; font-size: 14px;
background: #dfdfdf; font-weight: bold;
} padding: 4px 8px;
background: #f0f0f0;
.phabricator-timer-pane { border-radius: 4px 4px 0 0;
padding: 8px 2em; border-bottom: 1px solid #d7d7d7;
} }
.phabricator-timer-table { .phabricator-timer-table {
width: 100%; width: 100%;
margin: 8px 0;
} }
.phabricator-timer-table th { .phabricator-timer-table th {
font-weight: bold;
text-align: center; text-align: center;
color: #666666; color: #666666;
width: 10%; width: 10%;
@ -35,11 +39,13 @@
.phabricator-timer-table td { .phabricator-timer-table td {
padding: 4px; padding: 4px;
text-align: center; text-align: center;
font-size: 4em; font-size: 36px;
overflow: hidden;
line-height: auto;
height: auto;
line-height: 36px;
} }
.phabricator-timer-chrome-link { .phabricator-timer-table small {
float: right; color: #999999;
padding: 3px 6px;
font-size: 11px;
} }

View file

@ -19,29 +19,35 @@ JX.behavior('countdown-timer', function(config) {
var hours = 0; var hours = 0;
var minutes = 0; var minutes = 0;
var seconds = 0; var seconds = 0;
var partial = 0;
var current_timestamp = Math.round(new Date() / 1000); var current_timestamp = +new Date();
var delta = config.timestamp - current_timestamp;
var delta = (config.timestamp * 1000) - current_timestamp;
if (delta > 0) { if (delta > 0) {
days = Math.floor(delta/86400); days = Math.floor(delta / 86400000);
delta -= days * 86400; delta -= days * 86400000;
hours = Math.floor(delta/3600); hours = Math.floor(delta / 3600000);
delta -= hours * 3600; delta -= hours * 3600000;
minutes = Math.floor(delta / 60); minutes = Math.floor(delta / 60000);
delta -= minutes * 60; delta -= minutes * 60000;
seconds = delta; seconds = Math.floor(delta / 1000);
delta -= seconds * 1000;
setTimeout(calculateTimeLeft, 1000); partial = Math.floor(delta / 100) || '0';
setTimeout(calculateTimeLeft, 100);
} }
setComponent('days', days); setComponent('days', days);
setComponent('hours', hours); setComponent('hours', hours);
setComponent('minutes', minutes); setComponent('minutes', minutes);
setComponent('seconds', seconds); setComponent('seconds', [seconds, JX.$N('small', {}, ['.', partial])]);
} }
}); });