feat: make new connections

This commit is contained in:
Joshua Shoemaker 2023-07-26 08:56:25 -05:00
parent ee5ac6ea69
commit 7b7735c6af
20 changed files with 480 additions and 128 deletions

View File

@ -0,0 +1,112 @@
package contextGroup
import (
"fmt"
"textualize/entities"
)
type ContextGroupCollection struct {
Groups []entities.LinkedAreaList
}
var contextGroupCollectionInstance *ContextGroupCollection
func GetContextGroupCollection() *ContextGroupCollection {
if contextGroupCollectionInstance == nil {
contextGroupCollectionInstance = &ContextGroupCollection{}
}
return contextGroupCollectionInstance
}
func SetContextGroupCollection(collection ContextGroupCollection) *ContextGroupCollection {
contextGroupCollectionInstance = &collection
return contextGroupCollectionInstance
}
func SetContextGroupCollectionBySerialized(serialized []entities.SerializedLinkedProcessedArea) *ContextGroupCollection {
newInstance := ContextGroupCollection{}
newInstance.Groups = append(newInstance.Groups, entities.DeserializeLinkedAreaList(serialized))
SetContextGroupCollection(newInstance)
return &newInstance
}
func (collection *ContextGroupCollection) FindGroupById(id string) (*entities.LinkedAreaList, error) {
found := false
var foundGroup *entities.LinkedAreaList = nil
for _, group := range collection.Groups {
if group.Id == id {
found = true
foundGroup = &group
}
}
if !found {
return nil, fmt.Errorf("ContextGroupCollection.FindGroupById: Group with id %s not found", id)
}
return foundGroup, nil
}
func (collection *ContextGroupCollection) FindGroupByLinkedProcessedAreaId(id string) (*entities.LinkedAreaList, error) {
found := false
var foundGroup *entities.LinkedAreaList = nil
for _, group := range collection.Groups {
for n := group.First(); n != nil && !found; n = n.GetNext() {
if n.Area.Id == id {
found = true
foundGroup = &group
}
}
}
if !found {
return nil, fmt.Errorf("ContextGroupCollection.FindGroupByLinkedProcessedAreaId: Group with LinkedProcessedArea.Id %s not found", id)
}
return foundGroup, nil
}
func (collection *ContextGroupCollection) ConnectProcessedAreas(ancestorNode entities.ProcessedArea, descendantNode entities.ProcessedArea) bool {
ancestorGroup, _ := collection.FindGroupByLinkedProcessedAreaId(ancestorNode.Id)
descendantGroup, _ := collection.FindGroupByLinkedProcessedAreaId(descendantNode.Id)
isAncestorInAnyInGroup := ancestorGroup != nil
isDescendantInAnyInGroup := descendantGroup != nil
isEitherInAnyInGroup := isAncestorInAnyInGroup || isDescendantInAnyInGroup
areBothInAnyInGroup := isAncestorInAnyInGroup && isDescendantInAnyInGroup
areBothInSameGroup := false
if areBothInAnyInGroup {
areBothInSameGroup = ancestorGroup.Id == descendantGroup.Id
}
if areBothInSameGroup {
return true
}
if !isEitherInAnyInGroup {
collection.createNewGroupAndConnectNodes(ancestorNode, descendantNode)
return true
}
if isAncestorInAnyInGroup && !isDescendantInAnyInGroup {
ancestorGroup.InsertAfter(ancestorNode.Id, descendantNode)
return true
}
if !isAncestorInAnyInGroup && isDescendantInAnyInGroup {
descendantGroup.InsertBefore(descendantNode.Id, ancestorNode)
return true
}
return false
}
func (collection *ContextGroupCollection) createNewGroupAndConnectNodes(ancestorNode entities.ProcessedArea, descendantNode entities.ProcessedArea) {
newGroup := entities.LinkedAreaList{
Id: ancestorNode.Id,
DocumentId: ancestorNode.DocumentId,
Head: &entities.LinkedProcessedArea{Area: ancestorNode},
Tail: &entities.LinkedProcessedArea{Area: descendantNode},
}
newGroup.Head.Next = newGroup.Tail
newGroup.Tail.Previous = newGroup.Head
collection.Groups = append(collection.Groups, newGroup)
}

View File

@ -3,8 +3,6 @@ package entities
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/google/uuid"
) )
type IndependentTranslatedWord struct { type IndependentTranslatedWord struct {
@ -15,43 +13,57 @@ type IndependentTranslatedWord struct {
type LinkedProcessedArea struct { type LinkedProcessedArea struct {
Area ProcessedArea Area ProcessedArea
previous *LinkedProcessedArea Previous *LinkedProcessedArea
next *LinkedProcessedArea Next *LinkedProcessedArea
}
type SerializedLinkedProcessedArea struct {
AreaId string `json:"areaId"`
PreviousId string `json:"previousId"`
NextId string `json:"nextId"`
}
type ContextGroupCollection struct {
Groups []LinkedAreaList
} }
type LinkedAreaList struct { type LinkedAreaList struct {
head *LinkedProcessedArea Id string
tail *LinkedProcessedArea DocumentId string
TranslationText string
Head *LinkedProcessedArea
Tail *LinkedProcessedArea
} }
func (l *LinkedAreaList) First() *LinkedProcessedArea { func (l *LinkedAreaList) First() *LinkedProcessedArea {
return l.head return l.Head
} }
func (linkedProcessedWord *LinkedProcessedArea) Next() *LinkedProcessedArea { func (linkedProcessedWord *LinkedProcessedArea) GetNext() *LinkedProcessedArea {
return linkedProcessedWord.next return linkedProcessedWord.Next
} }
func (linkedProcessedWord *LinkedProcessedArea) Prev() *LinkedProcessedArea { func (linkedProcessedWord *LinkedProcessedArea) GetPrevious() *LinkedProcessedArea {
return linkedProcessedWord.previous return linkedProcessedWord.Previous
} }
// Create new node with value // Create new node with value
func (l *LinkedAreaList) Push(processedArea ProcessedArea) *LinkedAreaList { func (l *LinkedAreaList) Push(processedArea ProcessedArea) *LinkedAreaList {
n := &LinkedProcessedArea{Area: processedArea} n := &LinkedProcessedArea{Area: processedArea}
if l.head == nil { if l.Head == nil {
l.head = n // First node l.Head = n // First node
} else { } else {
l.tail.next = n // Add after prev last node l.Tail.Next = n // Add after prev last node
n.previous = l.tail // Link back to prev last node n.Previous = l.Tail // Link back to prev last node
} }
l.tail = n // reset tail to newly added node l.Tail = n // reset tail to newly added node
return l return l
} }
func (l *LinkedAreaList) Find(id string) *LinkedProcessedArea { func (l *LinkedAreaList) Find(id string) *LinkedProcessedArea {
found := false found := false
var ret *LinkedProcessedArea = nil var ret *LinkedProcessedArea = nil
for n := l.First(); n != nil && !found; n = n.Next() { for n := l.First(); n != nil && !found; n = n.GetNext() {
if n.Area.Id == id { if n.Area.Id == id {
found = true found = true
ret = n ret = n
@ -59,16 +71,17 @@ func (l *LinkedAreaList) Find(id string) *LinkedProcessedArea {
} }
return ret return ret
} }
func (l *LinkedAreaList) Delete(id string) bool { func (l *LinkedAreaList) Delete(id string) bool {
success := false success := false
node2del := l.Find(id) node2del := l.Find(id)
if node2del != nil { if node2del != nil {
fmt.Println("Delete - FOUND: ", id) fmt.Println("Delete - FOUND: ", id)
prev_node := node2del.previous prev_node := node2del.Previous
next_node := node2del.next next_node := node2del.Next
// Remove this node // Remove this node
prev_node.next = node2del.next prev_node.Next = node2del.Next
next_node.previous = node2del.previous next_node.Previous = node2del.Previous
success = true success = true
} }
return success return success
@ -78,86 +91,83 @@ var errEmpty = errors.New("ERROR - List is empty")
// Pop last item from list // Pop last item from list
func (l *LinkedAreaList) Pop() (processedArea ProcessedArea, err error) { func (l *LinkedAreaList) Pop() (processedArea ProcessedArea, err error) {
if l.tail == nil { if l.Tail == nil {
err = errEmpty err = errEmpty
} else { } else {
processedArea = l.tail.Area processedArea = l.Tail.Area
l.tail = l.tail.previous l.Tail = l.Tail.Previous
if l.tail == nil { if l.Tail == nil {
l.head = nil l.Head = nil
} }
} }
return processedArea, err return processedArea, err
} }
type ContextGroup struct { // TODO: possibly remove this and expand the LinkedAreaList struct instead func (l *LinkedAreaList) InsertAfter(id string, processedArea ProcessedArea) bool {
Id string found := false
DocumentId string for n := l.First(); n != nil && !found; n = n.GetNext() {
LinkedAreaList LinkedAreaList if n.Area.Id == id {
TranslationText string found = true
} newNode := &LinkedProcessedArea{Area: processedArea}
newNode.Next = n.Next
type ContextGroupCollection struct { // TODO: these methods should live in core not entitites newNode.Previous = n
Groups []ContextGroup n.Next = newNode
}
var contextGroupCollectionInstance *ContextGroupCollection
func GetContextGroupCollection() *ContextGroupCollection {
if contextGroupCollectionInstance == nil {
contextGroupCollectionInstance = &ContextGroupCollection{}
}
return contextGroupCollectionInstance
}
func SetContextGroupCollection(collection ContextGroupCollection) *ContextGroupCollection {
contextGroupCollectionInstance = &collection
return contextGroupCollectionInstance
}
func (collection *ContextGroupCollection) FindContextGroupByNodeId(id string) *ContextGroup {
var foundContextGroup *ContextGroup
for i, g := range collection.Groups {
if g.LinkedAreaList.Find(id) != nil {
foundContextGroup = &collection.Groups[i]
break
} }
} }
return found
return foundContextGroup
} }
func (collection *ContextGroupCollection) CreateContextGroupFromProcessedArea(area ProcessedArea) bool { func (l *LinkedAreaList) InsertBefore(id string, processedArea ProcessedArea) bool {
fmt.Println("CreateContextGroupFromProcessedArea") found := false
for n := l.First(); n != nil && !found; n = n.GetNext() {
newLinkedAreaList := LinkedAreaList{} if n.Area.Id == id {
newLinkedAreaList.Push(area) found = true
newNode := &LinkedProcessedArea{Area: processedArea}
newContextGroup := ContextGroup{ newNode.Next = n
Id: uuid.NewString(), newNode.Previous = n.Previous
DocumentId: area.DocumentId, n.Previous = newNode
LinkedAreaList: newLinkedAreaList, }
}
return found
} }
collection.Groups = append(collection.Groups, newContextGroup) func (l *LinkedAreaList) Serialize() []SerializedLinkedProcessedArea {
return true var serialized []SerializedLinkedProcessedArea
for n := l.First(); n != nil; n = n.GetNext() {
areaId := n.Area.Id
previousId := ""
if n.Previous != nil {
previousId = n.Previous.Area.Id
}
nextId := ""
if n.Next != nil {
nextId = n.Next.Area.Id
} }
// TODO: completely rework this linked list and the collection serialized = append(serialized, SerializedLinkedProcessedArea{
func (collection *ContextGroupCollection) ConnectAreaAsTailToNode(tailArea ProcessedArea, headArea ProcessedArea) bool { AreaId: areaId,
headNodeContextGroup := collection.FindContextGroupByNodeId(headArea.Id) PreviousId: previousId,
NextId: nextId,
if headNodeContextGroup == nil { })
collection.CreateContextGroupFromProcessedArea(headArea) }
headNodeContextGroup = collection.FindContextGroupByNodeId(headArea.Id) return serialized
} }
headNode := headNodeContextGroup.LinkedAreaList.Find(headArea.Id) func DeserializeLinkedAreaList(serialized []SerializedLinkedProcessedArea) LinkedAreaList {
headNode.next = &LinkedProcessedArea{ linkedAreaList := LinkedAreaList{}
Area: tailArea, for _, serializedLinkedProcessedArea := range serialized {
previous: headNode, linkedAreaList.Push(ProcessedArea{
Id: serializedLinkedProcessedArea.AreaId,
})
} }
for _, serializedLinkedProcessedArea := range serialized {
return true linkedProcessedArea := linkedAreaList.Find(serializedLinkedProcessedArea.AreaId)
if serializedLinkedProcessedArea.PreviousId != "" {
linkedProcessedArea.Previous = linkedAreaList.Find(serializedLinkedProcessedArea.PreviousId)
}
if serializedLinkedProcessedArea.NextId != "" {
linkedProcessedArea.Next = linkedAreaList.Find(serializedLinkedProcessedArea.NextId)
}
}
return linkedAreaList
} }

View File

@ -0,0 +1,71 @@
'use client'
import React from 'react'
import { Group, Line } from 'react-konva'
import { useProject } from '../../../context/Project/provider'
import { useStage } from '../context/provider'
const ConnectionLines = () => {
const { scale } = useStage()
const { getSelectedDocument, contextGroups } = useProject()
const areas = getSelectedDocument()?.areas || []
const renderLines = () => {
console.log('contextGroups', contextGroups)
if (!contextGroups?.length) return <></>
const linesAlreadyRendered = new Set<string>()
const lines = contextGroups.map((contextGroup) => {
const currentArea = areas.find(a => a.id === contextGroup.areaId)
const nextArea = areas.find(a => a.id === contextGroup.nextId)
if (!currentArea || !nextArea) return
if (linesAlreadyRendered.has(`${contextGroup.areaId}-${contextGroup.nextId}`)) return
if (linesAlreadyRendered.has(`${contextGroup.nextId}-${contextGroup.areaId}`)) return
const startingPoint = {
x: ((currentArea.startX + currentArea.endX) * scale) / 2,
y: currentArea.startY * scale
}
const startingTensionPoint = {
x: (startingPoint.x + (nextArea.startX * scale)) / 2,
y: startingPoint.y,
}
const endingPoint = {
x: ((nextArea.startX + nextArea.endX) * scale) / 2,
y: nextArea.endY * scale
}
const endingTensionPoint = {
x: (startingPoint.x + (nextArea.startX * scale)) / 2,
y: endingPoint.y,
}
linesAlreadyRendered.add(`${contextGroup.areaId}-${contextGroup.nextId}`)
linesAlreadyRendered.add(`${contextGroup.nextId}-${contextGroup.areaId}`)
return <Line
key={`${contextGroup.areaId}-${contextGroup.nextId}`}
points={[
...Object.values(startingPoint),
...Object.values(startingTensionPoint),
...Object.values(endingTensionPoint),
...Object.values(endingPoint),
]}
strokeEnabled
strokeWidth={2 * scale}
stroke='#dc8dec'
strokeScaleEnabled={false}
shadowForStrokeEnabled={false}
tension={0.2}
/>
})
return lines.filter(l => !!l)
}
return <Group>{renderLines()}</Group>
}
export default ConnectionLines

View File

@ -4,14 +4,14 @@ import { Circle, Group } from 'react-konva'
import { useStage } from '../context/provider' import { useStage } from '../context/provider'
import { entities } from '../../../wailsjs/wailsjs/go/models' import { entities } from '../../../wailsjs/wailsjs/go/models'
import { KonvaEventObject } from 'konva/lib/Node' import { KonvaEventObject } from 'konva/lib/Node'
import { RequestConnectAreaAsTailToNode } from '../../../wailsjs/wailsjs/go/ipc/Channel' import { useProject } from '../../../context/Project/provider'
type Props = { areas: entities.Area[] } type Props = { areas: entities.Area[] }
const ConnectionPoints = (props: Props) => { const ConnectionPoints = (props: Props) => {
const { isLinkAreaContextsVisible, scale, startingContextConnection, setStartingContextConnection } = useStage() const { isLinkAreaContextsVisible, scale, startingContextConnection, setStartingContextConnection } = useStage()
const { requestConnectProcessedAreas } = useProject()
const handleContextAreaMouseDown = async (e: KonvaEventObject<MouseEvent>) => {
const handleContextAreaMouseDown = (e: KonvaEventObject<MouseEvent>) => {
e.cancelBubble = true e.cancelBubble = true
const clickedConnectionPoint = { const clickedConnectionPoint = {
isHead: e.currentTarget.attrs.isHead, isHead: e.currentTarget.attrs.isHead,
@ -24,10 +24,15 @@ const ConnectionPoints = (props: Props) => {
|| clickedConnectionPoint.areaId === startingContextConnection.areaId) || clickedConnectionPoint.areaId === startingContextConnection.areaId)
return setStartingContextConnection(null) return setStartingContextConnection(null)
console.log('connected points', startingContextConnection, clickedConnectionPoint) const headId = startingContextConnection.isHead ? startingContextConnection.areaId : clickedConnectionPoint.areaId
const headId = clickedConnectionPoint.isHead ? clickedConnectionPoint.areaId : startingContextConnection.areaId const tailId = !startingContextConnection.isHead ? startingContextConnection.areaId : clickedConnectionPoint.areaId
const tailId = !clickedConnectionPoint.isHead ? startingContextConnection.areaId : clickedConnectionPoint.areaId setStartingContextConnection(null)
RequestConnectAreaAsTailToNode(headId, tailId).then(res => console.log(res)).catch(err => console.warn(err))
try {
await requestConnectProcessedAreas(headId, tailId)
} catch (err) {
console.warn('RequestConnectProcessedAreas', err)
}
} }
const renderConnectingPointsForArea = (a: entities.Area) => { const renderConnectingPointsForArea = (a: entities.Area) => {
@ -36,7 +41,7 @@ const ConnectionPoints = (props: Props) => {
const headConnector = <Circle const headConnector = <Circle
key={`head-${a.id}`} key={`head-${a.id}`}
id={a.id} id={a.id}
radius={10} radius={8}
x={((a.startX + a.endX) * scale) / 2} x={((a.startX + a.endX) * scale) / 2}
y={a.startY * scale} y={a.startY * scale}
strokeEnabled={false} strokeEnabled={false}
@ -84,7 +89,7 @@ const ConnectionPoints = (props: Props) => {
/>) />)
} }
return <Group> return <Group key={`group-${a.id}`}>
{connectorsToRender} {connectorsToRender}
</Group> </Group>
} }

View File

@ -2,18 +2,19 @@
import React from 'react' import React from 'react'
import { Line } from 'react-konva' import { Line } from 'react-konva'
import { StartingContextConnection } from '../context/types'
import { entities } from '../../../wailsjs/wailsjs/go/models'
import { Coordinates } from '../types' import { Coordinates } from '../types'
import { useStage } from '../context/provider'
import { useProject } from '../../../context/Project/provider'
type CurrentDrawingConnectionProps = { type CurrentDrawingConnectionProps = {
startingContextConnection: StartingContextConnection | null
areas: entities.Area[],
scale: number,
endDrawingPosition: Coordinates | null endDrawingPosition: Coordinates | null
} }
const CurrentDrawingConnection = (props: CurrentDrawingConnectionProps) => { const CurrentDrawingConnection = (props: CurrentDrawingConnectionProps) => {
const { startingContextConnection, areas, scale, endDrawingPosition } = props const { endDrawingPosition } = props
const { startingContextConnection, scale } = useStage()
const { getSelectedDocument } = useProject()
const areas = getSelectedDocument()?.areas || []
if (!startingContextConnection || !endDrawingPosition) return <></> if (!startingContextConnection || !endDrawingPosition) return <></>
const { areaId, isHead } = startingContextConnection const { areaId, isHead } = startingContextConnection

View File

@ -8,10 +8,11 @@ import Konva from 'konva'
import { Coordinates } from '../types' import { Coordinates } from '../types'
import CurrentDrawingConnection from './CurrentDrawingConnection' import CurrentDrawingConnection from './CurrentDrawingConnection'
import ConnectionPoints from './ConnectionPoints' import ConnectionPoints from './ConnectionPoints'
import ConnectionLines from './ConnectionLines'
const ContextConnections = () => { const ContextConnections = () => {
const { getSelectedDocument } = useProject() const { getSelectedDocument } = useProject()
const { isLinkAreaContextsVisible, startingContextConnection, setStartingContextConnection, scale } = useStage() const { isLinkAreaContextsVisible, startingContextConnection, scale } = useStage()
const areas = getSelectedDocument()?.areas || [] const areas = getSelectedDocument()?.areas || []
const [endDrawingPosition, setEndDrawingPosition] = useState<Coordinates | null>(null) const [endDrawingPosition, setEndDrawingPosition] = useState<Coordinates | null>(null)
@ -34,7 +35,8 @@ const ContextConnections = () => {
return <Group> return <Group>
<ConnectionPoints areas={areas} /> <ConnectionPoints areas={areas} />
<CurrentDrawingConnection areas={areas} startingContextConnection={startingContextConnection} endDrawingPosition={endDrawingPosition} scale={scale} /> <ConnectionLines />
<CurrentDrawingConnection endDrawingPosition={endDrawingPosition} />
</Group> </Group>
} }

View File

@ -0,0 +1,38 @@
import { saveContextGroups } from '../../useCases/saveData'
import { RequestConnectProcessedAreas, GetSerializedContextGroups } from '../../wailsjs/wailsjs/go/ipc/Channel'
import { entities } from '../../wailsjs/wailsjs/go/models'
type Dependencies = { updateDocuments: Function }
const createContextGroupProviderMethods = (dependencies?: Dependencies) => {
const requestConnectProcessedAreas = async (headId: string, tailId: string) => {
let wasSuccessful = false
try {
wasSuccessful = await RequestConnectProcessedAreas(headId, tailId)
await saveContextGroups()
} catch (err) {
console.error(err)
}
dependencies?.updateDocuments()
return wasSuccessful
}
const getSerializedContextGroups = async () => {
let response: entities.SerializedLinkedProcessedArea[] = []
try {
response = await GetSerializedContextGroups()
} catch (err) {
console.error(err)
}
return response
}
return {
requestConnectProcessedAreas,
getSerializedContextGroups,
}
}
export default createContextGroupProviderMethods

View File

@ -1,6 +1,6 @@
import { saveUserProcessedMarkdown } from '../../useCases/saveData' import { saveUserProcessedMarkdown } from '../../useCases/saveData'
import { GetUserMarkdownByDocumentId, RequestUpdateDocumentUserMarkdown } from '../../wailsjs/wailsjs/go/ipc/Channel' import { GetUserMarkdownByDocumentId, RequestUpdateDocumentUserMarkdown } from '../../wailsjs/wailsjs/go/ipc/Channel'
import { ipc, entities } from '../../wailsjs/wailsjs/go/models' import { entities } from '../../wailsjs/wailsjs/go/models'
type Dependencies = {} type Dependencies = {}

View File

@ -5,12 +5,13 @@ const makeDefaultProject = (): ProjectContextType => ({
id: '', id: '',
documents: [] as entities.Document[], documents: [] as entities.Document[],
groups: [] as entities.Group[], groups: [] as entities.Group[],
contextGroups: [] as entities.SerializedLinkedProcessedArea[],
selectedAreaId: '', selectedAreaId: '',
selectedDocumentId: '', selectedDocumentId: '',
getSelectedDocument: () => new entities.Document(), getSelectedDocument: () => new entities.Document(),
getAreaById: (areaId) => undefined, getAreaById: (areaId) => undefined,
getProcessedAreasByDocumentId: (documentId) => Promise.resolve([new entities.ProcessedArea()]), getProcessedAreasByDocumentId: (documentId) => Promise.resolve([new entities.ProcessedArea()]),
requestAddProcessedArea: (processesArea) => Promise.resolve(new entities.ProcessedArea()), requestAddProcessedArea: (processesArea) => Promise.resolve(false),
requestAddArea: (documentId, area) => Promise.resolve(new entities.Area()), requestAddArea: (documentId, area) => Promise.resolve(new entities.Area()),
requestUpdateArea: (updatedArea) => Promise.resolve(false), requestUpdateArea: (updatedArea) => Promise.resolve(false),
requestDeleteAreaById: (areaId) => Promise.resolve(false), requestDeleteAreaById: (areaId) => Promise.resolve(false),
@ -33,6 +34,8 @@ const makeDefaultProject = (): ProjectContextType => ({
requestUpdateProcessedWordById: (wordId, newTestValue) => Promise.resolve(false), requestUpdateProcessedWordById: (wordId, newTestValue) => Promise.resolve(false),
getProcessedAreaById: (areaId) => Promise.resolve(undefined), getProcessedAreaById: (areaId) => Promise.resolve(undefined),
requestUpdateProcessedArea: updatedProcessedArea => Promise.resolve(false), requestUpdateProcessedArea: updatedProcessedArea => Promise.resolve(false),
requestConnectProcessedAreas: (headId, tailId) => Promise.resolve(false),
getSerializedContextGroups: () => Promise.resolve([]),
}) })
export default makeDefaultProject export default makeDefaultProject

View File

@ -10,6 +10,7 @@ import createAreaProviderMethods from './createAreaProviderMethods'
import createDocumentProviderMethods from './createDocumentMethods' import createDocumentProviderMethods from './createDocumentMethods'
import createSessionProviderMethods from './createSessionProviderMethods' import createSessionProviderMethods from './createSessionProviderMethods'
import createUserMarkdownProviderMethods from './createUserMarkdownProviderMethods' import createUserMarkdownProviderMethods from './createUserMarkdownProviderMethods'
import createContextGroupProviderMethods from './createContextGroupProviderMethods'
const ProjectContext = createContext<ProjectContextType>(makeDefaultProject()) const ProjectContext = createContext<ProjectContextType>(makeDefaultProject())
@ -21,15 +22,17 @@ type Props = { children: ReactNode, projectProps: ProjectProps }
export function ProjectProvider({ children, projectProps }: Props) { export function ProjectProvider({ children, projectProps }: Props) {
const [documents, setDocuments] = useState<entities.Document[]>(projectProps.documents) const [documents, setDocuments] = useState<entities.Document[]>(projectProps.documents)
const [groups, setGroups] = useState<entities.Group[]>(projectProps.groups) const [groups, setGroups] = useState<entities.Group[]>(projectProps.groups)
const [contextGroups, setContextGroups] = useState<entities.SerializedLinkedProcessedArea[]>(projectProps.contextGroups)
const [selectedAreaId, setSelectedAreaId] = useState<string>('') const [selectedAreaId, setSelectedAreaId] = useState<string>('')
const [selectedDocumentId, setSelectedDocumentId] = useState<string>('') const [selectedDocumentId, setSelectedDocumentId] = useState<string>('')
const [currentSession, setCurrentSession] = useState<entities.Session>(new entities.Session()) const [currentSession, setCurrentSession] = useState<entities.Session>(new entities.Session())
const updateDocuments = async () => { const updateDocuments = async () => {
const response = await GetDocuments() const response = await GetDocuments()
const { documents, groups } = response const { documents, groups, contextGroups } = response
setDocuments(documents) setDocuments(documents)
setGroups(groups) setGroups(groups)
setContextGroups(contextGroups)
return response return response
} }
@ -43,6 +46,7 @@ export function ProjectProvider({ children, projectProps }: Props) {
const areaMethods = createAreaProviderMethods({ documents, updateDocuments, selectedDocumentId }) const areaMethods = createAreaProviderMethods({ documents, updateDocuments, selectedDocumentId })
const sessionMethods = createSessionProviderMethods({ updateSession, updateDocuments }) const sessionMethods = createSessionProviderMethods({ updateSession, updateDocuments })
const userMarkDownMethods = createUserMarkdownProviderMethods() const userMarkDownMethods = createUserMarkdownProviderMethods()
const contextGroupMethods = createContextGroupProviderMethods({ updateDocuments })
useEffect(() => { useEffect(() => {
@ -60,6 +64,7 @@ export function ProjectProvider({ children, projectProps }: Props) {
id: '', id: '',
documents, documents,
groups, groups,
contextGroups,
selectedAreaId, selectedAreaId,
setSelectedAreaId, setSelectedAreaId,
selectedDocumentId, selectedDocumentId,
@ -69,6 +74,7 @@ export function ProjectProvider({ children, projectProps }: Props) {
...documentMethods, ...documentMethods,
...sessionMethods, ...sessionMethods,
...userMarkDownMethods, ...userMarkDownMethods,
...contextGroupMethods,
} }
return <ProjectContext.Provider value={value}> return <ProjectContext.Provider value={value}>

View File

@ -4,6 +4,7 @@ export type ProjectProps = {
id: string, id: string,
documents: entities.Document[], documents: entities.Document[],
groups: entities.Group[], groups: entities.Group[],
contextGroups: entities.SerializedLinkedProcessedArea[],
} }
export type AddAreaProps = { export type AddAreaProps = {
@ -65,4 +66,6 @@ export type ProjectContextType = {
requestUpdateProcessedWordById: (wordId: string, newTextValue: string) => Promise<boolean> requestUpdateProcessedWordById: (wordId: string, newTextValue: string) => Promise<boolean>
getProcessedAreaById: (areaId: string) => Promise<entities.ProcessedArea | undefined> getProcessedAreaById: (areaId: string) => Promise<entities.ProcessedArea | undefined>
requestUpdateProcessedArea: (updatedProcessedArea: entities.ProcessedArea) => Promise<boolean> requestUpdateProcessedArea: (updatedProcessedArea: entities.ProcessedArea) => Promise<boolean>
requestConnectProcessedAreas: (headId: string, tailId: string) => Promise<boolean>
getSerializedContextGroups: () => Promise<entities.SerializedLinkedProcessedArea[]>
} & ProjectProps } & ProjectProps

View File

@ -1,4 +1,7 @@
import { RequestSaveDocumentCollection, RequestSaveGroupCollection, RequestSaveLocalUserProcessedMarkdownCollection, RequestSaveProcessedTextCollection } from '../wailsjs/wailsjs/go/ipc/Channel' import { RequestSaveDocumentCollection, RequestSaveGroupCollection,
RequestSaveLocalUserProcessedMarkdownCollection,
RequestSaveProcessedTextCollection, RequestSaveContextGroupCollection
} from '../wailsjs/wailsjs/go/ipc/Channel'
const saveDocuments = async () => { const saveDocuments = async () => {
try { try {
@ -36,9 +39,19 @@ const saveUserProcessedMarkdown = async () => {
} }
} }
const saveContextGroups = async () => {
try {
const sucessfulSave = await RequestSaveContextGroupCollection()
if (!sucessfulSave) console.error('Could not save ContextGroupCollection')
} catch (err) {
console.error('Could not save ContextGroupCollection: ', err)
}
}
export { export {
saveDocuments, saveDocuments,
saveGroups, saveGroups,
saveProcessedText, saveProcessedText,
saveUserProcessedMarkdown, saveUserProcessedMarkdown,
saveContextGroups,
} }

View File

@ -23,6 +23,8 @@ export function GetProcessedAreasByDocumentId(arg1:string):Promise<Array<entitie
export function GetProjectByName(arg1:string):Promise<entities.Project>; export function GetProjectByName(arg1:string):Promise<entities.Project>;
export function GetSerializedContextGroups():Promise<Array<entities.SerializedLinkedProcessedArea>>;
export function GetSupportedLanguages():Promise<Array<entities.Language>>; export function GetSupportedLanguages():Promise<Array<entities.Language>>;
export function GetUserMarkdownByDocumentId(arg1:string):Promise<entities.UserMarkdown>; export function GetUserMarkdownByDocumentId(arg1:string):Promise<entities.UserMarkdown>;
@ -43,7 +45,7 @@ export function RequestChangeSessionProjectByName(arg1:string):Promise<boolean>;
export function RequestChooseUserAvatar():Promise<string>; export function RequestChooseUserAvatar():Promise<string>;
export function RequestConnectAreaAsTailToNode(arg1:string,arg2:string):Promise<boolean>; export function RequestConnectProcessedAreas(arg1:string,arg2:string):Promise<boolean>;
export function RequestDeleteAreaById(arg1:string):Promise<boolean>; export function RequestDeleteAreaById(arg1:string):Promise<boolean>;
@ -51,6 +53,8 @@ export function RequestDeleteDocumentAndChildren(arg1:string):Promise<boolean>;
export function RequestDeleteProcessedAreaById(arg1:string):Promise<boolean>; export function RequestDeleteProcessedAreaById(arg1:string):Promise<boolean>;
export function RequestSaveContextGroupCollection():Promise<boolean>;
export function RequestSaveDocumentCollection():Promise<boolean>; export function RequestSaveDocumentCollection():Promise<boolean>;
export function RequestSaveGroupCollection():Promise<boolean>; export function RequestSaveGroupCollection():Promise<boolean>;

View File

@ -42,6 +42,10 @@ export function GetProjectByName(arg1) {
return window['go']['ipc']['Channel']['GetProjectByName'](arg1); return window['go']['ipc']['Channel']['GetProjectByName'](arg1);
} }
export function GetSerializedContextGroups() {
return window['go']['ipc']['Channel']['GetSerializedContextGroups']();
}
export function GetSupportedLanguages() { export function GetSupportedLanguages() {
return window['go']['ipc']['Channel']['GetSupportedLanguages'](); return window['go']['ipc']['Channel']['GetSupportedLanguages']();
} }
@ -82,8 +86,8 @@ export function RequestChooseUserAvatar() {
return window['go']['ipc']['Channel']['RequestChooseUserAvatar'](); return window['go']['ipc']['Channel']['RequestChooseUserAvatar']();
} }
export function RequestConnectAreaAsTailToNode(arg1, arg2) { export function RequestConnectProcessedAreas(arg1, arg2) {
return window['go']['ipc']['Channel']['RequestConnectAreaAsTailToNode'](arg1, arg2); return window['go']['ipc']['Channel']['RequestConnectProcessedAreas'](arg1, arg2);
} }
export function RequestDeleteAreaById(arg1) { export function RequestDeleteAreaById(arg1) {
@ -98,6 +102,10 @@ export function RequestDeleteProcessedAreaById(arg1) {
return window['go']['ipc']['Channel']['RequestDeleteProcessedAreaById'](arg1); return window['go']['ipc']['Channel']['RequestDeleteProcessedAreaById'](arg1);
} }
export function RequestSaveContextGroupCollection() {
return window['go']['ipc']['Channel']['RequestSaveContextGroupCollection']();
}
export function RequestSaveDocumentCollection() { export function RequestSaveDocumentCollection() {
return window['go']['ipc']['Channel']['RequestSaveDocumentCollection'](); return window['go']['ipc']['Channel']['RequestSaveDocumentCollection']();
} }

View File

@ -422,6 +422,22 @@ export namespace entities {
} }
} }
export class SerializedLinkedProcessedArea {
areaId: string;
previousId: string;
nextId: string;
static createFrom(source: any = {}) {
return new SerializedLinkedProcessedArea(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.areaId = source["areaId"];
this.previousId = source["previousId"];
this.nextId = source["nextId"];
}
}
export class Session { export class Session {
project: Project; project: Project;
organization: Organization; organization: Organization;
@ -481,6 +497,7 @@ export namespace ipc {
export class GetDocumentsResponse { export class GetDocumentsResponse {
documents: entities.Document[]; documents: entities.Document[];
groups: entities.Group[]; groups: entities.Group[];
contextGroups: entities.SerializedLinkedProcessedArea[];
static createFrom(source: any = {}) { static createFrom(source: any = {}) {
return new GetDocumentsResponse(source); return new GetDocumentsResponse(source);
@ -490,6 +507,7 @@ export namespace ipc {
if ('string' === typeof source) source = JSON.parse(source); if ('string' === typeof source) source = JSON.parse(source);
this.documents = this.convertValues(source["documents"], entities.Document); this.documents = this.convertValues(source["documents"], entities.Document);
this.groups = this.convertValues(source["groups"], entities.Group); this.groups = this.convertValues(source["groups"], entities.Group);
this.contextGroups = this.convertValues(source["contextGroups"], entities.SerializedLinkedProcessedArea);
} }
convertValues(a: any, classs: any, asMap: boolean = false): any { convertValues(a: any, classs: any, asMap: boolean = false): any {

View File

@ -1,22 +1,46 @@
package ipc package ipc
import ( import (
contextGroup "textualize/core/ContextGroup"
document "textualize/core/Document" document "textualize/core/Document"
"textualize/entities" "textualize/entities"
"textualize/storage"
) )
func (c *Channel) RequestConnectAreaAsTailToNode(tailId string, headId string) bool { func (c *Channel) RequestConnectProcessedAreas(ancestorAreaId string, descendantAreaId string) bool {
processedAreaOfTail := document.GetProcessedAreaCollection().GetAreaById(tailId) processedAreaCollection := document.GetProcessedAreaCollection()
if processedAreaOfTail == nil {
ancestorArea := processedAreaCollection.GetAreaById(ancestorAreaId)
descendantArea := processedAreaCollection.GetAreaById(descendantAreaId)
wasSuccessfulConnect := contextGroup.GetContextGroupCollection().ConnectProcessedAreas(*ancestorArea, *descendantArea)
if wasSuccessfulConnect {
wasSuccessfulWrite := c.RequestSaveContextGroupCollection()
return wasSuccessfulWrite
}
return false return false
} }
processedAreaOfHead := document.GetProcessedAreaCollection().GetAreaById(headId) func (c *Channel) GetSerializedContextGroups() []entities.SerializedLinkedProcessedArea {
if processedAreaOfHead == nil { contextGroupCollection := contextGroup.GetContextGroupCollection()
return false
serializedContextGroups := make([]entities.SerializedLinkedProcessedArea, 0)
for _, group := range contextGroupCollection.Groups {
serializedContextGroups = append(serializedContextGroups, group.Serialize()...)
} }
entities.GetContextGroupCollection().ConnectAreaAsTailToNode(*processedAreaOfTail, *processedAreaOfHead) return serializedContextGroups
}
return true
func (c *Channel) RequestSaveContextGroupCollection() bool {
contextGroupCollection := contextGroup.GetContextGroupCollection()
projectName := c.GetCurrentSession().Project.Name
serializedContextGroups := make([]entities.SerializedLinkedProcessedArea, 0)
for _, group := range contextGroupCollection.Groups {
serializedContextGroups = append(serializedContextGroups, group.Serialize()...)
}
successfulWrite := storage.GetDriver().WriteContextGroupCollection(serializedContextGroups, projectName)
return successfulWrite
} }

View File

@ -16,6 +16,7 @@ import (
type GetDocumentsResponse struct { type GetDocumentsResponse struct {
Documents []entities.Document `json:"documents"` Documents []entities.Document `json:"documents"`
Groups []entities.Group `json:"groups"` Groups []entities.Group `json:"groups"`
ContextGroups []entities.SerializedLinkedProcessedArea `json:"contextGroups"`
} }
func (c *Channel) GetDocumentById(id string) entities.Document { func (c *Channel) GetDocumentById(id string) entities.Document {
@ -26,10 +27,12 @@ func (c *Channel) GetDocumentById(id string) entities.Document {
func (c *Channel) GetDocuments() GetDocumentsResponse { func (c *Channel) GetDocuments() GetDocumentsResponse {
documents := document.GetDocumentCollection().Documents documents := document.GetDocumentCollection().Documents
groups := document.GetGroupCollection().Groups groups := document.GetGroupCollection().Groups
contextGroups := c.GetSerializedContextGroups()
response := GetDocumentsResponse{ response := GetDocumentsResponse{
Groups: make([]entities.Group, 0), Groups: make([]entities.Group, 0),
Documents: make([]entities.Document, 0), Documents: make([]entities.Document, 0),
ContextGroups: contextGroups,
} }
for _, d := range documents { for _, d := range documents {

View File

@ -3,6 +3,7 @@ package ipc
import ( import (
app "textualize/core/App" app "textualize/core/App"
consts "textualize/core/Consts" consts "textualize/core/Consts"
contextGroup "textualize/core/ContextGroup"
document "textualize/core/Document" document "textualize/core/Document"
session "textualize/core/Session" session "textualize/core/Session"
"textualize/entities" "textualize/entities"
@ -144,6 +145,7 @@ func (c *Channel) RequestChangeSessionProjectByName(projectName string) bool {
session.GetInstance().Project = foundProject session.GetInstance().Project = foundProject
// Documents
localDocumentCollection := storageDriver.ReadDocumentCollection(projectName) localDocumentCollection := storageDriver.ReadDocumentCollection(projectName)
documentCount := len(localDocumentCollection.Documents) documentCount := len(localDocumentCollection.Documents)
readableDocuments := make([]document.Entity, documentCount) readableDocuments := make([]document.Entity, documentCount)
@ -155,6 +157,7 @@ func (c *Channel) RequestChangeSessionProjectByName(projectName string) bool {
ProjectId: foundProject.Id, ProjectId: foundProject.Id,
}) })
// Groups
localGroupsCollection := storageDriver.ReadGroupCollection(projectName) localGroupsCollection := storageDriver.ReadGroupCollection(projectName)
groupCount := len(localGroupsCollection.Groups) groupCount := len(localGroupsCollection.Groups)
readableGroups := make([]entities.Group, groupCount) readableGroups := make([]entities.Group, groupCount)
@ -167,6 +170,10 @@ func (c *Channel) RequestChangeSessionProjectByName(projectName string) bool {
Groups: readableGroups, Groups: readableGroups,
}) })
// Context Groups
localSerializedContextGroups := storageDriver.ReadContextGroupCollection(projectName)
contextGroup.SetContextGroupCollectionBySerialized(localSerializedContextGroups)
// Processed Texts // Processed Texts
localProcessedAreaCollection := storageDriver.ReadProcessedTextCollection(projectName) localProcessedAreaCollection := storageDriver.ReadProcessedTextCollection(projectName)
areaCount := len(localProcessedAreaCollection.Areas) areaCount := len(localProcessedAreaCollection.Areas)

View File

@ -0,0 +1,22 @@
package storage
import (
"encoding/json"
"textualize/entities"
)
func (d LocalDriver) WriteContextGroupCollection(serializedContextGroups []entities.SerializedLinkedProcessedArea, projectName string) bool {
jsonData, _ := json.MarshalIndent(serializedContextGroups, "", " ")
writeError := WriteDataToAppDir(jsonData, "/projects/"+projectName+"/", "ContextGroups.json")
return writeError == nil
}
func (d LocalDriver) ReadContextGroupCollection(projectName string) []entities.SerializedLinkedProcessedArea {
contextGroupCollectionData := make([]entities.SerializedLinkedProcessedArea, 0)
readError := AssignFileDataToStruct("/projects/"+projectName+"/ContextGroups.json", &contextGroupCollectionData)
if readError != nil {
return make([]entities.SerializedLinkedProcessedArea, 0)
}
return contextGroupCollectionData
}

View File

@ -19,6 +19,8 @@ type Driver interface {
ReadProcessedTextCollection(string) entities.ProcessedTextCollection ReadProcessedTextCollection(string) entities.ProcessedTextCollection
WriteProcessedUserMarkdownCollection(entities.ProcessedUserMarkdownCollection, string) bool WriteProcessedUserMarkdownCollection(entities.ProcessedUserMarkdownCollection, string) bool
ReadProcessedUserMarkdownCollection(string) entities.ProcessedUserMarkdownCollection ReadProcessedUserMarkdownCollection(string) entities.ProcessedUserMarkdownCollection
WriteContextGroupCollection([]entities.SerializedLinkedProcessedArea, string) bool
ReadContextGroupCollection(string) []entities.SerializedLinkedProcessedArea
} }
var driverInstance Driver var driverInstance Driver