Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / view / phui / PHUITimelineView.php
blob2e6d8298c86a0322ddf11241f2488cb670035b90
1 <?php
3 final class PHUITimelineView extends AphrontView {
5 private $events = array();
6 private $id;
7 private $shouldTerminate = false;
8 private $shouldAddSpacers = true;
9 private $pager;
10 private $viewData = array();
11 private $quoteTargetID;
12 private $quoteRef;
14 public function setID($id) {
15 $this->id = $id;
16 return $this;
19 public function setShouldTerminate($term) {
20 $this->shouldTerminate = $term;
21 return $this;
24 public function setShouldAddSpacers($bool) {
25 $this->shouldAddSpacers = $bool;
26 return $this;
29 public function setPager(AphrontCursorPagerView $pager) {
30 $this->pager = $pager;
31 return $this;
34 public function getPager() {
35 return $this->pager;
38 public function addEvent(PHUITimelineEventView $event) {
39 $this->events[] = $event;
40 return $this;
43 public function setViewData(array $data) {
44 $this->viewData = $data;
45 return $this;
48 public function getViewData() {
49 return $this->viewData;
52 public function setQuoteTargetID($quote_target_id) {
53 $this->quoteTargetID = $quote_target_id;
54 return $this;
57 public function getQuoteTargetID() {
58 return $this->quoteTargetID;
61 public function setQuoteRef($quote_ref) {
62 $this->quoteRef = $quote_ref;
63 return $this;
66 public function getQuoteRef() {
67 return $this->quoteRef;
70 public function render() {
71 if ($this->getPager()) {
72 if ($this->id === null) {
73 $this->id = celerity_generate_unique_node_id();
75 Javelin::initBehavior(
76 'phabricator-show-older-transactions',
77 array(
78 'timelineID' => $this->id,
79 'viewData' => $this->getViewData(),
80 ));
82 $events = $this->buildEvents();
84 return phutil_tag(
85 'div',
86 array(
87 'class' => 'phui-timeline-view',
88 'id' => $this->id,
90 array(
91 phutil_tag(
92 'h3',
93 array(
94 'class' => 'aural-only',
96 pht('Event Timeline')),
97 $events,
98 ));
101 public function buildEvents() {
102 require_celerity_resource('phui-timeline-view-css');
104 $spacer = self::renderSpacer();
106 // Track why we're hiding older results.
107 $hide_reason = null;
109 $hide = array();
110 $show = array();
112 // Bucket timeline events into events we'll hide by default (because they
113 // predate your most recent interaction with the object) and events we'll
114 // show by default.
115 foreach ($this->events as $event) {
116 if ($event->getHideByDefault()) {
117 $hide[] = $event;
118 } else {
119 $show[] = $event;
123 // If you've never interacted with the object, all the events will be shown
124 // by default. We may still need to paginate if there are a large number
125 // of events.
126 $more = (bool)$hide;
128 if ($more) {
129 $hide_reason = 'comment';
132 if ($this->getPager()) {
133 if ($this->getPager()->getHasMoreResults()) {
134 if (!$more) {
135 $hide_reason = 'limit';
137 $more = true;
141 $events = array();
142 if ($more && $this->getPager()) {
143 switch ($hide_reason) {
144 case 'comment':
145 $hide_help = pht(
146 'Changes from before your most recent comment are hidden.');
147 break;
148 case 'limit':
149 default:
150 $hide_help = pht(
151 'There are a very large number of changes, so older changes are '.
152 'hidden.');
153 break;
156 $uri = $this->getPager()->getNextPageURI();
158 $target_id = $this->getQuoteTargetID();
159 if ($target_id === null) {
160 $uri->removeQueryParam('quoteTargetID');
161 } else {
162 $uri->replaceQueryParam('quoteTargetID', $target_id);
165 $quote_ref = $this->getQuoteRef();
166 if ($quote_ref === null) {
167 $uri->removeQueryParam('quoteRef');
168 } else {
169 $uri->replaceQueryParam('quoteRef', $quote_ref);
172 $events[] = javelin_tag(
173 'div',
174 array(
175 'sigil' => 'show-older-block',
176 'class' => 'phui-timeline-older-transactions-are-hidden',
178 array(
179 $hide_help,
180 ' ',
181 javelin_tag(
182 'a',
183 array(
184 'href' => (string)$uri,
185 'mustcapture' => true,
186 'sigil' => 'show-older-link',
188 pht('Show Older Changes')),
191 if ($show) {
192 $events[] = $spacer;
196 if ($show) {
197 $this->prepareBadgeData($show);
198 $events[] = phutil_implode_html($spacer, $show);
201 if ($events) {
202 if ($this->shouldAddSpacers) {
203 $events = array($spacer, $events, $spacer);
205 } else {
206 $events = array($spacer);
209 if ($this->shouldTerminate) {
210 $events[] = self::renderEnder();
213 return $events;
216 public static function renderSpacer() {
217 return phutil_tag(
218 'div',
219 array(
220 'class' => 'phui-timeline-event-view '.
221 'phui-timeline-spacer',
223 '');
226 public static function renderEnder() {
227 return phutil_tag(
228 'div',
229 array(
230 'class' => 'phui-timeline-event-view '.
231 'the-worlds-end',
233 '');
236 private function prepareBadgeData(array $events) {
237 assert_instances_of($events, 'PHUITimelineEventView');
239 $viewer = $this->getUser();
240 $can_use_badges = PhabricatorApplication::isClassInstalledForViewer(
241 'PhabricatorBadgesApplication',
242 $viewer);
243 if (!$can_use_badges) {
244 return;
247 $user_phid_type = PhabricatorPeopleUserPHIDType::TYPECONST;
249 $user_phids = array();
250 foreach ($events as $key => $event) {
251 $author_phid = $event->getAuthorPHID();
252 if (!$author_phid) {
253 unset($events[$key]);
254 continue;
257 if (phid_get_type($author_phid) != $user_phid_type) {
258 // This is likely an application actor, like "Herald" or "Harbormaster".
259 // They can't have badges.
260 unset($events[$key]);
261 continue;
264 $user_phids[$author_phid] = $author_phid;
267 if (!$user_phids) {
268 return;
271 $users = id(new PhabricatorPeopleQuery())
272 ->setViewer($viewer)
273 ->withPHIDs($user_phids)
274 ->needBadgeAwards(true)
275 ->execute();
276 $users = mpull($users, null, 'getPHID');
278 foreach ($events as $event) {
279 $user_phid = $event->getAuthorPHID();
280 if (!array_key_exists($user_phid, $users)) {
281 continue;
283 $badges = $users[$user_phid]->getRecentBadgeAwards();
284 foreach ($badges as $badge) {
285 $badge_view = id(new PHUIBadgeMiniView())
286 ->setIcon($badge['icon'])
287 ->setQuality($badge['quality'])
288 ->setHeader($badge['name'])
289 ->setTipDirection('E')
290 ->setHref('/badges/view/'.$badge['id'].'/');
291 $event->addBadge($badge_view);