@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix the most significant "phantom notification" badness

Summary:
Ref T13124. Ref T13131. Fixes T8953. See PHI512.

When you receieve a notification about an object and then someone hides that object from you (or deletes it), you get a phantom notification which is very difficult to clear.

For now, test that notifications are visible when you open the menu and clear any that are not.

This could be a little more elegant than it is, but the current behavior is very clearly broken. This unbreaks it, at least.

Test Plan:
- As Alice, configured task stuff to notify me (instead of sending email).
- As Bailey, added Alice as a subscriber to a task, then commented on it.
- As Alice, loaded home and saw a notification count. Didn't click it yet.
- As Bailey, set the task to private.
- As Alice, clicked the notification bell menu icon.
- Before change: no unread notifications, bell menu is semi-stuck in a phantom state which you can't clear.
- After change: bad notifications automatically cleared.

{F5530005}

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13131, T13124, T8953

Differential Revision: https://secure.phabricator.com/D19384

+105 -13
+3 -3
resources/celerity/map.php
··· 9 9 'names' => array( 10 10 'conpherence.pkg.css' => 'e68cf1fa', 11 11 'conpherence.pkg.js' => '15191c65', 12 - 'core.pkg.css' => '39061f68', 12 + 'core.pkg.css' => 'c30f6eaa', 13 13 'core.pkg.js' => 'e1f0f7bd', 14 14 'differential.pkg.css' => '06dc617c', 15 15 'differential.pkg.js' => 'c2ca903a', ··· 38 38 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 39 39 'rsrc/css/application/auth/auth.css' => '0877ed6e', 40 40 'rsrc/css/application/base/main-menu-view.css' => '1802a242', 41 - 'rsrc/css/application/base/notification-menu.css' => '10685bd4', 41 + 'rsrc/css/application/base/notification-menu.css' => 'ef480927', 42 42 'rsrc/css/application/base/phui-theme.css' => '9f261c6b', 43 43 'rsrc/css/application/base/standard-page-view.css' => '34ee718b', 44 44 'rsrc/css/application/chatlog/chatlog.css' => 'd295b020', ··· 768 768 'phabricator-nav-view-css' => '694d7723', 769 769 'phabricator-notification' => '4f774dac', 770 770 'phabricator-notification-css' => '457861ec', 771 - 'phabricator-notification-menu-css' => '10685bd4', 771 + 'phabricator-notification-menu-css' => 'ef480927', 772 772 'phabricator-object-selector-css' => '85ee8ce6', 773 773 'phabricator-phtize' => 'd254d646', 774 774 'phabricator-prefab' => '77b0ae28',
+83 -3
src/applications/notification/controller/PhabricatorNotificationPanelController.php
··· 6 6 public function handleRequest(AphrontRequest $request) { 7 7 $viewer = $request->getViewer(); 8 8 9 + $unread_count = $viewer->getUnreadNotificationCount(); 10 + 11 + $warning = $this->prunePhantomNotifications($unread_count); 12 + 9 13 $query = id(new PhabricatorNotificationQuery()) 10 14 ->setViewer($viewer) 11 15 ->withUserPHIDs(array($viewer->getPHID())) ··· 66 70 )); 67 71 68 72 $content = hsprintf( 69 - '%s%s%s', 73 + '%s%s%s%s', 70 74 $header, 75 + $warning, 71 76 $content, 72 77 $connection_ui); 73 - 74 - $unread_count = $viewer->getUnreadNotificationCount(); 75 78 76 79 $json = array( 77 80 'content' => $content, ··· 80 83 81 84 return id(new AphrontAjaxResponse())->setContent($json); 82 85 } 86 + 87 + private function prunePhantomNotifications($unread_count) { 88 + // See T8953. If you have an unread notification about an object you 89 + // do not have permission to view, it isn't possible to clear it by 90 + // visiting the object. Identify these notifications and mark them as 91 + // read. 92 + 93 + $viewer = $this->getViewer(); 94 + 95 + if (!$unread_count) { 96 + return null; 97 + } 98 + 99 + $table = new PhabricatorFeedStoryNotification(); 100 + $conn = $table->establishConnection('r'); 101 + 102 + $rows = queryfx_all( 103 + $conn, 104 + 'SELECT chronologicalKey, primaryObjectPHID FROM %T 105 + WHERE userPHID = %s AND hasViewed = 0', 106 + $table->getTableName(), 107 + $viewer->getPHID()); 108 + if (!$rows) { 109 + return null; 110 + } 111 + 112 + $map = array(); 113 + foreach ($rows as $row) { 114 + $map[$row['primaryObjectPHID']][] = $row['chronologicalKey']; 115 + } 116 + 117 + $handles = $viewer->loadHandles(array_keys($map)); 118 + $purge_keys = array(); 119 + foreach ($handles as $handle) { 120 + $phid = $handle->getPHID(); 121 + if ($handle->isComplete()) { 122 + continue; 123 + } 124 + 125 + foreach ($map[$phid] as $chronological_key) { 126 + $purge_keys[] = $chronological_key; 127 + } 128 + } 129 + 130 + if (!$purge_keys) { 131 + return null; 132 + } 133 + 134 + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 135 + 136 + $conn = $table->establishConnection('w'); 137 + queryfx( 138 + $conn, 139 + 'UPDATE %T SET hasViewed = 1 140 + WHERE userPHID = %s AND chronologicalKey IN (%Ls)', 141 + $table->getTableName(), 142 + $viewer->getPHID(), 143 + $purge_keys); 144 + 145 + PhabricatorUserCache::clearCache( 146 + PhabricatorUserNotificationCountCacheType::KEY_COUNT, 147 + $viewer->getPHID()); 148 + 149 + unset($unguarded); 150 + 151 + return phutil_tag( 152 + 'div', 153 + array( 154 + 'class' => 'phabricator-notification phabricator-notification-warning', 155 + ), 156 + pht( 157 + '%s notification(s) about objects which no longer exist or which '. 158 + 'you can no longer see were discarded.', 159 + phutil_count($purge_keys))); 160 + } 161 + 162 + 83 163 }
+6 -6
src/applications/notification/query/PhabricatorNotificationQuery.php
··· 76 76 return $stories; 77 77 } 78 78 79 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 80 - $where = array(); 79 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 80 + $where = parent::buildWhereClauseParts($conn); 81 81 82 82 if ($this->userPHIDs !== null) { 83 83 $where[] = qsprintf( 84 - $conn_r, 84 + $conn, 85 85 'notif.userPHID IN (%Ls)', 86 86 $this->userPHIDs); 87 87 } 88 88 89 89 if ($this->unread !== null) { 90 90 $where[] = qsprintf( 91 - $conn_r, 91 + $conn, 92 92 'notif.hasViewed = %d', 93 93 (int)!$this->unread); 94 94 } 95 95 96 96 if ($this->keys) { 97 97 $where[] = qsprintf( 98 - $conn_r, 98 + $conn, 99 99 'notif.chronologicalKey IN (%Ls)', 100 100 $this->keys); 101 101 } 102 102 103 - return $this->formatWhereClause($where); 103 + return $where; 104 104 } 105 105 106 106 protected function getResultCursor($item) {
+8
src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
··· 1651 1651 'Destroyed %s credentials of type "%s".', 1652 1652 ), 1653 1653 1654 + '%s notification(s) about objects which no longer exist or which '. 1655 + 'you can no longer see were discarded.' => array( 1656 + 'One notification about an object which no longer exists or which '. 1657 + 'you can no longer see was discarded.', 1658 + '%s notifications about objects which no longer exist or which '. 1659 + 'you can no longer see were discarded.', 1660 + ), 1661 + 1654 1662 ); 1655 1663 } 1656 1664
+5 -1
webroot/rsrc/css/application/base/notification-menu.css
··· 68 68 color: {$lightgreytext}; 69 69 } 70 70 71 + .phabricator-notification-warning { 72 + background: {$sh-yellowbackground}; 73 + } 74 + 71 75 .phabricator-notification-list .phabricator-notification-unread, 72 76 .phabricator-notification-menu .phabricator-notification-unread { 73 77 background: {$hoverblue}; ··· 95 99 .phabricator-notification-unread .phabricator-notification-foot 96 100 .phabricator-notification-status { 97 101 font-size: 7px; 98 - color: {$lightgreytext}; 102 + color: {$lightbluetext}; 99 103 position: absolute; 100 104 display: inline-block; 101 105 top: 6px;