From d8efe9f8919fc75e5e54826d72a5e3f53a1fde4e Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 1 Mar 2026 16:53:52 +0000 Subject: [PATCH] Infinite scrolling is fixed in general and on contacts --- src/Content/Text/HTML.php | 11 ++++- src/Model/Contact.php | 66 +++++++++++---------------- src/Module/Contact/Conversations.php | 24 ++++++---- src/Module/Contact/Follow.php | 2 +- src/Module/Contact/Media.php | 2 +- src/Module/Contact/Posts.php | 2 +- src/Module/Contact/Unfollow.php | 2 +- src/Module/Conversation/Channel.php | 9 +--- src/Module/Conversation/Community.php | 9 +--- src/Module/Conversation/Network.php | 9 +--- src/Module/Profile/Media.php | 2 +- src/Module/Search/Filed.php | 11 +---- src/Module/Search/Index.php | 7 +-- src/Module/Update/Contact.php | 2 +- 14 files changed, 66 insertions(+), 92 deletions(-) diff --git a/src/Content/Text/HTML.php b/src/Content/Text/HTML.php index 3d789e7fa0..a30c9d7e99 100644 --- a/src/Content/Text/HTML.php +++ b/src/Content/Text/HTML.php @@ -772,10 +772,17 @@ class HTML * @return string html for loader * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function scrollLoader(): string + public static function scrollLoader(array $request = []): string { + if (in_array($request['mode'] ?? '', ['minimal', 'raw'])) { + return ''; + } + + $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); + $loader = Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]); + $tpl = Renderer::getMarkupTemplate("scroll_loader.tpl"); - return Renderer::replaceMacros($tpl, [ + return $loader . Renderer::replaceMacros($tpl, [ 'wait' => DI::l10n()->t('Loading more entries...'), 'end' => DI::l10n()->t('The end') ]); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 8226786303..6bab4573ea 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1625,29 +1625,29 @@ class Contact /** * Returns posts from a given contact url * - * @param string $contact_url Contact URL - * @param int $uid User ID - * @param bool $only_media Only display media content - * @param string $last_created Newest creation date, used for paging + * @param string $contact_url Contact URL + * @param int $uid User ID + * @param bool $only_media Only display media content + * @param array $request Request variables * @return string posts in HTML * @throws Exception */ - public static function getPostsFromUrl(string $contact_url, int $uid, bool $only_media = false, ?string $last_created = null): string + public static function getPostsFromUrl(string $contact_url, int $uid, bool $only_media = false, array $request = []): string { - return self::getPostsFromId(self::getIdForURL($contact_url), $uid, $only_media, $last_created); + return self::getPostsFromId(self::getIdForURL($contact_url), $uid, $only_media, $request); } /** * Returns posts from a given contact id * - * @param int $cid Contact ID - * @param int $uid User ID - * @param bool $only_media Only display media content - * @param string $last_created Newest creation date, used for paging + * @param int $cid Contact ID + * @param int $uid User ID + * @param bool $only_media Only display media content + * @param array $request Request variables * @return string posts in HTML * @throws Exception */ - public static function getPostsFromId(int $cid, int $uid, bool $only_media = false, ?string $last_created = null): string + public static function getPostsFromId(int $cid, int $uid, bool $only_media = false, array $request = []): string { $contact = DBA::selectFirst('contact', ['contact-type', 'network', 'name', 'nick'], ['id' => $cid]); if (!DBA::isResult($contact)) { @@ -1668,8 +1668,8 @@ class Contact $condition = DBA::mergeConditions($condition, ["`$contact_field` = ? AND `gravity` IN (?, ?)", $cid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]); - if (!empty($last_created)) { - $condition = DBA::mergeConditions($condition, ["`created` < ?", $last_created]); + if (!empty($request['last_created'])) { + $condition = DBA::mergeConditions($condition, ["`created` < ?", $request['last_created']]); } if ($only_media) { @@ -1688,21 +1688,13 @@ class Contact $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage); $params = ['order' => ['created' => true], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]]; - - if (DI::pConfig()->get($uid, 'system', 'infinite_scroll', true)) { - $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); - $o = Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]); - } else { - $o = ''; - } - $fields = array_merge(Item::DISPLAY_FIELDLIST, ['featured']); $items = Post::toArray(Post::selectForUser($uid, $fields, $condition, $params)); - $o .= DI::conversation()->render($items, ConversationContent::MODE_CONTACT_POSTS); + $o = DI::conversation()->render($items, ConversationContent::MODE_CONTACT_POSTS, isset($request['mode']) && ($request['mode'] == 'raw')); if (DI::pConfig()->get($uid, 'system', 'infinite_scroll', true)) { - $o .= HTML::scrollLoader(); + $o .= HTML::scrollLoader($request); } else { $o .= $pager->renderMinimal(count($items)); } @@ -1713,13 +1705,14 @@ class Contact /** * Returns threads from a given contact id * - * @param int $cid Contact ID - * @param int $update Update mode - * @param int $parent Item parent ID for the update mode + * @param int $cid Contact ID + * @param int $update Update mode + * @param int $parent Item parent ID for the update mode + * @param array $request Request variables * @return string posts in HTML * @throws Exception */ - public static function getThreadsFromId(int $cid, int $uid, int $update = 0, int $parent = 0, string $last_created = ''): string + public static function getThreadsFromId(int $cid, int $uid, int $update = 0, int $parent = 0, array $request = []): string { $contact = DBA::selectFirst('contact', ['contact-type', 'network', 'name', 'nick'], ['id' => $cid]); if (!DBA::isResult($contact)) { @@ -1738,8 +1731,8 @@ class Contact if (!empty($parent)) { $condition = DBA::mergeConditions($condition, ['parent' => $parent]); - } elseif (!empty($last_created)) { - $condition = DBA::mergeConditions($condition, ["`created` < ?", $last_created]); + } elseif (isset($request['last_created'])) { + $condition = DBA::mergeConditions($condition, ["`created` < ?", $request['last_created']]); } $contact_field = ((($contact["contact-type"] == self::TYPE_COMMUNITY) || ($contact['network'] == Protocol::MAIL)) ? 'owner-id' : 'author-id'); @@ -1752,13 +1745,6 @@ class Contact $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage); - if (!$update && DI::pConfig()->get($uid, 'system', 'infinite_scroll', true)) { - $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); - $o = Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]); - } else { - $o = ''; - } - $condition1 = DBA::mergeConditions($condition, ["`$contact_field` = ? AND `gravity` = ?", $cid, Item::GRAVITY_PARENT]); $condition2 = DBA::mergeConditions($condition, [ @@ -1776,17 +1762,19 @@ class Contact $union = array_merge($union, [$pager->getStart(), $pager->getItemsPerPage()]); $items = Post::toArray(DBA::p($sql, $union)); - if (empty($last_created) && ($pager->getStart() == 0)) { + $raw = isset($request['mode']) && ($request['mode'] == 'raw'); + + if (!$raw && !$update && ($pager->getStart() == 0)) { $fields = ['uri-id', 'thr-parent-id', 'gravity', 'author-id', 'created']; $pinned = Post\Collection::selectToArrayForContact($cid, Post\Collection::FEATURED, $fields); $items = array_merge($items, $pinned); } - $o .= DI::conversation()->render($items, ConversationContent::MODE_CONTACTS, $update, false, 'pinned_created', $uid); + $o = DI::conversation()->render($items, ConversationContent::MODE_CONTACTS, $update || $raw, false, 'pinned_created', $uid); if (!$update) { if (DI::pConfig()->get($uid, 'system', 'infinite_scroll', true)) { - $o .= HTML::scrollLoader(); + $o .= HTML::scrollLoader($request); } else { $o .= $pager->renderMinimal(count($items)); } diff --git a/src/Module/Contact/Conversations.php b/src/Module/Contact/Conversations.php index 4d10935303..66ea8dc0a8 100644 --- a/src/Module/Contact/Conversations.php +++ b/src/Module/Contact/Conversations.php @@ -88,19 +88,23 @@ class Conversations extends BaseModule $this->baseUrl->redirect('profile/' . $contact['nick']); } - // Load necessary libraries for the status editor - $this->page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js')); - $this->page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js')); - $this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css')); - $this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css')); + $raw = isset($request['mode']) && ($request['mode'] == 'raw'); - $this->page['aside'] .= VCard::getHTML($contact, true); + if (!$raw) { + // Load necessary libraries for the status editor + $this->page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js')); + $this->page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js')); + $this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css')); + $this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css')); + + $this->page['aside'] .= VCard::getHTML($contact, true); + } Nav::setSelected('contacts'); $output = ''; - if (!$contact['ap-posting-restricted']) { + if (!$contact['ap-posting-restricted'] && !$raw) { $options = [ 'lockstate' => ACL::getLockstateForUserId($this->userSession->getLocalUserId()) ? 'lock' : 'unlock', 'acl' => ACL::getFullSelectorHTML($this->page, $this->userSession->getLocalUserId(), true, []), @@ -111,8 +115,10 @@ class Conversations extends BaseModule } Contact::setPageTitle($contact); - $output .= Contact::getTabsHTML($contact, Contact::TAB_CONVERSATIONS); - $output .= ModelContact::getThreadsFromId($contact['id'], $this->userSession->getLocalUserId(), 0, 0, $request['last_created'] ?? ''); + if (!$raw) { + $output .= Contact::getTabsHTML($contact, Contact::TAB_CONVERSATIONS); + } + $output .= ModelContact::getThreadsFromId($contact['id'], $this->userSession->getLocalUserId(), 0, 0, $request); return $output; } diff --git a/src/Module/Contact/Follow.php b/src/Module/Contact/Follow.php index 68c556aa52..ce8162b9e7 100644 --- a/src/Module/Contact/Follow.php +++ b/src/Module/Contact/Follow.php @@ -179,7 +179,7 @@ class Follow extends BaseModule ); // Show last public posts - $output .= Contact::getPostsFromUrl($contact['url'], $this->session->getLocalUserId()); + $output .= Contact::getPostsFromUrl($contact['url'], $this->session->getLocalUserId(), false, $request); } return $output; diff --git a/src/Module/Contact/Media.php b/src/Module/Contact/Media.php index 892e4162c0..a054186cdd 100644 --- a/src/Module/Contact/Media.php +++ b/src/Module/Contact/Media.php @@ -53,7 +53,7 @@ class Media extends BaseModule $o = Contact::getTabsHTML($contact, Contact::TAB_MEDIA); - $o .= ModelContact::getPostsFromUrl($contact['url'], $this->userSession->getLocalUserId(), true, $request['last_created'] ?? ''); + $o .= ModelContact::getPostsFromUrl($contact['url'], $this->userSession->getLocalUserId(), true, $request); return $o; } diff --git a/src/Module/Contact/Posts.php b/src/Module/Contact/Posts.php index 2445b3b930..1a506606c5 100644 --- a/src/Module/Contact/Posts.php +++ b/src/Module/Contact/Posts.php @@ -87,7 +87,7 @@ class Posts extends BaseModule $o = Contact::getTabsHTML($contact, Contact::TAB_POSTS); - $o .= Model\Contact::getPostsFromId($contact['id'], $this->userSession->getLocalUserId(), false, $request['last_created'] ?? ''); + $o .= Model\Contact::getPostsFromId($contact['id'], $this->userSession->getLocalUserId(), false, $request); return $o; } diff --git a/src/Module/Contact/Unfollow.php b/src/Module/Contact/Unfollow.php index 6e0283cb3f..94efacc2f9 100644 --- a/src/Module/Contact/Unfollow.php +++ b/src/Module/Contact/Unfollow.php @@ -124,7 +124,7 @@ class Unfollow extends \Friendica\BaseModule $o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), ['$title' => $this->t('Posts and Replies')]); // Show last public posts - $o .= Contact::getPostsFromUrl($contact['url'], $this->userSession->getLocalUserId()); + $o .= Contact::getPostsFromUrl($contact['url'], $this->userSession->getLocalUserId(), false, $request); return $o; } diff --git a/src/Module/Conversation/Channel.php b/src/Module/Conversation/Channel.php index b8d8ba4ff0..d18c22d4a8 100644 --- a/src/Module/Conversation/Channel.php +++ b/src/Module/Conversation/Channel.php @@ -84,11 +84,6 @@ class Channel extends Timeline '$header' => '', ]); - if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'infinite_scroll', true)) { - $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); - $o .= Renderer::replaceMacros($tpl, ['$reload_uri' => $this->args->getQueryString()]); - } - if (!$this->raw) { $tabs = $this->getTabArray($this->channel->getTimelines($this->session->getLocalUserId()), 'channel'); $tabs = array_merge($tabs, $this->getTabArray($this->channelRepository->selectByUid($this->session->getLocalUserId()), 'channel')); @@ -126,7 +121,7 @@ class Channel extends Timeline return $o; } - $o .= $this->conversation->render($items, Conversation::MODE_CHANNEL, false, false, $order, $this->session->getLocalUserId()); + $o .= $this->conversation->render($items, Conversation::MODE_CHANNEL, $this->raw, false, $order, $this->session->getLocalUserId()); $pager = new BoundariesPager( $this->l10n, @@ -137,7 +132,7 @@ class Channel extends Timeline ); if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'infinite_scroll', true)) { - $o .= HTML::scrollLoader(); + $o .= HTML::scrollLoader($request); } else { $o .= $pager->renderMinimal(count($items)); } diff --git a/src/Module/Conversation/Community.php b/src/Module/Conversation/Community.php index 8787268e9f..40e680d42f 100644 --- a/src/Module/Conversation/Community.php +++ b/src/Module/Conversation/Community.php @@ -80,11 +80,6 @@ class Community extends Timeline '$global_community_hint' => $this->l10n->t("This community stream shows all public posts received by this node. They may not reflect the opinions of this node’s users.") ]); - if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'infinite_scroll', true)) { - $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); - $o .= Renderer::replaceMacros($tpl, ['$reload_uri' => $this->args->getQueryString()]); - } - if (!$this->raw) { $tabs = $this->getTabArray($this->community->getTimelines($this->session->isAuthenticated()), 'community'); $tab_tpl = Renderer::getMarkupTemplate('common_tabs.tpl'); @@ -117,7 +112,7 @@ class Community extends Timeline return $o; } - $o .= $this->conversation->render($items, Conversation::MODE_COMMUNITY, false, false, 'received', $this->session->getLocalUserId()); + $o .= $this->conversation->render($items, Conversation::MODE_COMMUNITY, $this->raw, false, 'received', $this->session->getLocalUserId()); $pager = new BoundariesPager( $this->l10n, @@ -128,7 +123,7 @@ class Community extends Timeline ); if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'infinite_scroll', true)) { - $o .= HTML::scrollLoader(); + $o .= HTML::scrollLoader($request); } else { $o .= $pager->renderMinimal(count($items)); } diff --git a/src/Module/Conversation/Network.php b/src/Module/Conversation/Network.php index aa832bd8b3..f5f32237bd 100644 --- a/src/Module/Conversation/Network.php +++ b/src/Module/Conversation/Network.php @@ -224,11 +224,6 @@ class Network extends Timeline } } - if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'infinite_scroll', true) && ($_GET['mode'] ?? '') != 'minimal') { - $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); - $o .= Renderer::replaceMacros($tpl, ['$reload_uri' => $this->args->getQueryString()]); - } - if (!$this->raw) { $o .= $this->getTabsHTML(); @@ -294,7 +289,7 @@ class Network extends Timeline $items = $this->getItems(); } - $o .= $this->conversation->render($items, Conversation::MODE_NETWORK, false, false, $this->getOrder(), $this->session->getLocalUserId()); + $o .= $this->conversation->render($items, Conversation::MODE_NETWORK, $this->raw, false, $this->getOrder(), $this->session->getLocalUserId()); } catch (\Exception $e) { $this->logger->error('Exception when fetching items', ['code' => $e->getCode(), 'message' => $e->getMessage()]); $o .= $this->l10n->t('Error %d (%s) while fetching the timeline.', $e->getCode(), $e->getMessage()); @@ -302,7 +297,7 @@ class Network extends Timeline } if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'infinite_scroll', true)) { - $o .= HTML::scrollLoader(); + $o .= HTML::scrollLoader($request); } else { $pager = new BoundariesPager( $this->l10n, diff --git a/src/Module/Profile/Media.php b/src/Module/Profile/Media.php index 489d93eeaa..78c97c0bec 100644 --- a/src/Module/Profile/Media.php +++ b/src/Module/Profile/Media.php @@ -66,7 +66,7 @@ class Media extends BaseProfile $o = self::getTabsHTML('media', $is_owner, $profile['nickname'], $profile['hide-friends']); - $o .= Contact::getPostsFromUrl($profile['url'], $this->userSession->getLocalUserId(), true, $request['last_created'] ?? ''); + $o .= Contact::getPostsFromUrl($profile['url'], $this->userSession->getLocalUserId(), true, $request); return $o; } diff --git a/src/Module/Search/Filed.php b/src/Module/Search/Filed.php index b90dd7d923..17884ede64 100644 --- a/src/Module/Search/Filed.php +++ b/src/Module/Search/Filed.php @@ -31,13 +31,6 @@ class Filed extends BaseSearch DI::page()['aside'] .= Widget::fileAs(DI::args()->getCommand(), $_GET['file'] ?? ''); - if (DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'infinite_scroll', true) && ($_GET['mode'] ?? '') != 'minimal') { - $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); - $o = Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]); - } else { - $o = ''; - } - $file = $_GET['file'] ?? ''; // Rawmode is used for fetching new content at the end of the page @@ -93,10 +86,10 @@ class Filed extends BaseSearch $items = Post::toArray(Post::selectForUser(DI::userSession()->getLocalUserId(), Item::DISPLAY_FIELDLIST, $item_condition, $item_params)); - $o .= DI::conversation()->render($items, Conversation::MODE_FILED, false, false, '', DI::userSession()->getLocalUserId()); + $o = DI::conversation()->render($items, Conversation::MODE_FILED, false, false, '', DI::userSession()->getLocalUserId()); if (DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'infinite_scroll', true)) { - $o .= HTML::scrollLoader(); + $o .= HTML::scrollLoader($request); } else { $o .= $pager->renderMinimal($count); } diff --git a/src/Module/Search/Index.php b/src/Module/Search/Index.php index 7fcb6a6b94..49d0609277 100644 --- a/src/Module/Search/Index.php +++ b/src/Module/Search/Index.php @@ -195,11 +195,6 @@ class Index extends BaseSearch return $o; } - if (DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'infinite_scroll', true)) { - $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl'); - $o .= Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]); - } - if ($tag) { $title = DI::l10n()->t('Items tagged with: %s', $search); } else { @@ -215,7 +210,7 @@ class Index extends BaseSearch $o .= DI::conversation()->render($items, Conversation::MODE_SEARCH, false, false, 'commented', DI::userSession()->getLocalUserId()); if (DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'infinite_scroll', true)) { - $o .= HTML::scrollLoader(); + $o .= HTML::scrollLoader($request); } else { $o .= $pager->renderMinimal($count); } diff --git a/src/Module/Update/Contact.php b/src/Module/Update/Contact.php index 44de5c2d73..0fbf603177 100644 --- a/src/Module/Update/Contact.php +++ b/src/Module/Update/Contact.php @@ -77,6 +77,6 @@ final class Contact extends ContactModule throw new NotFoundException(); } - System::htmlUpdateExit(ModelContact::getThreadsFromId($pcid, $this->userSession->getLocalUserId(), true, $item['parent'] ?? 0, $request['last_received'] ?? '')); + System::htmlUpdateExit(ModelContact::getThreadsFromId($pcid, $this->userSession->getLocalUserId(), true, $item['parent'] ?? 0, $request)); } }