refactor: replace global editing lock with active_edits dictionary to support concurrent file editing
This commit is contained in:
@@ -260,7 +260,7 @@ class SharePointApp(wx.Frame):
|
||||
self.tree_item_data = {} # Mappenoder -> {type, id, name, drive_id, path}
|
||||
self.tree_root = None
|
||||
self.is_navigating_back = False
|
||||
self.is_editing = False # Låse-state ved filredigering
|
||||
self.active_edits = {} # item_id -> { "name": name, "event": Event, "waiting": bool }
|
||||
|
||||
# System Ikoner (ArtProvider - mest basale for kompatibilitet)
|
||||
self.image_list = wx.ImageList(16, 16)
|
||||
@@ -270,7 +270,6 @@ class SharePointApp(wx.Frame):
|
||||
self.idx_file = self.image_list.Add(wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, (16, 16))) # File
|
||||
|
||||
# Threading/Sync til filredigering
|
||||
self.edit_wait_event = threading.Event()
|
||||
|
||||
# MSAL Cache
|
||||
self.msal_app = msal.PublicClientApplication(CLIENT_ID, authority=AUTHORITY)
|
||||
@@ -440,7 +439,7 @@ class SharePointApp(wx.Frame):
|
||||
|
||||
if item['type'] == "FILE":
|
||||
edit_item = menu.Append(wx.ID_ANY, self.get_txt("msg_edit_file"))
|
||||
self.Bind(wx.EVT_MENU, lambda e: threading.Thread(target=self.process_file, args=(item['id'], item['name']), daemon=True).start(), edit_item)
|
||||
self.Bind(wx.EVT_MENU, lambda e, i=item: self.open_file(i), edit_item)
|
||||
|
||||
if item['type'] in ["FILE", "FOLDER"]:
|
||||
rename_item = menu.Append(wx.ID_ANY, f"{self.get_txt('msg_rename')} '{item['name']}'")
|
||||
@@ -475,7 +474,6 @@ class SharePointApp(wx.Frame):
|
||||
menu.Destroy()
|
||||
|
||||
def on_tree_right_click(self, event):
|
||||
if self.is_editing: return
|
||||
item = event.GetItem()
|
||||
if not item.IsOk() or item == self.tree_root: return
|
||||
|
||||
@@ -697,10 +695,34 @@ class SharePointApp(wx.Frame):
|
||||
wx.CallAfter(_do)
|
||||
|
||||
def on_done_editing_clicked(self, event):
|
||||
print("[DEBUG] 'Jeg er færdig'-knap klikket.")
|
||||
self.set_status("Knap trykket - Uploader nu...")
|
||||
self.edit_wait_event.set()
|
||||
self.info_bar.Dismiss()
|
||||
waiting_files = [fid for fid, d in self.active_edits.items() if d.get("waiting")]
|
||||
if not waiting_files:
|
||||
return
|
||||
|
||||
if len(waiting_files) == 1:
|
||||
fid = waiting_files[0]
|
||||
self.active_edits[fid]["event"].set()
|
||||
else:
|
||||
# Show menu to let user pick which file is finished
|
||||
menu = wx.Menu()
|
||||
for fid in waiting_files:
|
||||
name = self.active_edits[fid]["name"]
|
||||
item = menu.Append(wx.ID_ANY, f"Gem '{name}'")
|
||||
# closure to capture fid
|
||||
def make_handler(f_id):
|
||||
return lambda e: self.active_edits[f_id]["event"].set()
|
||||
self.Bind(wx.EVT_MENU, make_handler(fid), item)
|
||||
|
||||
menu.AppendSeparator()
|
||||
item_all = menu.Append(wx.ID_ANY, "Gem alle")
|
||||
def handle_all(e):
|
||||
for f in waiting_files:
|
||||
if f in self.active_edits:
|
||||
self.active_edits[f]["event"].set()
|
||||
self.Bind(wx.EVT_MENU, handle_all, item_all)
|
||||
|
||||
self.PopupMenu(menu)
|
||||
menu.Destroy()
|
||||
|
||||
def on_language_changed(self, event):
|
||||
selection = self.lang_choice.GetSelection()
|
||||
@@ -743,7 +765,7 @@ class SharePointApp(wx.Frame):
|
||||
self._refresh_current_view() # Gendanner list-item tekster (Mappe/Fil)
|
||||
|
||||
def on_close_window(self, event):
|
||||
if self.is_editing:
|
||||
if self.active_edits:
|
||||
self.show_info(self.get_txt("msg_edit_warning"), wx.ICON_WARNING, auto_hide=False)
|
||||
return
|
||||
event.Skip()
|
||||
@@ -759,7 +781,6 @@ class SharePointApp(wx.Frame):
|
||||
wx.CallAfter(_do)
|
||||
|
||||
def on_refresh(self, event=None):
|
||||
if self.is_editing: return
|
||||
|
||||
selected = self.tree_ctrl.GetSelection()
|
||||
if not selected.IsOk() or selected == self.tree_root:
|
||||
@@ -1009,7 +1030,7 @@ class SharePointApp(wx.Frame):
|
||||
self.tree_ctrl.SelectItem(target_node)
|
||||
|
||||
def on_tree_selected(self, event):
|
||||
if not self or self.is_editing: return
|
||||
if not self: return
|
||||
item = event.GetItem()
|
||||
data = self.tree_item_data.get(item)
|
||||
if not data:
|
||||
@@ -1120,15 +1141,13 @@ class SharePointApp(wx.Frame):
|
||||
pass
|
||||
|
||||
def on_item_activated(self, event):
|
||||
if self.is_editing: return
|
||||
item_idx = event.GetIndex()
|
||||
item = self.current_items[item_idx]
|
||||
|
||||
if item['type'] in ["SITE", "DRIVE", "FOLDER"]:
|
||||
self._sync_tree_selection(item['id'])
|
||||
elif item['type'] == "FILE":
|
||||
self.current_drive_id = item['drive_id']
|
||||
threading.Thread(target=self.process_file, args=(item['id'], item['name']), daemon=True).start()
|
||||
self.open_file(item)
|
||||
|
||||
def _sync_tree_selection(self, target_id):
|
||||
selected = self.tree_ctrl.GetSelection()
|
||||
@@ -1151,7 +1170,6 @@ class SharePointApp(wx.Frame):
|
||||
child, cookie = self.tree_ctrl.GetNextChild(selected, cookie)
|
||||
|
||||
def go_back(self, event=None):
|
||||
if self.is_editing: return
|
||||
if len(self.history) > 1:
|
||||
self.history.pop() # Remove current
|
||||
prev_item = self.history[-1] # Peak at previous
|
||||
@@ -1159,15 +1177,58 @@ class SharePointApp(wx.Frame):
|
||||
self.tree_ctrl.SelectItem(prev_item)
|
||||
self.is_navigating_back = False
|
||||
|
||||
def process_file(self, item_id, file_name):
|
||||
def open_file(self, item):
|
||||
item_id = item['id']
|
||||
file_name = item['name']
|
||||
drive_id = item['drive_id']
|
||||
|
||||
if item_id in self.active_edits:
|
||||
self.show_info(f"'{file_name}' er allerede ved at blive redigeret.", wx.ICON_INFORMATION)
|
||||
return
|
||||
|
||||
if len(self.active_edits) >= 10:
|
||||
wx.MessageBox("Du kan kun have 10 filer åbne til redigering ad gangen.", "Maksimum grænse nået", wx.OK | wx.ICON_WARNING)
|
||||
return
|
||||
|
||||
threading.Thread(target=self.process_file, args=(item_id, file_name, drive_id), daemon=True).start()
|
||||
|
||||
def update_edit_ui(self):
|
||||
def _do():
|
||||
try:
|
||||
if not self: return
|
||||
count = len(self.active_edits)
|
||||
waiting_count = sum(1 for d in self.active_edits.values() if d.get("waiting"))
|
||||
|
||||
if waiting_count > 0:
|
||||
self.done_btn.SetLabel(f"{self.get_txt('btn_save_changes')} ({waiting_count})")
|
||||
self.done_btn.Show()
|
||||
else:
|
||||
self.done_btn.Hide()
|
||||
|
||||
# Opdater statusbesked hvis der er aktive opgaver
|
||||
if count > 0:
|
||||
status_msg = f"Aktive filer: {count}"
|
||||
if waiting_count > 0:
|
||||
status_msg += f" ({waiting_count} venter på gem)"
|
||||
self.set_status(status_msg)
|
||||
else:
|
||||
self.set_status(self.get_txt("status_ready"))
|
||||
|
||||
self.Layout()
|
||||
except RuntimeError:
|
||||
pass
|
||||
wx.CallAfter(_do)
|
||||
|
||||
def process_file(self, item_id, file_name, drive_id):
|
||||
if not self.ensure_valid_token(): return
|
||||
self.is_editing = True
|
||||
self.lock_ui(True)
|
||||
|
||||
edit_event = threading.Event()
|
||||
self.active_edits[item_id] = {"name": file_name, "event": edit_event, "waiting": False}
|
||||
self.update_edit_ui()
|
||||
|
||||
try:
|
||||
# 1. Lokation info
|
||||
site_id = self.current_site_id
|
||||
drive_id = self.current_drive_id
|
||||
base_url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives/{drive_id}/items/{item_id}"
|
||||
base_url = f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/{item_id}"
|
||||
|
||||
# Unik undermappe baseret på ID, men brug originalt filnavn indeni
|
||||
item_hash = hashlib.md5(item_id.encode()).hexdigest()[:8]
|
||||
@@ -1217,12 +1278,15 @@ class SharePointApp(wx.Frame):
|
||||
pass
|
||||
else:
|
||||
self.set_status(self.get_txt("msg_waiting_for_file", name=file_name))
|
||||
self.edit_wait_event.clear()
|
||||
wx.CallAfter(self.done_btn.Show)
|
||||
wx.CallAfter(self.Layout)
|
||||
self.edit_wait_event.wait()
|
||||
wx.CallAfter(self.done_btn.Hide)
|
||||
wx.CallAfter(self.Layout)
|
||||
edit_event.clear()
|
||||
self.active_edits[item_id]["waiting"] = True
|
||||
self.update_edit_ui()
|
||||
|
||||
edit_event.wait()
|
||||
|
||||
if item_id in self.active_edits:
|
||||
self.active_edits[item_id]["waiting"] = False
|
||||
self.update_edit_ui()
|
||||
|
||||
# 4. Tjek om noget er ændret
|
||||
new_hash = get_file_hash(local_path)
|
||||
@@ -1255,8 +1319,9 @@ class SharePointApp(wx.Frame):
|
||||
self.set_status(f"{self.get_txt('msg_error')}: {str(e)}")
|
||||
self.show_info(f"{self.get_txt('msg_error')}: {e}", wx.ICON_ERROR)
|
||||
finally:
|
||||
self.is_editing = False
|
||||
self.lock_ui(False)
|
||||
if item_id in self.active_edits:
|
||||
del self.active_edits[item_id]
|
||||
self.update_edit_ui()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = wx.App()
|
||||
|
||||
Reference in New Issue
Block a user