From 14af40afa09e97903fed59126daf252182184801 Mon Sep 17 00:00:00 2001 From: lkassianik Date: Tue, 5 May 2015 12:51:32 -0700 Subject: [PATCH] Calendar day view should have forward/back controls to scroll through days Summary: Ref T4393, Calendar day view should have forward/back controls to scroll through days Test Plan: Open day view on first or last day of the month, scrolling backwards or forwards through days should correctly calculate the date of the next day and the previous day and correctly find events. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin, epriestley Maniphest Tasks: T4393 Differential Revision: https://secure.phabricator.com/D12721 --- .../PhabricatorCalendarApplication.php | 3 +- ...PhabricatorCalendarEventListController.php | 3 +- .../PhabricatorCalendarEventSearchEngine.php | 45 ++--- .../phui/calendar/PHUICalendarDayView.php | 159 +++++++++++++++++- 4 files changed, 170 insertions(+), 40 deletions(-) diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php index 7a1ca000e5..aeeaa68d2b 100644 --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -42,7 +42,8 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication { return array( '/E(?P[1-9]\d*)' => 'PhabricatorCalendarEventViewController', '/calendar/' => array( - '(?:query/(?P[^/]+)/(?:(?P\d+)/(?P\d+)/)?)?' + '(?:query/(?P[^/]+)/(?:(?P\d+)/'. + '(?P\d+)/)?(?:(?P\d+)/)?)?' => 'PhabricatorCalendarEventListController', 'event/' => array( 'create/' diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventListController.php b/src/applications/calendar/controller/PhabricatorCalendarEventListController.php index 30a908fb8e..519841d1f3 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarEventListController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventListController.php @@ -10,10 +10,11 @@ final class PhabricatorCalendarEventListController public function handleRequest(AphrontRequest $request) { $year = $request->getURIData('year'); $month = $request->getURIData('month'); + $day = $request->getURIData('day'); $engine = new PhabricatorCalendarEventSearchEngine(); if ($month && $year) { - $engine->setCalendarYearAndMonth($year, $month); + $engine->setCalendarYearAndMonthAndDay($year, $month, $day); } $controller = id(new PhabricatorApplicationSearchController()) diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php index e4a9067a5b..05b1d95f51 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php @@ -57,7 +57,8 @@ final class PhabricatorCalendarEventSearchEngine $max_range = $this->getDateTo($saved)->getEpoch(); if ($saved->getParameter('display') == 'month') { - list($start_month, $start_year) = $this->getDisplayMonthAndYear($saved); + list($start_year, $start_month) = + $this->getDisplayYearAndMonthAndDay($saved); $start_day = 1; $end_year = ($start_month == 12) ? $start_year + 1 : $start_year; @@ -226,9 +227,10 @@ final class PhabricatorCalendarEventSearchEngine return $names; } - public function setCalendarYearAndMonth($year, $month) { + public function setCalendarYearAndMonthAndDay($year, $month, $day = null) { $this->calendarYear = $year; $this->calendarMonth = $month; + $this->calendarDay = $day; return $this; } @@ -309,7 +311,8 @@ final class PhabricatorCalendarEventSearchEngine $viewer = $this->requireViewer(); $now = time(); - list($start_month, $start_year) = $this->getDisplayMonthAndYear($query); + list($start_year, $start_month) = + $this->getDisplayYearAndMonthAndDay($query); $now_year = phabricator_format_local_time($now, $viewer, 'Y'); $now_month = phabricator_format_local_time($now, $viewer, 'm'); @@ -373,12 +376,12 @@ final class PhabricatorCalendarEventSearchEngine PhabricatorSavedQuery $query, array $handles) { $viewer = $this->requireViewer(); - list($start_month, $start_year, $start_day) = - $this->getDisplayMonthAndYearAndDay($query); + list($start_year, $start_month, $start_day) = + $this->getDisplayYearAndMonthAndDay($query); $day_view = new PHUICalendarDayView( - $start_month, $start_year, + $start_month, $start_day); $day_view->setUser($viewer); @@ -395,39 +398,19 @@ final class PhabricatorCalendarEventSearchEngine $day_view->addEvent($event); } + $day_view->setBrowseURI( + $this->getURI('query/'.$query->getQueryKey().'/')); + return $day_view; } - private function getDisplayMonthAndYear( + private function getDisplayYearAndMonthAndDay( PhabricatorSavedQuery $query) { $viewer = $this->requireViewer(); - - // get month/year from url if ($this->calendarYear && $this->calendarMonth) { $start_year = $this->calendarYear; $start_month = $this->calendarMonth; - } else { - $epoch = $this->getDateFrom($query)->getEpoch(); - if (!$epoch) { - $epoch = $this->getDateTo($query)->getEpoch(); - if (!$epoch) { - $epoch = time(); - } - } - $start_year = phabricator_format_local_time($epoch, $viewer, 'Y'); - $start_month = phabricator_format_local_time($epoch, $viewer, 'm'); - } - - return array($start_month, $start_year); - } - - private function getDisplayMonthAndYearAndDay( - PhabricatorSavedQuery $query) { - $viewer = $this->requireViewer(); - if ($this->calendarYear && $this->calendarMonth && $this->calendarDay) { - $start_year = $this->calendarYear; - $start_month = $this->calendarMonth; - $start_day = $this->calendarDay; + $start_day = $this->calendarDay ? $this->calendarDay : 1; } else { $epoch = $this->getDateFrom($query)->getEpoch(); if (!$epoch) { diff --git a/src/view/phui/calendar/PHUICalendarDayView.php b/src/view/phui/calendar/PHUICalendarDayView.php index 61a0b09c7a..69bdb69239 100644 --- a/src/view/phui/calendar/PHUICalendarDayView.php +++ b/src/view/phui/calendar/PHUICalendarDayView.php @@ -5,6 +5,7 @@ final class PHUICalendarDayView extends AphrontView { private $day; private $month; private $year; + private $browseURI; private $events = array(); public function addEvent(AphrontCalendarDayEventView $event) { @@ -12,6 +13,14 @@ final class PHUICalendarDayView extends AphrontView { return $this; } + public function setBrowseURI($browse_uri) { + $this->browseURI = $browse_uri; + return $this; + } + private function getBrowseURI() { + return $this->browseURI; + } + public function __construct($year, $month, $day = null) { $this->day = $day; $this->month = $month; @@ -21,11 +30,6 @@ final class PHUICalendarDayView extends AphrontView { public function render() { require_celerity_resource('phui-calendar-day-css'); - $day_box = new PHUIObjectBoxView(); - $day_of_week = $this->getDayOfWeek(); - $header_text = $this->getDateTime()->format('F j, Y'); - $header_text = $day_of_week.', '.$header_text; - $day_box->setHeaderText($header_text); $hours = $this->getHoursOfDay(); $hourly_events = array(); $rows = array(); @@ -112,9 +116,64 @@ final class PHUICalendarDayView extends AphrontView { $rows, )); - $day_box->appendChild($table); - return $day_box; + $header = $this->renderDayViewHeader(); + $day_box = (new PHUIObjectBoxView()) + ->setHeader($header) + ->appendChild($table); + + return $day_box; + } + + private function renderDayViewHeader() { + $button_bar = null; + + // check for a browseURI, which means we need "fancy" prev / next UI + $uri = $this->getBrowseURI(); + if ($uri) { + list($prev_year, $prev_month, $prev_day) = $this->getPrevDay(); + $prev_uri = $uri.$prev_year.'/'.$prev_month.'/'.$prev_day.'/'; + + list($next_year, $next_month, $next_day) = $this->getNextDay(); + $next_uri = $uri.$next_year.'/'.$next_month.'/'.$next_day.'/'; + + $button_bar = new PHUIButtonBarView(); + + $left_icon = id(new PHUIIconView()) + ->setIconFont('fa-chevron-left bluegrey'); + $left = id(new PHUIButtonView()) + ->setTag('a') + ->setColor(PHUIButtonView::GREY) + ->setHref($prev_uri) + ->setTitle(pht('Previous Day')) + ->setIcon($left_icon); + + $right_icon = id(new PHUIIconView()) + ->setIconFont('fa-chevron-right bluegrey'); + $right = id(new PHUIButtonView()) + ->setTag('a') + ->setColor(PHUIButtonView::GREY) + ->setHref($next_uri) + ->setTitle(pht('Next Day')) + ->setIcon($right_icon); + + $button_bar->addButton($left); + $button_bar->addButton($right); + + } + + $day_of_week = $this->getDayOfWeek(); + $header_text = $this->getDateTime()->format('F j, Y'); + $header_text = $day_of_week.', '.$header_text; + + $header = id(new PHUIHeaderView()) + ->setHeader($header_text); + + if ($button_bar) { + $header->setButtonBar($button_bar); + } + + return $header; } private function updateEventsFromCluster($cluster, $hourly_events) { @@ -194,6 +253,92 @@ final class PHUICalendarDayView extends AphrontView { return $included_datetimes; } + private function getNumberOfDaysInMonth($month, $year) { + $user = $this->user; + $timezone = new DateTimeZone($user->getTimezoneIdentifier()); + + list($next_year, $next_month) = $this->getNextYearAndMonth($month, $year); + + $end_date = new DateTime("{$next_year}-{$next_month}-01", $timezone); + $end_epoch = $end_date->format('U'); + + $days = 0; + for ($day = 1; $day <= 31; $day++) { + $day_date = new DateTime("{$year}-{$month}-{$day}", $timezone); + $day_epoch = $day_date->format('U'); + if ($day_epoch >= $end_epoch) { + break; + } else { + $days++; + } + } + + return $days; + } + + private function getPrevDay() { + $day = $this->day; + $month = $this->month; + $year = $this->year; + + $prev_year = $year; + $prev_month = $month; + $prev_day = $day - 1; + if ($prev_day == 0) { + $prev_month--; + if ($prev_month == 0) { + $prev_year--; + $prev_month = 12; + } + $prev_day = $this->getNumberOfDaysInMonth($prev_month, $prev_year); + } + + return array($prev_year, $prev_month, $prev_day); + } + + private function getNextDay() { + $day = $this->day; + $month = $this->month; + $year = $this->year; + + $next_year = $year; + $next_month = $month; + $next_day = $day + 1; + $days_in_month = $this->getNumberOfDaysInMonth($month, $year); + if ($next_day > $days_in_month) { + $next_day = 1; + $next_month++; + } + if ($next_month == 13) { + $next_year++; + $next_month = 1; + } + + return array($next_year, $next_month, $next_day); + } + + private function getNextYearAndMonth($month, $year) { + $next_year = $year; + $next_month = $month + 1; + if ($next_month == 13) { + $next_year = $year + 1; + $next_month = 1; + } + + return array($next_year, $next_month); + } + + private function getPrevYearAndMonth($month, $year) { + $prev_year = $year; + $prev_month = $month - 1; + if ($prev_month == 0) { + $prev_year = $year - 1; + $prev_month = 12; + } + + return array($prev_year, $prev_month); + } + private function getDateTime() { $user = $this->user;