longform icon indicating copy to clipboard operation
longform copied to clipboard

Scenes disappear from list when reordering with drag/drop

Open p2edwards opened this issue 1 year ago • 12 comments

I'm using 2.0.0.

Occasionally when I rearrange scenes in the scene list, scenes disappear. Reload app without saving causes Longform to find them again, but it loses their ordering / location in the index tree.

I'm attempting to reproduce this to gather more detail, but it's a bit of a Heisenbug for me. I can update the description or comment here if or when I encounter it next -- just wanted to flag it now in case it was introduced in a recent commit.

p2edwards avatar Aug 16 '22 20:08 p2edwards

When did you install the beta? I recently fixed this exact behavior in #91. If you reinstall it, does this still occur?

kevboh avatar Aug 16 '22 21:08 kevboh

Interesting! I installed last night, I'll check again. I'm using BRAT for the first time (and I'm not too sure what I'm doing), so that might be a factor.

p2edwards avatar Aug 16 '22 21:08 p2edwards

Hmm, that should be recent enough...

What platform and Obsidian version number are you seeing this on? Is there any way you could make an anonymized version of the buggy project to share here?

kevboh avatar Aug 17 '22 01:08 kevboh

I ran the update command on BRAT again after my last comment, and haven't seen the problem since. I guess that's good? Though it's mysterious. I'll keep using it and see if it comes up again. Happy to share an anonymized version if it does.

  • Obsidian 0.15.9, macOS 11.6.8 (intel)

p2edwards avatar Aug 17 '22 03:08 p2edwards

Strange. Let me know! Here's how the other bug looked, if it helps:

  1. Move a scene. Looks good.
  2. Move another scene. The previously-moved scene disappears.
  3. Repeat.

I thought I had guarded against this with the fix, but it's possible some other issue crept in. Keep me posted, and thanks for trying it!

kevboh avatar Aug 17 '22 14:08 kevboh

Strange. Let me know! Here's how the other bug looked, if it helps:

  1. Move a scene. Looks good.
  2. Move another scene. The previously-moved scene disappears.
  3. Repeat.

I thought I had guarded against this with the fix, but it's possible some other issue crept in. Keep me posted, and thanks for trying it!

for me, scenes disappear immediately after being dragged on mobile, and will not reappear in the index until i reload the app. so basically,

  1. move scene
  2. scene disappears
  3. repeat until there's no scenes left

GamerGirlandCo avatar Aug 17 '22 16:08 GamerGirlandCo

@GamerGirlandCo do you have the latest beta? You'll need to uninstall and reinstall in BRAT, as I'm not updating version numbers. If you haven't reinstalled in the past few days you're likely seeing the fixed issue I linked. If not let me know.

kevboh avatar Aug 17 '22 17:08 kevboh

@GamerGirlandCo do you have the latest beta? You'll need to uninstall and reinstall in BRAT, as I'm not updating version numbers. If you haven't reinstalled in the past few days you're likely seeing the fixed issue I linked. If not let me know.

aha, that fixed it! :D thank you!!

GamerGirlandCo avatar Aug 17 '22 18:08 GamerGirlandCo

Ok, I'm seeing the issue again. Often, scenes are disappearing from "folded" lists in the outline. In fact, I can pretty reliably disappear scenes using either of these methods:

  1. Nest some scenes under a parent, then "fold" the parent. Move an unrelated scene with drag and drop. This causes all the "nested" scenes to disappear.
  2. Nest at least one scene under a parent, then "fold" the parent. Move any scene under the parent, so that it would be "nested" if released. The held scene disappears. Now unfold the parent:
    1. If there was 1 scene nested before, it will remain, and the one you were moving has disappeared.
    2. If there were 2 or more nested before, they're gone and the one you were holding is there.

p2edwards avatar Aug 22 '22 06:08 p2edwards

I found a fix for this! Edit: Wrote about it below.

p2edwards avatar Aug 22 '22 09:08 p2edwards

Scenes under collapsed scenes get lost:

When Sortable.js serializes with toArray(), it does so by reading back the items from the DOM to determine order.

Items that are hidden (because they're indented and their parent was collapsed) are being skipped for rendering entirely, so they don't show up in the DOM. The next time serialization happens, anything that's missing from the tree gets dropped from the store.

One way to solve it:

Keep hidden scene items in the DOM, but style them with "display: none". SortableList still sees them and can maintain a stable sort, even when things are collapsed.

Simplified diff:

diff --git a/src/view/explorer/SceneList.svelte b/src/view/explorer/SceneList.svelte
index 4000ef8..b54bda5 100644
--- a/src/view/explorer/SceneList.svelte
+++ b/src/view/explorer/SceneList.svelte
@@ -61,12 +61,15 @@
     let ignoringUntilIndent = Infinity;
 
     scenes.forEach(({ title, indent, numbering }, index) => {
-      if (indent <= ignoringUntilIndent) {
-        ignoringUntilIndent = Infinity;
+      if (true) {
+        let hidden = indent > ignoringUntilIndent;
+        if (!hidden) {
+          ignoringUntilIndent = Infinity;
+        }
 
         const collapsed = _collapsedItems.contains(title);
         if (collapsed) {
-          ignoringUntilIndent = indent;
+          ignoringUntilIndent = Math.min(ignoringUntilIndent, indent);
         }
 
         const nextScene = index < scenes.length - 1 ? scenes[index + 1] : false;
@@ -76,6 +79,7 @@
           indent: indent,
           path: makeScenePath($selectedDraft as MultipleSceneDraft, title),
           collapsible: nextScene && nextScene.indent > indent,
+          hidden: hidden,
           numbering,
         };
         itemsToReturn.push(item);
@@ -315,7 +319,8 @@
     >
       <div
         class="scene-container"
-        style="margin-left: {item.indent * 32}px"
+        style="margin-left: {item.indent * 32}px; {item.hidden &&
+          'display: none;'}"
         class:selected={$activeFile && $activeFile.path === item.path}
         on:click={(e) =>
           typeof item.path === "string" ? onItemClick(item, e) : {}}

Notes:

  • I left the if (true) {} in because it makes the diff more readable.
  • the Math.min part becomes necessary for nested collapsed scenes, since we're walking through all of them now, not just the first one we see.

p2edwards avatar Aug 22 '22 10:08 p2edwards

Brilliant, thank you! I'll take a look at this this week, amazing work.

Funnily enough I saw the email subject this morning upon waking and realized immediately that I was relying on a DOM read... 😊

kevboh avatar Aug 22 '22 13:08 kevboh

by "this week" I guess I meant "two months from now," but such is life :)

kevboh avatar Oct 13 '22 14:10 kevboh