mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2026-10-16 02:06:52 +02:00
Fix duplicated posts in Bluesky threads and bogus list position in DMs
Thread buffers rebuilt themselves from scratch on every refresh with no deduplication and no guard against overlapping runs, and the list control was mutated from the update timer's background thread. Threads opened from the same account also shared a single session.db entry, because the buffer name was derived from the author instead of the post. - Name conversation buffers after the post URI, and navigate to an already open thread instead of opening a second buffer for it. - Deduplicate thread items by URI, and refuse a refresh while one is in flight. - Move the clear-and-fill step onto the GUI thread via wx.CallAfter. - Stop start_buffers() from starting a buffer that create_buffer() already started, since needs_init was never cleared. The direct messages list never focused an item after being rebuilt, so its position was announced as "0 of 1", which cannot exist. The conversation list now focuses an item whenever it cannot restore the previous selection, and the buffer navigation announcements no longer report a position while nothing is focused. Verified against the live Bluesky API that app.bsky.feed.getPostThread itself returns no repeated posts, so the duplication was entirely on our side. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
77291d7c3d
commit
0e5a75048a
@@ -448,6 +448,9 @@ class Controller(object):
|
||||
except ValueError:
|
||||
commonMessageDialogs.unauthorized()
|
||||
return
|
||||
# This buffer has already retrieved its items, so start_buffers()
|
||||
# must not start it a second time.
|
||||
buffer.needs_init = False
|
||||
self.buffers.append(buffer)
|
||||
if parent_tab == None:
|
||||
log.debug("Appending buffer {}...".format(buffer,))
|
||||
@@ -1090,6 +1093,31 @@ class Controller(object):
|
||||
# except:
|
||||
# pass
|
||||
|
||||
def announce_buffer_position(self, buffer, with_account=False):
|
||||
""" Speaks the current buffer name plus the position within its list.
|
||||
|
||||
If the list holds items but none of them is focused yet, this focuses one
|
||||
before speaking. Otherwise get_selected() returns -1 and we would announce
|
||||
a position of "0 of n", which does not exist."""
|
||||
try:
|
||||
count = buffer.buffer.list.get_count()
|
||||
if count == 0:
|
||||
raise ValueError("Empty buffer")
|
||||
selected = buffer.buffer.list.get_selected()
|
||||
if selected < 0:
|
||||
reverse = False
|
||||
if getattr(buffer, "session", None) != None:
|
||||
reverse = buffer.session.settings["general"].get("reverse_timelines", False)
|
||||
selected = 0 if reverse else count-1
|
||||
buffer.buffer.list.select_item(selected)
|
||||
if with_account == True:
|
||||
msg = _(u"%s. %s, %s of %s") % (buffer.account, self.view.get_buffer_text(), selected+1, count)
|
||||
else:
|
||||
msg = _(u"%s, %s of %s") % (self.view.get_buffer_text(), selected+1, count)
|
||||
except:
|
||||
msg = _(u"%s. Empty") % (self.view.get_buffer_text(),)
|
||||
output.speak(msg, True)
|
||||
|
||||
def left(self, *args, **kwargs):
|
||||
buff = self.view.get_current_buffer_pos()
|
||||
buffer = self.get_current_buffer()
|
||||
@@ -1103,11 +1131,7 @@ class Controller(object):
|
||||
while self.get_current_buffer().invisible == False: self.skip_buffer(False)
|
||||
buffer = self.get_current_buffer()
|
||||
if self.showing == True: buffer.buffer.set_focus_in_list()
|
||||
try:
|
||||
msg = _(u"%s, %s of %s") % (self.view.get_buffer_text(), buffer.buffer.list.get_selected()+1, buffer.buffer.list.get_count())
|
||||
except:
|
||||
msg = _(u"%s. Empty") % (self.view.get_buffer_text(),)
|
||||
output.speak(msg, True)
|
||||
self.announce_buffer_position(buffer)
|
||||
|
||||
def right(self, *args, **kwargs):
|
||||
buff = self.view.get_current_buffer_pos()
|
||||
@@ -1122,11 +1146,7 @@ class Controller(object):
|
||||
while self.get_current_buffer().invisible == False: self.skip_buffer(True)
|
||||
buffer = self.get_current_buffer()
|
||||
if self.showing == True: buffer.buffer.set_focus_in_list()
|
||||
try:
|
||||
msg = _(u"%s, %s of %s") % (self.view.get_buffer_text(), buffer.buffer.list.get_selected()+1, buffer.buffer.list.get_count())
|
||||
except:
|
||||
msg = _(u"%s. Empty") % (self.view.get_buffer_text(),)
|
||||
output.speak(msg, True)
|
||||
self.announce_buffer_position(buffer)
|
||||
|
||||
def next_account(self, *args, **kwargs):
|
||||
if not self.accounts:
|
||||
@@ -1153,11 +1173,7 @@ class Controller(object):
|
||||
self.view.change_buffer(buff)
|
||||
buffer = self.get_current_buffer()
|
||||
if self.showing == True: buffer.buffer.set_focus_in_list()
|
||||
try:
|
||||
msg = _(u"%s. %s, %s of %s") % (buffer.account, self.view.get_buffer_text(), buffer.buffer.list.get_selected()+1, buffer.buffer.list.get_count())
|
||||
except:
|
||||
msg = _(u"%s. Empty") % (self.view.get_buffer_text(),)
|
||||
output.speak(msg, True)
|
||||
self.announce_buffer_position(buffer, with_account=True)
|
||||
|
||||
def previous_account(self, *args, **kwargs):
|
||||
if not self.accounts:
|
||||
@@ -1184,11 +1200,7 @@ class Controller(object):
|
||||
self.view.change_buffer(buff)
|
||||
buffer = self.get_current_buffer()
|
||||
if self.showing == True: buffer.buffer.set_focus_in_list()
|
||||
try:
|
||||
msg = _(u"%s. %s, %s of %s") % (buffer.account, self.view.get_buffer_text(), buffer.buffer.list.get_selected()+1, buffer.buffer.list.get_count())
|
||||
except:
|
||||
msg = _(u"%s. Empty") % (self.view.get_buffer_text(),)
|
||||
output.speak(msg, True)
|
||||
self.announce_buffer_position(buffer, with_account=True)
|
||||
|
||||
def go_home(self):
|
||||
buffer = self.get_current_buffer()
|
||||
|
||||
Reference in New Issue
Block a user