From 4fd69d4547eed4efb613c7b956bcb0f3287ae2bc Mon Sep 17 00:00:00 2001
From: Przemek Kaminski <pk@intrepidus.pl>
Date: Mon, 27 Jan 2020 18:52:01 +0100
Subject: [PATCH] [CodeEditor] moveUp/Down fix solved with key props

Also, changed codeRef, codeTypeRef to state.
---
 .psc-package/local/.set/packages.json       |   2 +-
 src/Gargantext/Components/CodeEditor.purs   |  39 +++++--
 src/Gargantext/Components/Nodes/Corpus.purs | 117 +++++++++++++-------
 3 files changed, 109 insertions(+), 49 deletions(-)

diff --git a/.psc-package/local/.set/packages.json b/.psc-package/local/.set/packages.json
index ad9dee34..3cddef09 100644
--- a/.psc-package/local/.set/packages.json
+++ b/.psc-package/local/.set/packages.json
@@ -3336,4 +3336,4 @@
     "repo": "https://github.com/paf31/purescript-yargs.git",
     "version": "v4.0.0"
   }
-}
+}
\ No newline at end of file
diff --git a/src/Gargantext/Components/CodeEditor.purs b/src/Gargantext/Components/CodeEditor.purs
index e2ae3f19..53acdf8d 100644
--- a/src/Gargantext/Components/CodeEditor.purs
+++ b/src/Gargantext/Components/CodeEditor.purs
@@ -8,8 +8,9 @@ import Data.Generic.Rep.Show (genericShow)
 import Data.Maybe (Maybe(..))
 import Data.Nullable (Nullable, null, toMaybe)
 import Data.String.Utils (endsWith)
-import Data.Tuple (fst)
+import Data.Tuple (fst, snd)
 import Data.Tuple.Nested ((/\))
+import DOM.Simple.Console (log2)
 import DOM.Simple.Types (Element)
 import Effect (Effect)
 import FFI.Simple ((.=), delay)
@@ -98,6 +99,20 @@ codeEditorCpt = R.hooksComponent "G.C.CE.CodeEditor" cpt
   where
     cpt {code, defaultCodeType, onChange} _ = do
       controls <- initControls code defaultCodeType
+      -- codeRef <- R.useRef code
+      -- defaultCodeTypeRef <- R.useRef defaultCodeType
+
+      -- R.useEffect2' code defaultCodeType $ do
+      --   if R.readRef codeRef == code && R.readRef defaultCodeTypeRef == defaultCodeType then
+      --     pure unit
+      --   else do
+      --     log2 "[codeEditorCpt] code" code
+      --     log2 "[codeEditorCpt] defaultCodeType" defaultCodeType
+      --     R.setRef codeRef code
+      --     R.setRef defaultCodeTypeRef defaultCodeType
+      --     reinitControls controls code defaultCodeType
+      --     setCodeOverlay controls code
+      --     renderHtml code controls
 
       -- Initial rendering of elements with given data
 
@@ -127,7 +142,7 @@ codeEditorCpt = R.hooksComponent "G.C.CE.CodeEditor" cpt
                           -- , contentEditable: "true"
                         , ref: controls.codeOverlayElRef
                         , rows: 30
-                          --, on: { input: onEditChange (fst codeType) codeElRef htmlRef editorCodeRef error }
+                          --, on: { input: onEditChange (fst codeType) codeElRef htmlRef codeRef error }
                         } []
                ]
              ]
@@ -158,11 +173,11 @@ codeEditorCpt = R.hooksComponent "G.C.CE.CodeEditor" cpt
     previewHidden _ = " hidden"
 
     onEditChange :: forall e. Record Controls -> (CodeType -> Code -> Effect Unit) -> e -> Effect Unit
-    onEditChange controls@{codeElRef, codeOverlayElRef, codeType: (codeType /\ _), editorCodeRef} onChange e = do
+    onEditChange controls@{codeElRef, codeOverlayElRef, codeType: (codeType /\ _), codeS} onChange e = do
       let code = R2.unsafeEventValue e
-      R.setRef editorCodeRef code
+      snd codeS $ const code
       setCodeOverlay controls code
-      renderHtml (R.readRef controls.editorCodeRef) controls
+      renderHtml (fst codeS) controls
       onChange codeType code
 
 setCodeOverlay :: Record Controls -> Code -> Effect Unit
@@ -218,7 +233,7 @@ toolbarCpt = R.hooksComponent "G.C.CE.toolbar" cpt
       renderHtml code controls
       onChange (fst controls.codeType) code
       where
-        code = R.readRef controls.editorCodeRef
+        code = fst controls.codeS
 
 
 type ErrorComponentProps =
@@ -310,9 +325,9 @@ viewTypeSelectorCpt = R.hooksComponent "G.C.CE.ViewTypeSelector" cpt
 type Controls =
   (
       codeElRef :: R.Ref (Nullable Element)
+    , codeS :: R.State Code
     , codeType :: R.State CodeType
     , codeOverlayElRef :: R.Ref (Nullable Element)
-    , editorCodeRef :: R.Ref Code
     , error :: R.State (Maybe Error)
     , htmlElRef :: R.Ref (Nullable Element)
     , viewType :: R.State ViewType
@@ -321,19 +336,25 @@ type Controls =
 initControls :: Code -> CodeType -> R.Hooks (Record Controls)
 initControls code defaultCodeType = do
   htmlElRef <- R.useRef null
+  codeS <- R.useState' code
   codeElRef <- R.useRef null
   codeOverlayElRef <- R.useRef null
   codeType <- R.useState' defaultCodeType
-  editorCodeRef <- R.useRef code
   error <- R.useState' Nothing
   viewType <- R.useState' Both
 
   pure $ {
       codeElRef
+    , codeS
     , codeType
     , codeOverlayElRef
-    , editorCodeRef
     , error
     , htmlElRef
     , viewType
     }
+
+reinitControls :: Record Controls -> Code -> CodeType -> Effect Unit
+reinitControls c@{codeType, codeS, error} code defaultCodeType = do
+  snd codeType $ const defaultCodeType
+  snd codeS $ const code
+  snd error $ const Nothing
diff --git a/src/Gargantext/Components/Nodes/Corpus.purs b/src/Gargantext/Components/Nodes/Corpus.purs
index ce4d271c..b87d7b8f 100644
--- a/src/Gargantext/Components/Nodes/Corpus.purs
+++ b/src/Gargantext/Components/Nodes/Corpus.purs
@@ -6,7 +6,7 @@ import Data.Array as A
 import Data.Either (Either(..))
 import Data.List as List
 import Data.Maybe (Maybe(..))
-import Data.Tuple (Tuple(..), fst)
+import Data.Tuple (Tuple(..), fst, snd)
 import Data.Tuple.Nested ((/\))
 import DOM.Simple.Console (log2)
 import Effect (Effect)
@@ -20,7 +20,7 @@ import Gargantext.Prelude
 
 import Gargantext.Components.CodeEditor as CE
 import Gargantext.Components.Node (NodePoly(..), HyperdataList)
-import Gargantext.Components.Nodes.Corpus.Types (CorpusData, FTField, Field(..), FieldType(..), Hash, Hyperdata(..), defaultField, defaultHaskell', defaultJSON', defaultMarkdown')
+import Gargantext.Components.Nodes.Corpus.Types (CorpusData, FTField, Field(..), FieldType(..), Hyperdata(..), defaultField, defaultHaskell', defaultJSON', defaultMarkdown')
 import Gargantext.Data.Array as GDA
 import Gargantext.Hooks.Loader (useLoader)
 import Gargantext.Routes (SessionRoute(NodeAPI, Children))
@@ -69,6 +69,15 @@ corpusLayoutViewCpt = R.hooksComponent "G.C.N.C.corpusLayoutView" cpt
     cpt {corpus: (NodePoly {hyperdata: Hyperdata {fields}}), nodeId, reload, session} _ = do
       let fieldsWithIndex = List.mapWithIndex (\idx -> \t -> Tuple idx t) fields
       fieldsS <- R.useState' fieldsWithIndex
+      fieldsRef <- R.useRef fields
+
+      -- handle props change of fields
+      R.useEffect1' fields $ do
+        if R.readRef fieldsRef == fields then
+          pure unit
+        else do
+          R.setRef fieldsRef fields
+          snd fieldsS $ const fieldsWithIndex
 
       pure $ H.div {} [
         H.div { className: "row" } [
@@ -122,19 +131,23 @@ fieldsCodeEditorCpt :: R.Component FieldsCodeEditorProps
 fieldsCodeEditorCpt = R.hooksComponent "G.C.N.C.fieldsCodeEditorCpt" cpt
   where
     cpt {nodeId, fields: fS@(fields /\ _), session} _ = do
-      pure $ H.div {} $ List.toUnfoldable editors
+      masterKey <- R.useState' 0
+
+      pure $ H.div {} $ List.toUnfoldable (editors masterKey)
       where
-        editors = (\idxField@(Tuple idx field) ->
-                    fieldCodeEditorWrapper { canMoveDown: idx < (List.length fields - 1)
-                                           , canMoveUp: idx > 0
-                                           , field
-                                           , hash: hash idxField
-                                           , onChange: onChange fS idx
-                                           , onMoveDown: onMoveDown fS idx
-                                           , onMoveUp: onMoveUp fS idx
-                                           , onRemove: onRemove fS idx
-                                           , onRename: onRename fS idx
-                                           }) <$> fields
+        editors masterKey =
+          (\idxField@(Tuple idx field) ->
+            fieldCodeEditorWrapper { canMoveDown: idx < (List.length fields - 1)
+                                   , canMoveUp: idx > 0
+                                   , field
+                                   , hash: hash idxField
+                                   , key: (show $ fst masterKey) <> "-" <> (show idx)
+                                   , onChange: onChange fS idx
+                                   , onMoveDown: onMoveDown masterKey fS idx
+                                   , onMoveUp: onMoveUp masterKey fS idx
+                                   , onRemove: onRemove fS idx
+                                   , onRename: onRename fS idx
+                                   }) <$> fields
 
     onChange :: R.State FTFieldsWithIndex -> Index -> FieldType -> Effect Unit
     onChange (_ /\ setFields) idx typ = do
@@ -143,13 +156,15 @@ fieldsCodeEditorCpt = R.hooksComponent "G.C.N.C.fieldsCodeEditorCpt" cpt
           Nothing -> fields
           Just newFields -> newFields
 
-    onMoveDown :: R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit
-    onMoveDown (fs /\ setFields) idx _ = do
+    onMoveDown :: R.State Int -> R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit
+    onMoveDown (_ /\ setMasterKey) (fs /\ setFields) idx _ = do
       setFields $ recomputeIndices <<< (GDA.swapList idx (idx + 1))
+      setMasterKey $ (+) 1
 
-    onMoveUp :: R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit
-    onMoveUp (_ /\ setFields) idx _ = do
+    onMoveUp :: R.State Int -> R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit
+    onMoveUp (_ /\ setMasterKey) (_ /\ setFields) idx _ = do
       setFields $ recomputeIndices <<< (GDA.swapList idx (idx - 1))
+      setMasterKey $ (+) 1
 
     onRemove :: R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit
     onRemove (_ /\ setFields) idx _ = do
@@ -176,7 +191,8 @@ type FieldCodeEditorProps =
     canMoveDown :: Boolean
   , canMoveUp :: Boolean
   , field :: FTField
-  , hash :: Hash
+  , hash :: Hash  -- TODO this isn't needed anymore
+  , key :: String
   , onChange :: FieldType -> Effect Unit
   , onMoveDown :: Unit -> Effect Unit
   , onMoveUp :: Unit -> Effect Unit
@@ -241,30 +257,53 @@ renameableCpt = R.hooksComponent "G.C.N.C.renameableCpt" cpt
     cpt {onRename, text} _ = do
       isEditing <- R.useState' false
       state <- R.useState' text
+      textRef <- R.useRef text
+
+      -- handle props change of text
+      R.useEffect1' text $ do
+        if R.readRef textRef == text then
+          pure unit
+        else do
+          R.setRef textRef text
+          snd state $ const text
 
       pure $ H.div { className: "renameable" } [
-        textCpt isEditing state
+        renameableText {isEditing, onRename, state}
       ]
-      where
-        textCpt :: R.State Boolean -> R.State String -> R.Element
-        textCpt (false /\ setIsEditing) (text /\ _) = H.div {} [
-            H.span { className: "text" } [ H.text text ]
-          , H.span { className: "btn btn-default"
-                  , on: { click: \_ -> setIsEditing $ const true } } [
-              H.span { className: "glyphicon glyphicon-pencil" } []
-            ]
+
+type RenameableTextProps =
+  (
+    isEditing :: R.State Boolean
+  , onRename :: String -> Effect Unit
+  , state :: R.State String
+  )
+
+renameableText :: Record RenameableTextProps -> R.Element
+renameableText props = R.createElement renameableTextCpt props []
+
+renameableTextCpt :: R.Component RenameableTextProps
+renameableTextCpt = R.hooksComponent "G.C.N.C.renameableTextCpt" cpt
+  where
+    cpt {isEditing: (false /\ setIsEditing), state: (text /\ _)} _ = do
+      pure $ H.div {} [
+        H.span { className: "text" } [ H.text text ]
+        , H.span { className: "btn btn-default"
+                 , on: { click: \_ -> setIsEditing $ const true } } [
+           H.span { className: "glyphicon glyphicon-pencil" } []
+           ]
         ]
-        textCpt (true /\ setIsEditing) (text /\ setText) = H.div {} [
-            H.input { defaultValue: text
-                    , className: "form-control text"
-                    , on: { change: \e -> setText $ const $ R2.unsafeEventValue e } }
-          , H.span { className: "btn btn-default"
-                  , on: { click: \_ -> do
-                          setIsEditing $ const false
-                          onRename text
-                        } } [
-              H.span { className: "glyphicon glyphicon-floppy-disk" } []
-            ]
+    cpt {isEditing: (true /\ setIsEditing), onRename, state: (text /\ setText)} _ = do
+      pure $ H.div {} [
+        H.input { defaultValue: text
+                , className: "form-control text"
+                , on: { change: \e -> setText $ const $ R2.unsafeEventValue e } }
+        , H.span { className: "btn btn-default"
+                 , on: { click: \_ -> do
+                            setIsEditing $ const false
+                            onRename text
+                       } } [
+           H.span { className: "glyphicon glyphicon-floppy-disk" } []
+           ]
         ]
 
 fieldCodeEditor :: Record FieldCodeEditorProps -> R.Element
-- 
2.21.0