Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14095762
D12988.id31305.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D12988.id31305.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D12988: Rescheduling events by dragging them in day view
Attached
Detach File
Event Timeline
Log In to Comment