From eb749a2a1682ee0dc739914a9662d03d78345a45 Mon Sep 17 00:00:00 2001
From: David Sehnal <dsehnal@users.noreply.github.com>
Date: Tue, 2 May 2023 16:12:38 +0200
Subject: [PATCH] State.updateNode fix for Null parents (#807)

---
 CHANGELOG.md           |  1 +
 src/mol-state/state.ts | 29 ++++++++++++++++++++++++++---
 2 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 158c32811..ecfc32d68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ Note that since we don't clearly distinguish between a public and private interf
 - Add a uniform color theme for NtC tube that still paints residue and segment dividers in a different color
 - Fix bond assignments `struct_conn` records referencing waters
 - Fix `PluginState.setSnapshot` triggering unnecessary state updates
+- Fix an edge case in the `mol-state`'s `State` when trying to apply a transform to an existing Null object
 - Add `SbNcbrPartialCharges` extension for coloring and labeling atoms and residues by partial atomic charges
   - uses custom mmcif categories `_sb_ncbr_partial_atomic_charges_meta` and `_sb_ncbr_partial_atomic_charges` (more info in [README.md](./src/extensions/sb-ncbr/README.md))
 
diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts
index 1602278c8..83ed3fcbb 100644
--- a/src/mol-state/state.ts
+++ b/src/mol-state/state.ts
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  */
@@ -868,13 +868,36 @@ async function updateNode(ctx: UpdateContext, currentRef: Ref): Promise<UpdateNo
     const current = ctx.cells.get(currentRef)!;
     const transform = current.transform;
 
-    // special case for Root
+    // Special case for Root
     if (current.transform.ref === StateTransform.RootRef) {
         return { action: 'none' };
     }
 
+    const treeParent = ctx.cells.get(current.transform.parent);
+    const isParentNull = treeParent?.obj === StateObject.Null;
+
+    // Special case for when the immediate parent is null
+    // This could happen then manually applying transforms to
+    // already existing null nudes
+    if (isParentNull) {
+        current.sourceRef = treeParent.transform.ref;
+
+        if (oldTree.transforms.has(currentRef) && current.params) {
+            const oldParams = current.params.values;
+            const oldCache = current.cache;
+            dispose(transform, current.obj, oldParams, oldCache, ctx.parent.globalContext);
+
+            current.params = undefined;
+            current.obj = StateObject.Null;
+            return { ref: currentRef, action: 'updated', obj: current.obj! };
+        } else {
+            current.params = undefined;
+            return { ref: currentRef, action: 'created', obj: StateObject.Null };
+        }
+    }
+
     const parentCell = transform.transformer.definition.from.length === 0
-        ? ctx.cells.get(current.transform.parent)
+        ? treeParent
         : StateSelection.findAncestorOfType(tree, ctx.cells, currentRef, transform.transformer.definition.from);
     if (!parentCell) {
         throw new Error(`No suitable parent found for '${currentRef}'`);
-- 
GitLab