diff --git a/doc/changelog.md b/doc/changelog.md index 92745afa..4192a284 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -5,6 +5,7 @@ TWBlue Changelog * Core: * Expanded the keystroke editor actions list. Now, many previously hidden or unassignable actions are available to be mapped to custom keyboard shortcuts. * Mastodon: + * Fixed an issue where pinned posts could cause the retrieval of large amounts of old content during buffer updates or when loading more items. * Added support for sending quoted posts! You can now quote other users' posts from the context menu or the new Boost dialog. ([#860](https://github.com/mcv-software/twblue/issues/860)) * Fixed an issue where HTML entities were not decoded when editing a post. ([#893](https://github.com/mcv-software/twblue/issues/893)) diff --git a/src/controller/buffers/mastodon/base.py b/src/controller/buffers/mastodon/base.py index 85ac3bf8..1ca21ac1 100644 --- a/src/controller/buffers/mastodon/base.py +++ b/src/controller/buffers/mastodon/base.py @@ -160,7 +160,7 @@ class BaseBuffer(base.Buffer): self.username = self.session.db[self.name][0]["account"].username pub.sendMessage("core.change_buffer_title", name=self.session.get_name(), buffer=self.name, title=_("Timeline for {}").format(self.username)) self.finished_timeline = True - self.put_items_on_list(number_of_items) + self.put_items_on_list(number_of_items, results) if number_of_items > 0 and self.name != "sent_posts" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True: self.session.sound.play(self.sound) # Autoread settings @@ -247,7 +247,7 @@ class BaseBuffer(base.Buffer): output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True) return False - def put_items_on_list(self, number_of_items): + def put_items_on_list(self, number_of_items, new_items=None): list_to_use = self.session.db[self.name] if number_of_items == 0 and self.session.settings["general"]["persist_size"] == 0: return log.debug("The list contains %d items " % (self.buffer.list.get_count(),)) @@ -261,7 +261,10 @@ class BaseBuffer(base.Buffer): self.buffer.list.insert_item(False, *post) self.buffer.set_position(self.session.settings["general"]["reverse_timelines"]) elif self.buffer.list.get_count() > 0 and number_of_items > 0: - if self.session.settings["general"]["reverse_timelines"] == False: + if new_items: + for item in new_items: + self.add_new_item(item) + elif self.session.settings["general"]["reverse_timelines"] == False: items = list_to_use[len(list_to_use)-number_of_items:] for i in items: post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) @@ -279,10 +282,18 @@ class BaseBuffer(base.Buffer): if self.session.settings["general"]["read_preferences_from_instance"]: safe = self.session.expand_spoilers == False post = self.compose_function(item, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) - if self.session.settings["general"]["reverse_timelines"] == False: - self.buffer.list.insert_item(False, *post) - else: - self.buffer.list.insert_item(True, *post) + + try: + # We trust the DB to have the correct order (handled by order_buffer) + index = self.session.db[self.name].index(item) + self.buffer.list.insert_item(index, *post) + except ValueError: + # Fallback if somehow item isn't in DB yet (race condition?), default to simple append/prepend based on settings + if self.session.settings["general"]["reverse_timelines"] == False: + self.buffer.list.insert_item(False, *post) + else: + self.buffer.list.insert_item(True, *post) + if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False: output.speak(" ".join(post[:2]), speech=self.session.settings["reporting"]["speech_reporting"], braille=self.session.settings["reporting"]["braille_reporting"]) diff --git a/src/sessions/mastodon/session.py b/src/sessions/mastodon/session.py index 8a205862..ab0dd1b2 100644 --- a/src/sessions/mastodon/session.py +++ b/src/sessions/mastodon/session.py @@ -156,10 +156,12 @@ class Session(base.baseSession): self.db[name] = [] objects = self.db[name] if ignore_older and len(self.db[name]) > 0: + # We use the newest chronological ID to avoid ignoring new items + # if pinned posts are at the start/end of the list. if self.settings["general"]["reverse_timelines"] == False: - last_id = self.db[name][0].id + last_id = max(item.id for item in self.db[name]) else: - last_id = self.db[name][-1].id + last_id = max(item.id for item in self.db[name]) for i in data: # handle empty notifications. post_types = ["status", "mention", "reblog", "favourite", "update", "poll"] @@ -173,8 +175,37 @@ class Session(base.baseSession): filter_status = utils.evaluate_filters(post=i, current_context=utils.get_current_context(name)) if filter_status == "hide": continue - if self.settings["general"]["reverse_timelines"] == False: objects.append(i) - else: objects.insert(0, i) + + is_pinned = getattr(i, "pinned", False) or (isinstance(i, dict) and i.get("pinned", False)) + + if self.settings["general"]["reverse_timelines"] == False: + # Standard (Old -> New). Pinned items are at the end. + if is_pinned: + objects.append(i) + else: + # Insert before pinned items + insert_idx = len(objects) + for obj in reversed(objects): + obj_pinned = getattr(obj, "pinned", False) or (isinstance(obj, dict) and obj.get("pinned", False)) + if obj_pinned: + insert_idx -= 1 + else: + break + objects.insert(insert_idx, i) + else: + # Reverse (New -> Old). Pinned items are at the start. + if is_pinned: + objects.insert(0, i) + else: + # Insert after pinned items + insert_idx = 0 + for obj in objects: + obj_pinned = getattr(obj, "pinned", False) or (isinstance(obj, dict) and obj.get("pinned", False)) + if obj_pinned: + insert_idx += 1 + else: + break + objects.insert(insert_idx, i) num = num+1 self.db[name] = objects return num