diff --git a/mod/display.php b/mod/display.php index d3c0e0249..02e838f55 100644 --- a/mod/display.php +++ b/mod/display.php @@ -183,6 +183,8 @@ function display_content(App $a, $update = false, $update_uid = 0) $item = null; + $force = (bool)($_REQUEST['force'] ?? false); + if ($update) { $item_id = $_REQUEST['item_id']; $item = Item::selectFirst(['uid', 'parent', 'parent-uri'], ['id' => $item_id]); @@ -281,7 +283,7 @@ function display_content(App $a, $update = false, $update_uid = 0) } // We need the editor here to be able to reshare an item. - if ($is_owner) { + if ($is_owner && !$update) { $x = [ 'is_owner' => true, 'allow_location' => $a->user['allow_location'], @@ -304,7 +306,7 @@ function display_content(App $a, $update = false, $update_uid = 0) $unseen = false; } - if ($update && !$unseen) { + if ($update && !$unseen && !$force) { return ''; } diff --git a/src/Model/Item.php b/src/Model/Item.php index f014ed096..0fab3ba07 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2946,39 +2946,6 @@ class Item return false; } - switch ($verb) { - case 'like': - case 'unlike': - $activity = Activity::LIKE; - break; - case 'dislike': - case 'undislike': - $activity = Activity::DISLIKE; - break; - case 'attendyes': - case 'unattendyes': - $activity = Activity::ATTEND; - break; - case 'attendno': - case 'unattendno': - $activity = Activity::ATTENDNO; - break; - case 'attendmaybe': - case 'unattendmaybe': - $activity = Activity::ATTENDMAYBE; - break; - case 'follow': - case 'unfollow': - $activity = Activity::FOLLOW; - break; - default: - Logger::log('like: unknown verb ' . $verb . ' for item ' . $item_id); - return false; - } - - // Enable activity toggling instead of on/off - $event_verb_flag = $activity === Activity::ATTEND || $activity === Activity::ATTENDNO || $activity === Activity::ATTENDMAYBE; - Logger::log('like: verb ' . $verb . ' item ' . $item_id); $item = self::selectFirst(self::ITEM_FIELDLIST, ['`id` = ? OR `uri` = ?', $item_id, $item_id]); @@ -3027,9 +2994,44 @@ class Item } } + $activity = null; + switch ($verb) { + case 'like': + case 'unlike': + $activity = Activity::LIKE; + break; + case 'dislike': + case 'undislike': + $activity = Activity::DISLIKE; + break; + case 'attendyes': + case 'unattendyes': + $activity = Activity::ATTEND; + break; + case 'attendno': + case 'unattendno': + $activity = Activity::ATTENDNO; + break; + case 'attendmaybe': + case 'unattendmaybe': + $activity = Activity::ATTENDMAYBE; + break; + case 'follow': + case 'unfollow': + $activity = Activity::FOLLOW; + break; + default: + Logger::log('like: unknown verb ' . $verb . ' for item ' . $item_id); + return false; + } + + $mode = Strings::startsWith($verb, 'un') ? 'delete' : 'create'; + + // Enable activity toggling instead of on/off + $event_verb_flag = $activity === Activity::ATTEND || $activity === Activity::ATTENDNO || $activity === Activity::ATTENDMAYBE; + // Look for an existing verb row - // event participation are essentially radio toggles. If you make a subsequent choice, - // we need to eradicate your first choice. + // Event participation activities are mutually exclusive, only one of them can exist at all times. if ($event_verb_flag) { $verbs = [Activity::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE]; @@ -3044,20 +3046,43 @@ class Item $condition = ['vid' => $vids, 'deleted' => false, 'gravity' => GRAVITY_ACTIVITY, 'author-id' => $author_id, 'uid' => $item['uid'], 'thr-parent' => $item_uri]; - $like_item = self::selectFirst(['id', 'guid', 'verb'], $condition); - // If it exists, mark it as deleted if (DBA::isResult($like_item)) { - self::markForDeletionById($like_item['id']); + /** + * Truth table for existing activities + * + * | Inputs || Outputs | + * |----------------------------||-------------------| + * | Mode | Event | Same verb || Delete? | Return? | + * |--------|-------|-----------||---------|---------| + * | create | Yes | Yes || No | Yes | + * | create | Yes | No || Yes | No | + * | create | No | Yes || No | Yes | + * | create | No | No || N/A† | + * | delete | Yes | Yes || Yes | N/A‡ | + * | delete | Yes | No || No | N/A‡ | + * | delete | No | Yes || Yes | N/A‡ | + * | delete | No | No || N/A† | + * |--------|-------|-----------||---------|---------| + * | A | B | C || A xor C | !B or C | + * + * † Can't happen: It's impossible to find an existing non-event activity without + * the same verb because we are only looking for this single verb. + * + * ‡ The "mode = delete" is returning early whether an existing activity was found or not. + */ + if ($mode == 'create' xor $like_item['verb'] == $activity) { + self::markForDeletionById($like_item['id']); + } if (!$event_verb_flag || $like_item['verb'] == $activity) { return true; } } - // Verb is "un-something", just trying to delete existing entries - if (strpos($verb, 'un') === 0) { + // No need to go further if we aren't creating anything + if ($mode == 'delete') { return true; } diff --git a/src/Module/Like.php b/src/Module/Like.php index c926012f1..ca3824750 100644 --- a/src/Module/Like.php +++ b/src/Module/Like.php @@ -22,6 +22,7 @@ namespace Friendica\Module; use Friendica\BaseModule; +use Friendica\Core\System; use Friendica\DI; use Friendica\Model\Item; use Friendica\Core\Session; @@ -68,5 +69,7 @@ class Like extends BaseModule DI::baseUrl()->redirect($returnPath . $rand); } + + System::jsonExit(['status' => 'OK']); } } diff --git a/src/Object/Post.php b/src/Object/Post.php index 1ca02873d..0a68bbbe2 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -380,8 +380,11 @@ class Post } // Disable features that aren't available in several networks - if ($buttons["dislike"] && !in_array($item["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA])) { - $buttons["dislike"] = false; + if (!in_array($item["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA])) { + if ($buttons["dislike"]) { + $buttons["dislike"] = false; + } + $isevent = false; $tagger = ''; } diff --git a/view/js/main.js b/view/js/main.js index 861315398..60337918b 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -594,15 +594,17 @@ function liveUpdate(src) { in_progress = true; - if ($(document).scrollTop() == 0) { - force_update = true; - } + let force = force_update || $(document).scrollTop() === 0; var orgHeight = $("section").height(); var udargs = ((netargs.length) ? '/' + netargs : ''); - var update_url = 'update_' + src + udargs + '&p=' + profile_uid + '&force=' + ((force_update) ? 1 : 0) + '&item=' + update_item; + var update_url = 'update_' + src + udargs + '&p=' + profile_uid + '&force=' + (force ? 1 : 0) + '&item=' + update_item; + + if (force_update) { + force_update = false; + } if (getUrlParameter('page')) { update_url += '&page=' + getUrlParameter('page'); @@ -614,9 +616,8 @@ function liveUpdate(src) { update_url += '&max_id=' + getUrlParameter('max_id'); } - $.get(update_url,function(data) { + $.get(update_url, function(data) { in_progress = false; - force_update = false; update_item = 0; $('.wall-item-body', data).imagesLoaded(function() { @@ -648,9 +649,15 @@ function imgdull(node) { // trickery. This still could cause confusion if the "like" ajax call // is delayed and NavUpdate runs before it completes. -function dolike(ident,verb) { +/** + * @param {int} ident The id of the relevant item + * @param {string} verb The verb of the action + * @param {boolean} un Whether to perform an activity removal instead of creation + */ +function dolike(ident, verb, un) { unpause(); $('#like-rotator-' + ident.toString()).show(); + verb = un ? 'un' + verb : verb; $.get('like/' + ident.toString() + '?verb=' + verb, NavUpdate); liking = 1; force_update = true; diff --git a/view/templates/wall_thread.tpl b/view/templates/wall_thread.tpl index 7b205504d..0d8c896e1 100644 --- a/view/templates/wall_thread.tpl +++ b/view/templates/wall_thread.tpl @@ -77,8 +77,8 @@