Page MenuHomePhabricator

D12988.id31305.diff
No OneTemporary

D12988.id31305.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -121,7 +121,7 @@
'rsrc/css/layout/phabricator-hovercard-view.css' => 'dd9121a9',
'rsrc/css/layout/phabricator-side-menu-view.css' => 'c1db9e9c',
'rsrc/css/layout/phabricator-source-code-view.css' => '2ceee894',
- 'rsrc/css/phui/calendar/phui-calendar-day.css' => 'c0cf782a',
+ 'rsrc/css/phui/calendar/phui-calendar-day.css' => '63d4cd7b',
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1c7f338',
'rsrc/css/phui/calendar/phui-calendar-month.css' => '476be7e0',
'rsrc/css/phui/calendar/phui-calendar.css' => 'ccabe893',
@@ -331,7 +331,7 @@
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'b1a59974',
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761',
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
- 'rsrc/js/application/calendar/behavior-day-view.js' => 'f4f4ad80',
+ 'rsrc/js/application/calendar/behavior-day-view.js' => 'dd9a862a',
'rsrc/js/application/calendar/behavior-event-all-day.js' => '38dcf3c8',
'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de',
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '10246726',
@@ -554,7 +554,7 @@
'javelin-behavior-dashboard-move-panels' => '82439934',
'javelin-behavior-dashboard-query-panel-select' => '453c5375',
'javelin-behavior-dashboard-tab-panel' => 'd4eecc63',
- 'javelin-behavior-day-view' => 'f4f4ad80',
+ 'javelin-behavior-day-view' => 'dd9a862a',
'javelin-behavior-device' => 'a205cf28',
'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18',
'javelin-behavior-differential-comment-jump' => '4fdb476d',
@@ -767,7 +767,7 @@
'phui-box-css' => '7b3a2eed',
'phui-button-css' => 'de610129',
'phui-calendar-css' => 'ccabe893',
- 'phui-calendar-day-css' => 'c0cf782a',
+ 'phui-calendar-day-css' => '63d4cd7b',
'phui-calendar-list-css' => 'c1c7f338',
'phui-calendar-month-css' => '476be7e0',
'phui-crumbs-view-css' => '594d719e',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1498,6 +1498,7 @@
'PhabricatorCalendarEvent' => 'applications/calendar/storage/PhabricatorCalendarEvent.php',
'PhabricatorCalendarEventCancelController' => 'applications/calendar/controller/PhabricatorCalendarEventCancelController.php',
'PhabricatorCalendarEventCommentController' => 'applications/calendar/controller/PhabricatorCalendarEventCommentController.php',
+ 'PhabricatorCalendarEventDragController' => 'applications/calendar/controller/PhabricatorCalendarEventDragController.php',
'PhabricatorCalendarEventEditController' => 'applications/calendar/controller/PhabricatorCalendarEventEditController.php',
'PhabricatorCalendarEventEditIconController' => 'applications/calendar/controller/PhabricatorCalendarEventEditIconController.php',
'PhabricatorCalendarEventEditor' => 'applications/calendar/editor/PhabricatorCalendarEventEditor.php',
@@ -4853,6 +4854,7 @@
),
'PhabricatorCalendarEventCancelController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventCommentController' => 'PhabricatorCalendarController',
+ 'PhabricatorCalendarEventDragController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditIconController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditor' => 'PhabricatorApplicationTransactionEditor',
diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php
--- a/src/applications/calendar/application/PhabricatorCalendarApplication.php
+++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php
@@ -54,6 +54,8 @@
=> 'PhabricatorCalendarEventEditController',
'edit/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventEditController',
+ 'drag/(?P<id>[1-9]\d*)/'
+ => 'PhabricatorCalendarEventDragController',
'cancel/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventCancelController',
'(?P<action>join|decline|accept)/(?P<id>[1-9]\d*)/'
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventDragController.php b/src/applications/calendar/controller/PhabricatorCalendarEventDragController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventDragController.php
@@ -0,0 +1,62 @@
+<?php
+
+final class PhabricatorCalendarEventDragController
+ extends PhabricatorCalendarController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $user = $request->getUser();
+
+ $event = id(new PhabricatorCalendarEventQuery())
+ ->setViewer($user)
+ ->withIDs(array($request->getURIData('id')))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$event) {
+ return new Aphront404Response();
+ }
+
+ if (!$request->validateCSRF()) {
+ return new Aphront400Response();
+ }
+
+ if ($event->getIsAllDay()) {
+ return new Aphront400Response();
+ }
+
+ $xactions = array();
+
+ $duration = $event->getDateTo() - $event->getDateFrom();
+
+ $start = $request->getInt('start');
+ $start_value = id(AphrontFormDateControlValue::newFromEpoch($user, $start));
+
+ $end = $start + $duration;
+ $end_value = id(AphrontFormDateControlValue::newFromEpoch($user, $end));
+
+
+ $xactions[] = id(new PhabricatorCalendarEventTransaction())
+ ->setTransactionType(
+ PhabricatorCalendarEventTransaction::TYPE_START_DATE)
+ ->setNewValue($start_value);
+
+ $xactions[] = id(new PhabricatorCalendarEventTransaction())
+ ->setTransactionType(
+ PhabricatorCalendarEventTransaction::TYPE_END_DATE)
+ ->setNewValue($end_value);
+
+
+ $editor = id(new PhabricatorCalendarEventEditor())
+ ->setActor($user)
+ ->setContinueOnMissingFields(true)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true);
+
+ $xactions = $editor->applyTransactions($event, $xactions);
+
+ return id(new AphrontReloadResponse());
+ }
+}
diff --git a/src/view/phui/calendar/PHUICalendarDayView.php b/src/view/phui/calendar/PHUICalendarDayView.php
--- a/src/view/phui/calendar/PHUICalendarDayView.php
+++ b/src/view/phui/calendar/PHUICalendarDayView.php
@@ -44,6 +44,8 @@
public function render() {
require_celerity_resource('phui-calendar-day-css');
+ $viewer = $this->getUser();
+
$hours = $this->getHoursOfDay();
$js_hours = array();
@@ -67,6 +69,8 @@
$day_start_epoch = $day_start->format('U');
$day_end_epoch = $day_end->format('U') - 1;
+
+
foreach ($all_day_events as $all_day_event) {
$all_day_start = $all_day_event->getEpochStart();
$all_day_end = $all_day_event->getEpochEnd();
@@ -81,61 +85,58 @@
}
}
- foreach ($hours as $hour) {
- $current_hour_events = array();
- $hour_start = $hour->format('U');
- $hour_end = id(clone $hour)->modify('+1 hour')->format('U');
-
- foreach ($this->events as $event) {
- if ($event->getIsAllDay()) {
- continue;
- }
- if (($hour == $day_start &&
- $event->getEpochStart() <= $hour_start &&
- $event->getEpochEnd() > $day_start_epoch) ||
- ($event->getEpochStart() >= $hour_start
- && $event->getEpochStart() < $hour_end)) {
- $current_hour_events[] = $event;
- $this->jsTodayEvents[] = array(
- 'eventStartEpoch' => $event->getEpochStart(),
- 'eventEndEpoch' => $event->getEpochEnd(),
- 'eventName' => $event->getName(),
- 'eventID' => $event->getEventID(),
- 'viewerIsInvited' => $event->getViewerIsInvited(),
- 'uri' => $event->getURI(),
- );
- }
- }
- foreach ($current_hour_events as $event) {
- $day_start_epoch = $this->getDateTime()->format('U');
- $event_start = max($event->getEpochStart(), $day_start_epoch);
- $event_end = min($event->getEpochEnd(), $day_end_epoch);
-
- $top = (($event_start - $hour_start) / ($hour_end - $hour_start))
- * 100;
- $top = max(0, $top);
+ $this->events = msort($this->events, 'getEpochStart');
- $height = (($event_end - $event_start) / ($hour_end - $hour_start))
- * 100;
- $height = min(2400, $height);
+ if (count($this->events) == 0) {
+ $first_event_hour = $this->getDateTime()->setTime(8, 0, 0);
+ }
- if ($first_event_hour === null) {
- $first_event_hour = $hour;
+ foreach ($this->events as $event) {
+ if ($event->getIsAllDay()) {
+ continue;
+ }
+ if ($event->getEpochStart() <= $day_end_epoch &&
+ $event->getEpochEnd() > $day_start_epoch) {
+
+ if ($first_event_hour === null) {
+ $first_event_hour = new DateTime('@'.$event->getEpochStart());
+ $first_event_hour->setTimeZone($viewer->getTimeZone());
+ $eight_am = $this->getDateTime()->setTime(8, 0, 0);
+ if ($eight_am->format('U') < $first_event_hour->format('U')) {
+ $first_event_hour = clone $eight_am;
}
+ }
- $js_hourly_events[] = array(
- 'eventStartEpoch' => $event->getEpochStart(),
- 'eventEndEpoch' => $event->getEpochEnd(),
- 'eventName' => $event->getName(),
- 'eventID' => $event->getEventID(),
- 'viewerIsInvited' => $event->getViewerIsInvited(),
- 'uri' => $event->getURI(),
- 'hour' => $hour->format('G'),
- 'offset' => '0',
- 'width' => '100%',
- 'top' => $top.'%',
- 'height' => $height.'%',
- );
+ $day_start_epoch = $this->getDateTime()->format('U');
+ $day_end = id(clone $this->getDateTime())->modify('+1 day');
+ $day_end_epoch = $day_end->format('U');
+
+ $event_start = max($event->getEpochStart(), $day_start_epoch);
+ $event_end = min($event->getEpochEnd(), $day_end_epoch);
+
+ $top = (($event_start - $first_event_hour->format('U'))
+ / ($day_end_epoch - $first_event_hour->format('U')))
+ * 960;
+ $top = max(0, $top);
+
+ $height = (($event_end - $event_start)
+ / ($day_end_epoch - $first_event_hour->format('U')))
+ * 960;
+ $height = min(960, $height);
+
+ $this->jsTodayEvents[] = array(
+ 'eventStartEpoch' => $event->getEpochStart(),
+ 'eventEndEpoch' => $event->getEpochEnd(),
+ 'eventName' => $event->getName(),
+ 'eventID' => $event->getEventID(),
+ 'viewerIsInvited' => $event->getViewerIsInvited(),
+ 'uri' => $event->getURI(),
+ 'hour' => $hour->format('G'),
+ 'offset' => '0',
+ 'width' => '100%',
+ 'top' => $top.'px',
+ 'height' => $height.'px',
+ );
}
}
@@ -157,9 +158,9 @@
array(
'allDayEvents' => $js_today_all_day_events,
'todayEvents' => $this->jsTodayEvents,
- 'hourlyEvents' => $js_hourly_events,
'hours' => $js_hours,
'firstEventHour' => $first_event_hour->format('G'),
+ 'firstEventHourEpoch' => $first_event_hour->format('U'),
'tableID' => $table_id,
));
diff --git a/webroot/rsrc/css/phui/calendar/phui-calendar-day.css b/webroot/rsrc/css/phui/calendar/phui-calendar-day.css
--- a/webroot/rsrc/css/phui/calendar/phui-calendar-day.css
+++ b/webroot/rsrc/css/phui/calendar/phui-calendar-day.css
@@ -35,19 +35,28 @@
border-top: 1px solid {$lightgreyborder};
}
-.phui-calendar-day-view td div.phui-calendar-day-event {
+.phui-drag {
+ opacity: .25;
+}
+
+div.phui-calendar-day-event {
width: 100%;
position: absolute;
top: 0;
bottom: 0;
min-height: 30px;
+ z-index: 50;
+}
+
+div.phui-calendar-day-event.all-day {
+ position: relative;
}
.phui-calendar-day-event-link {
padding: 8px;
border: 1px solid {$greyborder};
background-color: {$darkgreybackground};
- margin: 0 4px;
+ margin: 0 1px;
position: absolute;
left: 0;
right: 0;
diff --git a/webroot/rsrc/js/application/calendar/behavior-day-view.js b/webroot/rsrc/js/application/calendar/behavior-day-view.js
--- a/webroot/rsrc/js/application/calendar/behavior-day-view.js
+++ b/webroot/rsrc/js/application/calendar/behavior-day-view.js
@@ -4,13 +4,6 @@
JX.behavior('day-view', function(config) {
- var hours = config.hours;
- var first_event_hour = config.firstEventHour;
- var hourly_events = config.hourlyEvents;
- var today_events = config.todayEvents;
- var today_all_day_events = config.allDayEvents;
- var table_wrapper = JX.$(config.tableID);
-
function findTodayClusters() {
var events = today_events.sort(function(x, y){
@@ -23,8 +16,8 @@
var today_event = events[i];
var destination_cluster_index = null;
- var event_start = today_event.eventStartEpoch - (30*60);
- var event_end = today_event.eventEndEpoch + (30*60);
+ var event_start = today_event.eventStartEpoch - (60);
+ var event_end = today_event.eventEndEpoch + (60);
for (var j=0; j < clusters.length; j++) {
var cluster = clusters[j];
@@ -59,7 +52,7 @@
return clusters;
}
- function updateEventsFromCluster(cluster, hourly_events) {
+ function updateEventsFromCluster(cluster) {
var cluster_size = cluster.length;
var n = 0;
for(var i=0; i < cluster.length; i++) {
@@ -69,28 +62,30 @@
var offset = ((n / cluster_size) * 100) + '%';
var width = ((1 / cluster_size) * 100) + '%';
- for (var j=0; j < hourly_events.length; j++) {
- if (hourly_events[j].eventID == event_id) {
+ for (var j=0; j < today_events.length; j++) {
+ if (today_events[j].eventID == event_id) {
- hourly_events[j]['offset'] = offset;
- hourly_events[j]['width'] = width;
+ today_events[j]['offset'] = offset;
+ today_events[j]['width'] = width;
}
}
n++;
}
- return hourly_events;
+ return today_events;
}
- function drawEvent(hourly_event) {
- var name = hourly_event['eventName'];
- var viewerIsInvited = hourly_event['viewerIsInvited'];
- var offset = hourly_event['offset'];
- var width = hourly_event['width'];
- var top = hourly_event['top'];
- var height = hourly_event['height'];
- var uri = hourly_events['uri'];
-
+ function drawEvent(e) {
+ var name = e['eventName'];
+ var eventID = e['eventID'];
+ var viewerIsInvited = e['viewerIsInvited'];
+ var offset = e['offset'];
+ var width = e['width'];
+ var top = e['top'];
+ var height = e['height'];
+ var uri = e['uri'];
+
+ var sigil = 'phui-calendar-day-event';
var link_class = 'phui-calendar-day-event-link';
if (viewerIsInvited) {
@@ -109,6 +104,8 @@
'div',
{
className: 'phui-calendar-day-event',
+ sigil: sigil,
+ meta: {eventID: eventID, record: e, uri: uri},
style: {
left: offset,
width: width,
@@ -145,7 +142,7 @@
var div_all_day = JX.$N(
'div',
- {className: 'phui-calendar-day-event'},
+ {className: 'phui-calendar-day-event all-day'},
[all_day_label, name]);
return div_all_day;
@@ -164,24 +161,17 @@
if (hours[i]['hour'] < min_early_hour) {
continue;
}
- var drawn_hourly_events = [];
var cell_time = JX.$N(
'td',
{className: 'phui-calendar-day-hour'},
hours[i]['hour_meridian']);
- for (var j=0; j < hourly_events.length; j++) {
- if (hourly_events[j]['hour'] == hours[i]['hour']) {
- drawn_hourly_events.push(drawEvent(hourly_events[j]));
- }
- }
-
var cell_event = JX.$N(
'td',
{
className: 'phui-calendar-day-events'
- },
- drawn_hourly_events);
+ });
+
var row = JX.$N(
'tr',
{},
@@ -191,10 +181,26 @@
return rows;
}
- var today_clusters = findTodayClusters();
- for(var i=0; i < today_clusters.length; i++) {
- hourly_events = updateEventsFromCluster(today_clusters[i], hourly_events);
+ function clusterAndDrawEvents() {
+ var today_clusters = findTodayClusters();
+ for(var i=0; i < today_clusters.length; i++) {
+ today_events = updateEventsFromCluster(today_clusters[i]);
+ }
+ var drawn_hourly_events = [];
+ for (i=0; i < today_events.length; i++) {
+ drawn_hourly_events.push(drawEvent(today_events[i]));
+ }
+
+ JX.DOM.setContent(hourly_events_wrapper, drawn_hourly_events);
+
}
+
+ var hours = config.hours;
+ var first_event_hour = config.firstEventHour;
+ var first_event_hour_epoch = parseInt(config.firstEventHourEpoch, 10);
+ var today_events = config.todayEvents;
+ var today_all_day_events = config.allDayEvents;
+ var table_wrapper = JX.$(config.tableID);
var rows = drawRows();
var all_day_events = [];
@@ -211,5 +217,113 @@
{className: 'phui-calendar-day-view'},
rows);
- JX.DOM.setContent(table_wrapper, [all_day_events, table]);
+ var dragging = false;
+ var origin = null;
+
+ var offset_top = null;
+ var new_top = null;
+
+ var click_time = null;
+
+ JX.DOM.listen(
+ table_wrapper,
+ 'mousedown',
+ 'phui-calendar-day-event',
+ function(e){
+
+ if (!e.isNormalMouseEvent()) {
+ return;
+ }
+ e.kill();
+ dragging = e.getNode('phui-calendar-day-event');
+ JX.DOM.alterClass(dragging, 'phui-drag', true);
+
+ click_time = new Date();
+
+ origin = JX.$V(e);
+
+ var outer = JX.Vector.getPos(table);
+ var inner = JX.Vector.getPos(dragging);
+
+ offset_top = inner.y - outer.y;
+ new_top = offset_top;
+
+ dragging.style.top = offset_top + 'px';
+
+
+ JX.log('start drag');
+ });
+ JX.Stratcom.listen('mousemove', null, function(e){
+ if (!dragging) {
+ return;
+ }
+ var cursor = JX.$V(e);
+ new_top = cursor.y - origin.y + offset_top;
+ new_top = Math.min(new_top, 960);
+ new_top = Math.max(new_top, 0);
+ new_top = Math.floor(new_top/15) * 15;
+
+ dragging.style.top = new_top + 'px';
+
+ JX.log('move');
+ });
+ JX.Stratcom.listen('mouseup', null, function(){
+ var data = JX.Stratcom.getData(dragging);
+ var record = data.record;
+
+ if (!dragging) {
+ return;
+ }
+ if (new_top == offset_top) {
+ var now = new Date();
+ if (now.getTime() - click_time.getTime() < 250) {
+ JX.$U(record.uri).go();
+ }
+
+ JX.DOM.alterClass(dragging, 'phui-drag', false);
+ dragging = false;
+ return;
+ }
+ var new_time = first_event_hour_epoch + (new_top * 60);
+ var id = data.eventID;
+ var duration = record.eventEndEpoch - record.eventStartEpoch;
+ record.eventStartEpoch = new_time;
+ record.eventEndEpoch = new_time + duration;
+ record.top = new_top + 'px';
+
+ new JX.Workflow(
+ '/calendar/event/drag/' + id + '/',
+ {start: new_time})
+ .setHandler(function(){})
+ .start();
+
+ JX.DOM.alterClass(dragging, 'phui-drag', false);
+ dragging = false;
+
+ clusterAndDrawEvents();
+
+ JX.log('drop');
+ });
+
+ JX.DOM.listen(table_wrapper, 'click', 'phui-calendar-day-event', function(e){
+ e.kill();
+ });
+
+ var hourly_events_wrapper = JX.$N(
+ 'div',
+ {style: {
+ position: 'absolute',
+ left: '69px',
+ right: 0
+ }});
+
+ clusterAndDrawEvents();
+
+ var daily_wrapper = JX.$N(
+ 'div',
+ {style: {position: 'relative'}},
+ [hourly_events_wrapper, table]);
+
+ JX.DOM.setContent(table_wrapper, [all_day_events, daily_wrapper]);
+
});

File Metadata

Mime Type
text/plain
Expires
Tue, Nov 26, 11:36 PM (17 h, 8 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6791203
Default Alt Text
D12988.id31305.diff (19 KB)

Event Timeline