From 144c2f66c54196bcc06e9096818317fc222ea009 Mon Sep 17 00:00:00 2001
From: "Thomas F. K. Jorna" <thomasfkjorna@gmail.com>
Date: Sat, 31 Jul 2021 15:02:46 +0200
Subject: tried to add expanding follow, and failed

---
 pages/index.tsx | 119 ++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 89 insertions(+), 30 deletions(-)

(limited to 'pages/index.tsx')

diff --git a/pages/index.tsx b/pages/index.tsx
index 90d9686..6eb86b1 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -131,15 +131,72 @@ export function GraphPage() {
   const { setEmacsTheme } = useContext(ThemeContext)
 
   const [threeDim, setThreeDim] = useState(false)
+  const [scope, setScope] = useState<Scope>({ nodeIds: [] })
 
   const graphRef = useRef<any>(null)
 
+  const followBehavior = (
+    command: string,
+    emacsNode: string,
+    speed: number = 2000,
+    padding: number = 200,
+  ) => {
+    const fg = graphRef.current
+    const links = linksByNodeIdRef.current[emacsNode] ?? []
+    const nodes = Object.fromEntries(
+      [emacsNode as string, ...links.flatMap((link) => [link.source, link.target])].map(
+        (nodeId) => [nodeId, {}],
+      ),
+    )
+    if (command === 'zoom') {
+      if (scope.nodeIds.length) {
+        setScope({ nodeIds: [] })
+      }
+      setTimeout(() => fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!]), 50)
+      return
+    }
+    if (!scope.nodeIds.length) {
+      setScope({ nodeIds: [emacsNode] })
+      setTimeout(() => {
+        fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!])
+      }, 50)
+      return
+    }
+    if (behavior.localSame !== 'add') {
+      setScope({ nodeIds: [emacsNode] })
+      setTimeout(() => {
+        fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!])
+      }, 50)
+      return
+    }
+
+    // if the node is in the scopednodes, add it to scope instead of replacing it
+    if (
+      !scope.nodeIds.includes(emacsNode) ||
+      !scope.nodeIds.some((scopeId: string) => {
+        return nodes[scopeId]
+      })
+    ) {
+      setScope({ nodeIds: [emacsNode] })
+      setTimeout(() => {
+        fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!])
+      }, 50)
+      return
+    }
+    setScope((currentScope: Scope) => ({
+      ...currentScope,
+      nodeIds: [...currentScope.nodeIds, emacsNode as string],
+    }))
+    setTimeout(() => fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!]), 50)
+  }
+
   useEffect(() => {
     const socket = new ReconnectingWebSocket('ws://localhost:35903')
     socket.addEventListener('open', (event) => {
       console.log('Connection with Emacs established')
     })
     socket.addEventListener('message', (event) => {
+      const fg = graphRef.current
       const message = JSON.parse(event.data)
       switch (message.type) {
         case 'graphdata':
@@ -148,21 +205,28 @@ export function GraphPage() {
           return setEmacsTheme(message.data)
         case 'command':
           switch (message.data.commandName) {
-            case 'follow':
-              return setEmacsNodeId(message.data.id)
+            case 'local':
+              const speed = behavior.zoomSpeed
+              const padding = behavior.zoomPadding
+              followBehavior('local', message.data.id, speed, padding)
+              setEmacsNodeId(message.data.id)
+              break
             case 'zoom': {
-              const links = linksByNodeIdRef.current[message.data.id!] ?? []
-              const nodes = Object.fromEntries(
-                [
-                  message.data.id! as string,
-                  ...links.flatMap((link) => [link.source, link.target]),
-                ].map((nodeId) => [nodeId, {}]),
-              )
-              const fg = graphRef.current
-              fg.zoomToFit(2000, 200, (node: OrgRoamNode) => nodes[node.id!])
+              const speed = message?.data?.speed || behavior.zoomSpeed
+              const padding = message?.data?.padding || behavior.zoomPadding
+              followBehavior('zoom', message.data.id, speed, padding)
+              setEmacsNodeId(message.data.id)
+              break
             }
-            case 'toggle': {
-              /* setBehavior({ ...behavior, followLocalorZoom: !behavior.followLocalOrZoom }) */
+            case 'follow': {
+              followBehavior(
+                behavior.follow,
+                message.data.id,
+                behavior.zoomSpeed,
+                behavior.zoomPadding,
+              )
+              setEmacsNodeId(message.data.id)
+              break
             }
             default:
               return console.error('unknown message type', message.type)
@@ -189,6 +253,8 @@ export function GraphPage() {
           setVisuals,
           mouse,
           setMouse,
+          behavior,
+          setBehavior,
         }}
       />
       <Box position="absolute" alignItems="top">
@@ -205,6 +271,8 @@ export function GraphPage() {
             visuals,
             behavior,
             mouse,
+            scope,
+            setScope,
           }}
         />
       </Box>
@@ -223,6 +291,8 @@ export interface GraphProps {
   visuals: typeof initialVisuals
   behavior: typeof initialBehavior
   mouse: typeof initialMouse
+  scope: Scope
+  setScope: any
 }
 
 export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
@@ -237,6 +307,8 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
     visuals,
     behavior,
     mouse,
+    scope,
+    setScope,
   } = props
 
   // react-force-graph does not track window size
@@ -245,7 +317,6 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
   const [windowWidth, windowHeight] = useWindowSize()
 
   const [hoverNode, setHoverNode] = useState<NodeObject | null>(null)
-  const [scope, setScope] = useState<Scope>({ nodeIds: [] })
 
   const handleClick = (click: string, node: NodeObject) => {
     switch (click) {
@@ -254,7 +325,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
         if (scope.nodeIds.includes(node.id as string)) {
           break
         }
-        setScope((currentScope) => ({
+        setScope((currentScope: Scope) => ({
           ...currentScope,
           nodeIds: [...currentScope.nodeIds, node.id as string],
         }))
@@ -278,27 +349,15 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
     )
   }
 
+  const centralHighlightedNode = useRef<NodeObject | null>(null)
+
   useEffect(() => {
     if (!emacsNodeId) {
       return
     }
-    const fg = graphRef.current
-    if (behavior.followLocalOrZoom) {
-      setScope({ nodeIds: [emacsNodeId] })
-      setTimeout(() => {
-        fg?.zoomToFit(
-          2000,
-          numberWithinRange(20, 200, windowWidth / 8),
-          (node: NodeObject) => getNeighborNodes(emacsNodeId)[node.id!],
-        )
-      }, 1)
-    } else {
-      fg?.zoomToFit(1000, 200, (node: NodeObject) => getNeighborNodes(emacsNodeId)[node.id!])
-      setHoverNode(nodeById[emacsNodeId] as NodeObject)
-    }
+    centralHighlightedNode.current = nodeById[emacsNodeId] as NodeObject
   }, [emacsNodeId])
 
-  const centralHighlightedNode = useRef<NodeObject | null>(null)
   centralHighlightedNode.current = hoverNode
   const highlightedNodes = useMemo(() => {
     if (!centralHighlightedNode.current) {
-- 
cgit v1.2.3

