From 471e3a22daeebe6edff975ab17f7c4e3adcdaf92 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Tue, 4 Jun 2019 15:53:26 +0200 Subject: [PATCH 1/2] fix move of channels --- graph/src/main/scala/Graph.scala | 18 +++++++----------- graph/src/main/scala/GraphChanges.scala | 15 +++++++-------- .../src/main/scala/dragdrop/DragActions.scala | 6 +++--- webApp/src/main/scala/dragdrop/DragItem.scala | 2 +- webApp/src/main/scala/state/FocusState.scala | 6 +++--- .../main/scala/state/GlobalStateFactory.scala | 2 +- webApp/src/main/scala/views/LeftSidebar.scala | 4 ++-- 7 files changed, 24 insertions(+), 29 deletions(-) diff --git a/graph/src/main/scala/Graph.scala b/graph/src/main/scala/Graph.scala index 9d1438e80..aa32e9f88 100644 --- a/graph/src/main/scala/Graph.scala +++ b/graph/src/main/scala/Graph.scala @@ -820,18 +820,14 @@ final class GraphLookup( def ancestorsIdx(nodeIdx: Int) = dfs.toArray(_(nodeIdx), dfs.afterStart, parentsIdx) def ancestors(nodeId: NodeId) = idToIdxFold(nodeId)(Seq.empty[NodeId])(nodeIdx => ancestorsIdx(nodeIdx).viewMap(nodeIds)) - def anyAncestorIsPinned(nodeIds: Iterable[NodeId], userId: NodeId): Boolean = idToIdxFold(userId)(false) { userIdx => - def starts(f: Int => Unit): Unit = nodeIds.foreach { nodeId => - idToIdxForeach(nodeId)(f) - } - - val isPinnedSet = { - val set = ArraySet.create(n) - pinnedNodeIdx.foreachElement(userIdx)(set.add) - set - } + def anyAncestorOrSelfIsPinned(nodeIdxs: Iterable[Int], userId: NodeId): Boolean = idToIdxFold(userId)(false) { userIdx => + pinnedNodeIdx.exists(userIdx)(pinnedIdx => nodeIdxs.exists(_ == pinnedIdx)) || anyAncestorIsPinned(nodeIdxs, userIdx) + } - dfs.exists(starts, dfs.withStart, parentsIdx, isFound = isPinnedSet.contains) + def anyAncestorIsPinned(nodeIdxs: Iterable[Int], userId: NodeId): Boolean = idToIdxFold(userId)(false)(anyAncestorIsPinned(nodeIdxs, _)) + def anyAncestorIsPinned(nodeIdxs: Iterable[Int], userIdx: Int): Boolean = { + val isPinnedSet = pinnedNodeIdx.toArraySet(userIdx) + dfs.exists(nodeIdxs.foreach, dfs.afterStart, parentsIdx, isFound = isPinnedSet.contains) } // IMPORTANT: diff --git a/graph/src/main/scala/GraphChanges.scala b/graph/src/main/scala/GraphChanges.scala index e4577244e..8ad0d60bf 100644 --- a/graph/src/main/scala/GraphChanges.scala +++ b/graph/src/main/scala/GraphChanges.scala @@ -286,19 +286,18 @@ object GraphChanges { @inline def moveInto(nodeIds: Iterable[ChildId], newParentIds: Iterable[ParentId], graph:Graph): GraphChanges = { GraphChanges.moveInto(graph, nodeIds, newParentIds) } - def movePinnedChannel(channelId: ChildId, targetChannelId: Option[ParentId], graph: Graph, userId: UserId): GraphChanges = graph.idToIdxFold(channelId)(GraphChanges.empty) { channelIdx => - val directParentsInChannelTree = graph.parentsIdx(channelIdx).collect { - case parentIdx if graph.anyAncestorIsPinned(graph.nodeIds(parentIdx) :: Nil, userId) => ParentId(graph.nodeIds(parentIdx)) - } - val disconnect: GraphChanges = GraphChanges.disconnect(Edge.Child)(directParentsInChannelTree, channelId) - val connect: GraphChanges = targetChannelId.fold(GraphChanges.empty) { - targetChannelId => GraphChanges.connect(Edge.Child)(targetChannelId, channelId) + def movePinnedChannel(channelId: ChildId, sourceChannelId: Option[ParentId], targetChannelId: Option[ParentId], graph: Graph, userId: UserId): GraphChanges = graph.idToIdxFold(channelId)(GraphChanges.empty) { channelIdx => + // target == None means that channel becomes top-level + + val disconnect: GraphChanges = GraphChanges.disconnect(Edge.Child)(sourceChannelId, channelId) + val connect: GraphChanges = targetChannelId.fold(GraphChanges.empty) { targetChannelId => + GraphChanges.connect(Edge.Child)(targetChannelId, channelId) } disconnect merge connect } - @inline def linkOrCopyInto(edge: Edge.LabeledProperty, nodeId: NodeId, graph:Graph): GraphChanges = { + def linkOrCopyInto(edge: Edge.LabeledProperty, nodeId: NodeId, graph:Graph): GraphChanges = { graph.nodesById(edge.propertyId) match { case Some(node: Node.Content) if node.role == NodeRole.Neutral => val copyNode = node.copy(id = NodeId.fresh) diff --git a/webApp/src/main/scala/dragdrop/DragActions.scala b/webApp/src/main/scala/dragdrop/DragActions.scala index 2e1bc9e85..cc6cc1872 100644 --- a/webApp/src/main/scala/dragdrop/DragActions.scala +++ b/webApp/src/main/scala/dragdrop/DragActions.scala @@ -128,10 +128,10 @@ object DragActions { case (payload: SelectedNodes, target: Workspace, ctrl, false) => (graph, userId) => linkOrMoveInto(payload.nodeIds.map(ChildId(_)), ParentId(target.nodeId), graph, ctrl) case (payload: SelectedNodes, target: Channel, ctrl, false) => (graph, userId) => linkOrMoveInto(payload.nodeIds.map(ChildId(_)), ParentId(target.nodeId), graph, ctrl) - case (payload: Channel, target: Channel, false, false) => (graph, userId) => movePinnedChannel(ChildId(payload.nodeId), Some(ParentId(target.nodeId)), graph, userId) + case (payload: Channel, target: Channel, false, false) => (graph, userId) => movePinnedChannel(ChildId(payload.nodeId), payload.parentId.map(ParentId(_)), Some(ParentId(target.nodeId)), graph, userId) case (payload: Channel, target: Channel, true, false) => (graph, userId) => linkOrMoveInto(ChildId(payload.nodeId), Some(ParentId(target.nodeId)), graph, true) - case (payload: Channel, target: Sidebar.type, false, false) => (graph, userId) => movePinnedChannel(ChildId(payload.nodeId), None, graph, userId) - case (payload: Channel, target: ContentNode, ctrl, false) => (graph, userId) => movePinnedChannel(ChildId(payload.nodeId), Some(ParentId(target.nodeId)), graph, userId) + case (payload: Channel, target: Sidebar.type, false, false) => (graph, userId) => movePinnedChannel(ChildId(payload.nodeId), payload.parentId.map(ParentId(_)), None, graph, userId) + case (payload: Channel, target: ContentNode, ctrl, false) => (graph, userId) => movePinnedChannel(ChildId(payload.nodeId), payload.parentId.map(ParentId(_)), Some(ParentId(target.nodeId)), graph, userId) case (payload: Property, target: ContentNode, false, false) => (graph, userId) => linkOrCopyInto(payload.edge, target.nodeId, graph) diff --git a/webApp/src/main/scala/dragdrop/DragItem.scala b/webApp/src/main/scala/dragdrop/DragItem.scala index 6d91ba863..eae0e7a11 100644 --- a/webApp/src/main/scala/dragdrop/DragItem.scala +++ b/webApp/src/main/scala/dragdrop/DragItem.scala @@ -26,7 +26,7 @@ object DragItem { case class Stage(nodeId: NodeId) extends DragPayloadAndTarget { override def toString = s"Stage(${nodeId.shortHumanReadable})" } case object Sidebar extends DragTarget - case class Channel(nodeId: NodeId) extends DragPayloadAndTarget { override def toString = s"Channel(${nodeId.shortHumanReadable})" } + case class Channel(nodeId: NodeId, parentId: Option[NodeId]) extends DragPayloadAndTarget { override def toString = s"Channel(${nodeId.shortHumanReadable}, parentId: ${parentId.map(_.shortHumanReadable)})" } case class BreadCrumb(nodeId: NodeId) extends DragPayloadAndTarget { override def toString = s"BreadCrumb(${nodeId.shortHumanReadable})" } case class Workspace(nodeId: NodeId) extends DragTarget { override def toString = s"Workspace(${nodeId.shortHumanReadable})" } case class TagBar(nodeId: NodeId) extends DragTarget { override def toString = s"TagBar(${nodeId.shortHumanReadable})" } diff --git a/webApp/src/main/scala/state/FocusState.scala b/webApp/src/main/scala/state/FocusState.scala index 271d19af5..261344253 100644 --- a/webApp/src/main/scala/state/FocusState.scala +++ b/webApp/src/main/scala/state/FocusState.scala @@ -6,10 +6,10 @@ import wust.ids.{NodeId, View} // when travsering a tree in the dom, we always have a current parent and a chain of ancestors. Needed to check cycles or operate on the parents in the views. case class TraverseState( parentId: NodeId, - parentIdChain: List[NodeId] = Nil + tail: List[NodeId] = Nil ) { - def contains(nodeId: NodeId): Boolean = parentId == nodeId || parentIdChain.contains(nodeId) - def step(nodeId: NodeId): TraverseState = TraverseState(nodeId, parentId :: parentIdChain) + def contains(nodeId: NodeId): Boolean = parentId == nodeId || tail.contains(nodeId) + def step(nodeId: NodeId): TraverseState = TraverseState(nodeId, parentId :: tail) } // a class for representing a preference to focus something in a certain view. e.g. used for configuring the right sidebar diff --git a/webApp/src/main/scala/state/GlobalStateFactory.scala b/webApp/src/main/scala/state/GlobalStateFactory.scala index 43c14f167..8936e5827 100644 --- a/webApp/src/main/scala/state/GlobalStateFactory.scala +++ b/webApp/src/main/scala/state/GlobalStateFactory.scala @@ -120,7 +120,7 @@ object GlobalStateFactory { page.parentId.fold(graph) { parentId => val userIdx = graph.idToIdx(user.id) graph.idToIdxFold(parentId)(graph) { pageIdx => - def anyPageParentIsPinned = graph.anyAncestorIsPinned(List(parentId), user.id) + def anyPageParentIsPinned = graph.anyAncestorOrSelfIsPinned(Array(pageIdx), user.id) def pageIsInvited = userIdx.fold(false)(userIdx => graph.inviteNodeIdx.contains(userIdx)(pageIdx)) def userIsMemberOfPage: Boolean = userIdx.fold(false)(userIdx => graph.membershipEdgeForNodeIdx.exists(pageIdx)(edgeIdx => graph.edgesIdx.b(edgeIdx) == userIdx)) diff --git a/webApp/src/main/scala/views/LeftSidebar.scala b/webApp/src/main/scala/views/LeftSidebar.scala index fbbc3e139..1a341c7a6 100644 --- a/webApp/src/main/scala/views/LeftSidebar.scala +++ b/webApp/src/main/scala/views/LeftSidebar.scala @@ -288,7 +288,7 @@ object LeftSidebar { onChannelClick(state, nodeId), onClick foreach { Analytics.sendEvent("sidebar_open", "clickchannel") }, cls := "node", - drag(DragItem.Channel(nodeId)), + drag(DragItem.Channel(nodeId, traverseState.tail.headOption)), channelModifier ) ) @@ -389,7 +389,7 @@ object LeftSidebar { UI.popup("right center") <-- node.map(_.str), onChannelClick(state, nodeId), onClick foreach { Analytics.sendEvent("sidebar_closed", "clickchannel") }, - drag(target = DragItem.Channel(nodeId)), + drag(target = DragItem.Channel(nodeId, traverseState.tail.headOption)), cls := "node", // for each indent, steal padding on left and right From 5f269f5657852332ee6d98ac2172faf38c52a03f Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Tue, 4 Jun 2019 16:09:38 +0200 Subject: [PATCH 2/2] wip --- webApp/src/main/scala/dragdrop/DragItem.scala | 30 +++++++++---------- webApp/src/main/scala/views/BreadCrumbs.scala | 2 +- webApp/src/main/scala/views/ChatView.scala | 2 +- webApp/src/main/scala/views/Components.scala | 22 +++++++------- .../src/main/scala/views/DashboardView.scala | 2 +- .../views/GraphChangesAutomationUI.scala | 2 +- .../main/scala/views/SharedViewElements.scala | 10 +++---- webApp/src/main/scala/views/TagList.scala | 2 +- webApp/src/main/scala/views/ThreadView.scala | 2 +- .../main/scala/views/TopologicalView.scala | 4 +-- 10 files changed, 40 insertions(+), 38 deletions(-) diff --git a/webApp/src/main/scala/dragdrop/DragItem.scala b/webApp/src/main/scala/dragdrop/DragItem.scala index eae0e7a11..d511255be 100644 --- a/webApp/src/main/scala/dragdrop/DragItem.scala +++ b/webApp/src/main/scala/dragdrop/DragItem.scala @@ -9,21 +9,21 @@ sealed trait DragPayloadAndTarget extends DragPayload with DragTarget object DragItem { case object DisableDrag extends DragPayloadAndTarget - sealed trait ContentNode extends DragPayloadAndTarget { def nodeId: NodeId } - case class Message(nodeId: NodeId) extends ContentNode { override def toString = s"Message(${nodeId.shortHumanReadable})" } - case class Task(nodeId: NodeId) extends ContentNode { override def toString = s"Task(${nodeId.shortHumanReadable})" } - case class Note(nodeId: NodeId) extends ContentNode { override def toString = s"Note(${nodeId.shortHumanReadable})" } - case class Project(nodeId: NodeId) extends ContentNode { override def toString = s"Project(${nodeId.shortHumanReadable})" } + sealed trait ContentNode extends DragPayloadAndTarget { def nodeId: NodeId; def parentId: Option[NodeId] } + case class Message(nodeId: NodeId, parentId: Option[NodeId]) extends ContentNode { override def toString = s"Message(${nodeId.shortHumanReadable}, parent = ${parentId.map(_.shortHumanReadable)})" } + case class Task(nodeId: NodeId, parentId: Option[NodeId]) extends ContentNode { override def toString = s"Task(${nodeId.shortHumanReadable}, parent = ${parentId.map(_.shortHumanReadable)})" } + case class Note(nodeId: NodeId, parentId: Option[NodeId]) extends ContentNode { override def toString = s"Note(${nodeId.shortHumanReadable}, parent = ${parentId.map(_.shortHumanReadable)})" } + case class Project(nodeId: NodeId, parentId: Option[NodeId]) extends ContentNode { override def toString = s"Project(${nodeId.shortHumanReadable}, parent = ${parentId.map(_.shortHumanReadable)})" } sealed trait ContentNodeConnect extends ContentNode { def propertyName: String } - case class TaskConnect(nodeId: NodeId, propertyName: String) extends ContentNodeConnect { override def toString = s"TaskConnect(${nodeId.shortHumanReadable}, $propertyName)" } - case class Tag(nodeId: NodeId) extends DragPayloadAndTarget { override def toString = s"Tag(${nodeId.shortHumanReadable})" } + case class TaskConnect(nodeId: NodeId, parentId: Option[NodeId], propertyName: String) extends ContentNodeConnect { override def toString = s"TaskConnect(${nodeId.shortHumanReadable}, parent = ${parentId.map(_.shortHumanReadable)}, $propertyName)" } + case class Tag(nodeId: NodeId, parentId: Option[NodeId]) extends DragPayloadAndTarget { override def toString = s"Tag(${nodeId.shortHumanReadable})" } case class Property(edge: Edge.LabeledProperty) extends DragPayloadAndTarget { override def toString = s"Property($edge)" } case class Thread(nodeIds: Seq[NodeId]) extends DragTarget { override def toString = s"Thread(${nodeIds.map(_.shortHumanReadable).mkString(",")})" } - case class Stage(nodeId: NodeId) extends DragPayloadAndTarget { override def toString = s"Stage(${nodeId.shortHumanReadable})" } + case class Stage(nodeId: NodeId, parentId: Option[NodeId]) extends DragPayloadAndTarget { override def toString = s"Stage(${nodeId.shortHumanReadable})" } case object Sidebar extends DragTarget case class Channel(nodeId: NodeId, parentId: Option[NodeId]) extends DragPayloadAndTarget { override def toString = s"Channel(${nodeId.shortHumanReadable}, parentId: ${parentId.map(_.shortHumanReadable)})" } @@ -36,13 +36,13 @@ object DragItem { case class SelectedNode(nodeId: NodeId) extends DragPayload { override def toString = s"SelectedNode(${nodeId.shortHumanReadable})" } case class SelectedNodes(nodeIds: Seq[NodeId]) extends DragPayload { override def toString = s"SelectedNodes(${nodeIds.map(_.shortHumanReadable).mkString(",")})" } - def fromNodeRole(nodeId: NodeId, role: NodeRole): Option[DragPayloadAndTarget] = Some(role) collect { - case NodeRole.Message => DragItem.Message(nodeId) - case NodeRole.Task => DragItem.Task(nodeId) - case NodeRole.Note => DragItem.Note(nodeId) - case NodeRole.Project => DragItem.Project(nodeId) - case NodeRole.Tag => DragItem.Tag(nodeId) - case NodeRole.Stage => DragItem.Stage(nodeId) + def fromNodeRole(nodeId: NodeId, parentId: Option[NodeId], role: NodeRole): Option[DragPayloadAndTarget] = Some(role) collect { + case NodeRole.Message => DragItem.Message(nodeId, parentId) + case NodeRole.Task => DragItem.Task(nodeId, parentId) + case NodeRole.Note => DragItem.Note(nodeId, parentId) + case NodeRole.Project => DragItem.Project(nodeId, parentId) + case NodeRole.Tag => DragItem.Tag(nodeId, parentId) + case NodeRole.Stage => DragItem.Stage(nodeId, parentId) } val payloadPropName = "_wust_dragpayload" diff --git a/webApp/src/main/scala/views/BreadCrumbs.scala b/webApp/src/main/scala/views/BreadCrumbs.scala index c7d5b52ad..3025f274e 100644 --- a/webApp/src/main/scala/views/BreadCrumbs.scala +++ b/webApp/src/main/scala/views/BreadCrumbs.scala @@ -103,7 +103,7 @@ object BreadCrumbs { Components.nodeCardAsOneLineText(node, projectWithIcon = true).apply( cls := "breadcrumb", VDomModifier.ifTrue(graph.isDeletedNowInAllParents(nid))(cls := "node-deleted"), - DragItem.fromNodeRole(node.id, node.role).map(drag(_)), + DragItem.fromNodeRole(node.id, Some(parentId), node.role).map(drag(_)), onClickFocus, ) diff --git a/webApp/src/main/scala/views/ChatView.scala b/webApp/src/main/scala/views/ChatView.scala index 4e7cbc40b..a4ab02c7e 100644 --- a/webApp/src/main/scala/views/ChatView.scala +++ b/webApp/src/main/scala/views/ChatView.scala @@ -317,7 +317,7 @@ object ChatView { else VDomModifier.empty } else VDomModifier.empty - val renderedMessage = renderMessage(state, nodeId, directParentIds, isDeletedNow = isDeletedNow, renderedMessageModifier = messageDragOptions(state, nodeId, selectedNodes)) + val renderedMessage = renderMessage(state, nodeId, directParentIds, isDeletedNow = isDeletedNow, renderedMessageModifier = messageDragOptions(state, nodeId, directParentIds.headOption, selectedNodes)) val controls = msgControls(state, nodeId, directParentIds.asInstanceOf[Iterable[ParentId]], selectedNodes, isDeletedNow = isDeletedNow, replyAction = replyAction) val checkbox = msgCheckbox(state, nodeId, selectedNodes, newSelectedNode = SelectedNode(_)(directParentIds), isSelected = isSelected) val selectByClickingOnRow = { diff --git a/webApp/src/main/scala/views/Components.scala b/webApp/src/main/scala/views/Components.scala index 3a375433e..bfdfb11d8 100644 --- a/webApp/src/main/scala/views/Components.scala +++ b/webApp/src/main/scala/views/Components.scala @@ -461,9 +461,10 @@ object Components { def checkboxNodeTag( state: GlobalState, tagNode: Node, + parentId: Option[NodeId], tagModifier: VDomModifier = VDomModifier.empty, pageOnClick: Boolean = false, - dragOptions: NodeId => VDomModifier = nodeId => drag(DragItem.Tag(nodeId), target = DragItem.DisableDrag), + dragOptions: (NodeId, Option[NodeId]) => VDomModifier = (nodeId, parentId) => drag(DragItem.Tag(nodeId, parentId), target = DragItem.DisableDrag), withAutomation: Boolean = false, )(implicit ctx: Ctx.Owner): VNode = { @@ -481,7 +482,7 @@ object Components { ), label(), // needed for fomanticui ), - nodeTag(state, tagNode, pageOnClick, dragOptions).apply(tagModifier), + nodeTag(state, tagNode, parentId, pageOnClick, dragOptions).apply(tagModifier), VDomModifier.ifTrue(withAutomation)(GraphChangesAutomationUI.settingsButton(state, tagNode.id, activeMod = visibility.visible).apply(cls := "singleButtonWithBg", marginLeft.auto)), ) } @@ -509,19 +510,20 @@ object Components { def nodeTag( state: GlobalState, tag: Node, + parentId: Option[NodeId], pageOnClick: Boolean = false, - dragOptions: NodeId => VDomModifier = nodeId => drag(DragItem.Tag(nodeId), target = DragItem.DisableDrag), + dragOptions: (NodeId, Option[NodeId]) => VDomModifier = (nodeId, parentId) => drag(DragItem.Tag(nodeId, parentId), target = DragItem.DisableDrag), ): VNode = { val contentString = renderAsOneLineText(tag) - renderNodeTag(state, tag, VDomModifier(contentString, dragOptions(tag.id)), pageOnClick) + renderNodeTag(state, tag, VDomModifier(contentString, dragOptions(tag.id, parentId)), pageOnClick) } - def removableNodeTagCustom(state: GlobalState, tag: Node, action: () => Unit, pageOnClick:Boolean = false): VNode = { - nodeTag(state, tag, pageOnClick)(removableTagMod(action)) + def removableNodeTagCustom(state: GlobalState, tag: Node, parentId: Option[NodeId], action: () => Unit, pageOnClick:Boolean = false): VNode = { + nodeTag(state, tag, parentId, pageOnClick)(removableTagMod(action)) } - def removableNodeTag(state: GlobalState, tag: Node, taggedNodeId: NodeId, pageOnClick:Boolean = false): VNode = { - removableNodeTagCustom(state, tag, () => { + def removableNodeTag(state: GlobalState, tag: Node, parentId: Option[NodeId], taggedNodeId: NodeId, pageOnClick:Boolean = false): VNode = { + removableNodeTagCustom(state, tag, parentId, () => { state.eventProcessor.changes.onNext( GraphChanges.disconnect(Edge.Child)(Array(ParentId(tag.id)), ChildId(taggedNodeId)) ) @@ -1076,7 +1078,7 @@ object Components { ) } - def automatedNodesOfNode(state: GlobalState, nodeId: NodeId)(implicit ctx: Ctx.Owner): VDomModifier = { + def automatedNodesOfNode(state: GlobalState, nodeId: NodeId, parentId: Option[NodeId])(implicit ctx: Ctx.Owner): VDomModifier = { val automatedNodes: Rx[Seq[Node]] = Rx { val graph = state.rawGraph() graph.idToIdxFold(nodeId)(Seq.empty[Node])(graph.automatedNodes) @@ -1089,7 +1091,7 @@ object Components { div( div(background := "repeating-linear-gradient(45deg, yellow, yellow 6px, black 6px, black 12px)", height := "3px"), UI.tooltip("bottom center") := "This node is an active automation template") - Components.nodeTag(state, node, pageOnClick = false, dragOptions = _ => VDomModifier.empty).prepend(renderFontAwesomeIcon(Icons.automate).apply(marginLeft := "3px", marginRight := "3px") + Components.nodeTag(state, node, parentId, pageOnClick = false, dragOptions = (_, _) => VDomModifier.empty).prepend(renderFontAwesomeIcon(Icons.automate).apply(marginLeft := "3px", marginRight := "3px") ) } ) diff --git a/webApp/src/main/scala/views/DashboardView.scala b/webApp/src/main/scala/views/DashboardView.scala index 888c58f22..c4e520044 100644 --- a/webApp/src/main/scala/views/DashboardView.scala +++ b/webApp/src/main/scala/views/DashboardView.scala @@ -125,7 +125,7 @@ object DashboardView { marginLeft := "10px", cls := "node channel-line", - drag(DragItem.Project(project.id)), + drag(DragItem.Project(project.id, Some(focusState.focusedId))), renderProject(project, renderNode = node => renderAsOneLineText(node).apply(cls := "channel-name"), withIcon = true), cursor.pointer, diff --git a/webApp/src/main/scala/views/GraphChangesAutomationUI.scala b/webApp/src/main/scala/views/GraphChangesAutomationUI.scala index 9d59e4f3a..499563059 100644 --- a/webApp/src/main/scala/views/GraphChangesAutomationUI.scala +++ b/webApp/src/main/scala/views/GraphChangesAutomationUI.scala @@ -146,7 +146,7 @@ object GraphChangesAutomationUI { state.rawGraph.map(g => VDomModifier.ifNot(g.parentsContains(templateNode.id)(focusedId))(i(color.gray, " * Template is not a direct child of the current node." ))), ), - DragItem.fromNodeRole(templateNode.id, templateNode.role).map(dragItem => Components.drag(target = dragItem)), + DragItem.fromNodeRole(templateNode.id, Some(focusedId), templateNode.role).map(dragItem => Components.drag(target = dragItem)), Components.sidebarNodeFocusMod(selectedTemplate, templateNode.id), ).prepend( b(color.gray, templateNode.role.toString) diff --git a/webApp/src/main/scala/views/SharedViewElements.scala b/webApp/src/main/scala/views/SharedViewElements.scala index 7d88765bd..79fb8c5b5 100644 --- a/webApp/src/main/scala/views/SharedViewElements.scala +++ b/webApp/src/main/scala/views/SharedViewElements.scala @@ -229,7 +229,7 @@ object SharedViewElements { }, ) - def messageDragOptions[T <: SelectedNodeBase](state: GlobalState, nodeId: NodeId, selectedNodes: Var[Set[T]])(implicit ctx: Ctx.Owner) = VDomModifier( + def messageDragOptions[T <: SelectedNodeBase](state: GlobalState, nodeId: NodeId, parentId: Option[NodeId], selectedNodes: Var[Set[T]])(implicit ctx: Ctx.Owner) = VDomModifier( Rx { val graph = state.graph() graph.idToIdx(nodeId).map { nodeIdx => @@ -238,17 +238,17 @@ object SharedViewElements { // payload is call by name, so it's always the current selectedNodeIds def payloadOverride:Option[() => DragPayload] = selection.find(_.nodeId == nodeId).map(_ => () => DragItem.SelectedNodes(selection.map(_.nodeId)(breakOut))) VDomModifier( - nodeDragOptions(nodeId, node.role, withHandle = false, payloadOverride = payloadOverride), + nodeDragOptions(nodeId, parentId, node.role, withHandle = false, payloadOverride = payloadOverride), onAfterPayloadWasDragged.foreach{ selectedNodes() = Set.empty[T] } ) } }, ) - def nodeDragOptions(nodeId:NodeId, role:NodeRole, withHandle:Boolean = false, payloadOverride:Option[() => DragPayload] = None): VDomModifier = { + def nodeDragOptions(nodeId:NodeId, parentId: Option[NodeId], role:NodeRole, withHandle:Boolean = false, payloadOverride:Option[() => DragPayload] = None): VDomModifier = { val dragItem = role match { - case NodeRole.Message => DragItem.Message(nodeId) - case NodeRole.Task => DragItem.Task(nodeId) + case NodeRole.Message => DragItem.Message(nodeId, parentId) + case NodeRole.Task => DragItem.Task(nodeId, parentId) case _ => DragItem.DisableDrag } val payload:() => DragPayload = payloadOverride.getOrElse(() => dragItem) diff --git a/webApp/src/main/scala/views/TagList.scala b/webApp/src/main/scala/views/TagList.scala index 32b51a169..f54f9ae00 100644 --- a/webApp/src/main/scala/views/TagList.scala +++ b/webApp/src/main/scala/views/TagList.scala @@ -98,7 +98,7 @@ object TagList { graph.tagChildrenIdx(workspaceIdx).map(tagIdx => graph.roleTree(root = tagIdx, NodeRole.Tag)) } - def renderTag(parentId: NodeId, tag: Node) = checkboxNodeTag(state, tag, tagModifier = removableTagMod(() => + def renderTag(parentId: NodeId, tag: Node) = checkboxNodeTag(state, tag, Some(workspaceId), tagModifier = removableTagMod(() => state.eventProcessor.changes.onNext(GraphChanges.disconnect(Edge.Child)(ParentId(parentId), ChildId(tag.id))) ), dragOptions = id => drag(DragItem.Tag(id)), withAutomation = true) diff --git a/webApp/src/main/scala/views/ThreadView.scala b/webApp/src/main/scala/views/ThreadView.scala index 92e68163e..831575e94 100644 --- a/webApp/src/main/scala/views/ThreadView.scala +++ b/webApp/src/main/scala/views/ThreadView.scala @@ -295,7 +295,7 @@ object ThreadView { color := "#666", boxShadow := "0px 1px 0px 1px rgb(102, 102, 102, 0.45)", ), - messageDragOptions(state, nodeId, selectedNodes), + messageDragOptions(state, nodeId, directParentIds.headOption, selectedNodes), )) val controls = msgControls(state, nodeId, directParentIds, selectedNodes, isDeletedNow = isDeletedNow, replyAction = showReplyField() = !showReplyField.now) val checkbox = msgCheckbox(state, nodeId, selectedNodes, newSelectedNode = SelectedNode(_, directParentIds)(showReplyField), isSelected = isSelected) diff --git a/webApp/src/main/scala/views/TopologicalView.scala b/webApp/src/main/scala/views/TopologicalView.scala index 2b48e6ff3..2b534da08 100644 --- a/webApp/src/main/scala/views/TopologicalView.scala +++ b/webApp/src/main/scala/views/TopologicalView.scala @@ -90,8 +90,8 @@ object TopologicalView { parentId = focusState.focusedId, focusState = focusState, inOneLine = true, - dragPayload = nodeId => DragItem.TaskConnect(nodeInfo.node.id, propertyName()), - dragTarget = nodeId => DragItem.TaskConnect(nodeInfo.node.id, propertyName()), + dragPayload = nodeId => DragItem.TaskConnect(nodeInfo.node.id, Some(focusState.focusedId), propertyName()), + dragTarget = nodeId => DragItem.TaskConnect(nodeInfo.node.id, Some(focusState.focusedId), propertyName()), ).apply( marginBottom := "3px", VDomModifier.ifTrue(isNewGroup)(marginTop := "40px"),