From f9cc43a0c5ebc228d1af7eb7fb896d7e77edfb67 Mon Sep 17 00:00:00 2001 From: Joshua Shoemaker Date: Fri, 26 May 2023 16:50:57 -0500 Subject: [PATCH 1/4] refact: generalized back end structs --- core/App/app.go | 5 +- core/Consts/Consts.go | 16 +- core/Document/Document.go | 29 +- core/Document/DocumentGroup.go | 22 +- core/Document/Initialize.go | 6 +- core/Document/ProcessedText.go | 49 +-- core/Document/UserMarkdown.go | 18 +- core/Session/Organization.go | 9 +- core/Session/Project.go | 15 +- core/Session/Session.go | 12 +- {storage/Entities => entities}/Document.go | 2 +- {storage/Entities => entities}/Group.go | 2 +- {storage/Entities => entities}/Language.go | 2 +- entities/Organization.go | 8 + .../Entities => entities}/ProcessedText.go | 2 +- {storage/Entities => entities}/Project.go | 2 +- entities/Session.go | 7 + {storage/Entities => entities}/User.go | 2 +- entities/UserMarkdown.go | 11 + .../DocumentCanvas/AreaTextPreview.tsx | 8 +- .../DocumentCanvas/EditProcessedWord.tsx | 10 +- .../components/DocumentCanvas/UiCanvas.tsx | 6 +- .../createUiCanvasInteractions.ts | 4 +- frontend/components/project/Main.tsx | 4 +- .../components/project/ProjectListModal.tsx | 4 +- .../components/workspace/LanguageSelect.tsx | 10 +- .../workspace/Sidebar/navigationProps.ts | 4 +- .../Project/createAreaProviderMethods.ts | 18 +- .../context/Project/createDocumentMethods.ts | 10 +- .../Project/createSessionProviderMethods.ts | 6 +- .../createUserMarkdownProviderMethods.ts | 8 +- .../context/Project/makeDefaultProject.ts | 36 +- frontend/context/Project/provider.tsx | 8 +- frontend/context/Project/types.ts | 46 +-- frontend/useCases/processImageArea.ts | 14 +- frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts | 41 +-- frontend/wailsjs/wailsjs/go/models.ts | 72 ++-- ipc/Documents.go | 330 +++++------------- ipc/JsonEntities.go | 128 ------- ipc/ProcessedDocument.go | 179 +--------- ipc/Session.go | 219 +++--------- storage/Local/DocumentDriver.go | 10 +- storage/Local/GroupDriver.go | 10 +- storage/Local/ProcessedTextDriver.go | 18 +- storage/Local/ProjectDriver.go | 14 +- storage/Local/UserDriver.go | 10 +- storage/Storage.go | 28 +- 47 files changed, 454 insertions(+), 1020 deletions(-) rename {storage/Entities => entities}/Document.go (97%) rename {storage/Entities => entities}/Group.go (95%) rename {storage/Entities => entities}/Language.go (90%) create mode 100644 entities/Organization.go rename {storage/Entities => entities}/ProcessedText.go (98%) rename {storage/Entities => entities}/Project.go (96%) create mode 100644 entities/Session.go rename {storage/Entities => entities}/User.go (94%) create mode 100644 entities/UserMarkdown.go delete mode 100644 ipc/JsonEntities.go diff --git a/core/App/app.go b/core/App/app.go index 0eab6b2..2eef02e 100644 --- a/core/App/app.go +++ b/core/App/app.go @@ -5,6 +5,7 @@ import ( "fmt" document "textualize/core/Document" session "textualize/core/Session" + "textualize/entities" storage "textualize/storage" ) @@ -26,10 +27,10 @@ func (a *App) Startup(ctx context.Context) { a.Context = ctx localUserData := storage.GetDriver().ReadUserData() session.InitializeModule(session.Session{ - User: session.User(localUserData), + User: entities.User(localUserData), }) - document.InitizeModule() + document.InitializeModule() fmt.Println(localUserData) } diff --git a/core/Consts/Consts.go b/core/Consts/Consts.go index 17b6b6e..0d4d37a 100644 --- a/core/Consts/Consts.go +++ b/core/Consts/Consts.go @@ -1,13 +1,15 @@ package consts -type Language struct { - DisplayName string - ProcessCode string - TranslateCode string -} +import "textualize/entities" -func GetSuppportedLanguages() []Language { - return []Language{ +// type Language struct { +// DisplayName string +// ProcessCode string +// TranslateCode string +// } + +func GetSuppportedLanguages() []entities.Language { + return []entities.Language{ { DisplayName: "English", ProcessCode: "eng", diff --git a/core/Document/Document.go b/core/Document/Document.go index ca8b35e..4a285db 100644 --- a/core/Document/Document.go +++ b/core/Document/Document.go @@ -1,36 +1,19 @@ package document import ( - consts "textualize/core/Consts" + "textualize/entities" ) -type Entity struct { - Id string - GroupId string - Name string - Path string - ProjectId string - Areas []Area - DefaultLanguage consts.Language -} +type Entity entities.Document -type Area struct { - Id string - Name string - StartX int - StartY int - EndX int - EndY int - Language consts.Language - Order int -} +type Area entities.Area -func (e *Entity) AddArea(a Area) { +func (e *Entity) AddArea(a entities.Area) { e.Areas = append(e.Areas, a) } -func (e *Entity) GetAreaById(areaId string) *Area { - var foundArea *Area +func (e *Entity) GetAreaById(areaId string) *entities.Area { + var foundArea *entities.Area for index, a := range e.Areas { if a.Id == areaId { diff --git a/core/Document/DocumentGroup.go b/core/Document/DocumentGroup.go index 74221e3..d1ab824 100644 --- a/core/Document/DocumentGroup.go +++ b/core/Document/DocumentGroup.go @@ -1,18 +1,10 @@ package document -type Group struct { - Id string - ParentId string - ProjectId string - Name string - Order int -} +import "textualize/entities" -type GroupCollection struct { - Id string - Groups []Group - ProjectId string -} +type Group entities.Group + +type GroupCollection entities.GroupCollection var groupCollectionInstance *GroupCollection @@ -29,12 +21,12 @@ func SetGroupCollection(collection GroupCollection) *GroupCollection { return groupCollectionInstance } -func (collection *GroupCollection) AddDocumentGroup(group Group) { +func (collection *GroupCollection) AddDocumentGroup(group entities.Group) { collection.Groups = append(collection.Groups, group) } -func (collection *GroupCollection) GetGroupById(groupId string) *Group { - var foundGroup *Group +func (collection *GroupCollection) GetGroupById(groupId string) *entities.Group { + var foundGroup *entities.Group for index, g := range collection.Groups { if g.Id == groupId { diff --git a/core/Document/Initialize.go b/core/Document/Initialize.go index a3e074c..82958c5 100644 --- a/core/Document/Initialize.go +++ b/core/Document/Initialize.go @@ -1,6 +1,8 @@ package document -func InitizeModule() { +import "textualize/entities" + +func InitializeModule() { GetDocumentCollection() GetGroupCollection() } @@ -9,7 +11,7 @@ func createTestData() { documentCollection := GetDocumentCollection() documentGroupCollection := GetGroupCollection() - documentGroupCollection.AddDocumentGroup(Group{ + documentGroupCollection.AddDocumentGroup(entities.Group{ Id: "XYZ", Name: "Test Group One", }) diff --git a/core/Document/ProcessedText.go b/core/Document/ProcessedText.go index 749f723..1423ffd 100644 --- a/core/Document/ProcessedText.go +++ b/core/Document/ProcessedText.go @@ -1,42 +1,19 @@ package document -type ProcessedBoundingBox struct { - X0 int32 - Y0 int32 - X1 int32 - Y1 int32 -} +import "textualize/entities" -type ProcessedSymbol struct { - Text string - Confidence float32 - BoundingBox ProcessedBoundingBox -} +type ProcessedBoundingBox entities.ProcessedBoundingBox -type ProcessedWord struct { - Id string - FullText string - Symbols []ProcessedSymbol - Confidence float32 - Direction string - BoundingBox ProcessedBoundingBox -} +type ProcessedSymbol entities.ProcessedSymbol -type ProcessedLine struct { - FullText string - Words []ProcessedWord -} +type ProcessedWord entities.ProcessedWord -type ProcessedArea struct { - Id string - DocumentId string - FullText string - Order int - Lines []ProcessedLine -} +type ProcessedLine entities.ProcessedLine + +type ProcessedArea entities.ProcessedArea type ProcessedAreaCollection struct { - Areas []ProcessedArea + Areas []entities.ProcessedArea } var processedAreaCollectionInstnace *ProcessedAreaCollection @@ -52,12 +29,12 @@ func SetProcessedAreaCollection(collection ProcessedAreaCollection) { processedAreaCollectionInstnace = &collection } -func (collection *ProcessedAreaCollection) AddProcessedArea(area ProcessedArea) { +func (collection *ProcessedAreaCollection) AddProcessedArea(area entities.ProcessedArea) { collection.Areas = append(collection.Areas, area) } -func (collection *ProcessedAreaCollection) GetAreasByDocumentId(id string) []*ProcessedArea { - var foundAreas []*ProcessedArea +func (collection *ProcessedAreaCollection) GetAreasByDocumentId(id string) []*entities.ProcessedArea { + var foundAreas []*entities.ProcessedArea for index, a := range collection.Areas { if a.DocumentId == id { @@ -68,8 +45,8 @@ func (collection *ProcessedAreaCollection) GetAreasByDocumentId(id string) []*Pr return foundAreas } -func (collection *ProcessedAreaCollection) GetAreaById(areaId string) *ProcessedArea { - var foundArea *ProcessedArea +func (collection *ProcessedAreaCollection) GetAreaById(areaId string) *entities.ProcessedArea { + var foundArea *entities.ProcessedArea for index, a := range collection.Areas { if a.Id == areaId { diff --git a/core/Document/UserMarkdown.go b/core/Document/UserMarkdown.go index 18d6cb5..a3278a6 100644 --- a/core/Document/UserMarkdown.go +++ b/core/Document/UserMarkdown.go @@ -1,13 +1,11 @@ package document -type UserMarkdown struct { - Id string - DocumentId string - Value string -} +import "textualize/entities" + +type UserMarkdown entities.UserMarkdown type UserMarkdownCollection struct { - Values []UserMarkdown + Values []entities.UserMarkdown } var userMarkdownCollection *UserMarkdownCollection @@ -24,8 +22,8 @@ func SetUserMarkdownCollection(collection UserMarkdownCollection) { userMarkdownCollection = &collection } -func (collection *UserMarkdownCollection) GetUserMarkdownByDocumentId(documentId string) *UserMarkdown { - var foundUserMarkdown *UserMarkdown +func (collection *UserMarkdownCollection) GetUserMarkdownByDocumentId(documentId string) *entities.UserMarkdown { + var foundUserMarkdown *entities.UserMarkdown for index, m := range collection.Values { if m.DocumentId == documentId { @@ -37,12 +35,12 @@ func (collection *UserMarkdownCollection) GetUserMarkdownByDocumentId(documentId return foundUserMarkdown } -func (collection *UserMarkdownCollection) AddUserMarkdown(userMarkdown UserMarkdown) UserMarkdown { +func (collection *UserMarkdownCollection) AddUserMarkdown(userMarkdown entities.UserMarkdown) entities.UserMarkdown { collection.Values = append(collection.Values, userMarkdown) return userMarkdown } -func (collection *UserMarkdownCollection) UpdateUserMarkdown(userMarkdown UserMarkdown) UserMarkdown { +func (collection *UserMarkdownCollection) UpdateUserMarkdown(userMarkdown entities.UserMarkdown) entities.UserMarkdown { currentUserMarkdown := collection.GetUserMarkdownByDocumentId(userMarkdown.DocumentId) if currentUserMarkdown != nil { diff --git a/core/Session/Organization.go b/core/Session/Organization.go index dc0191f..fc86d54 100644 --- a/core/Session/Organization.go +++ b/core/Session/Organization.go @@ -1,8 +1,5 @@ package session -type Organization struct { - Id string - Name string - LogoPath string - Users []User -} +import "textualize/entities" + +type Organization entities.Organization diff --git a/core/Session/Project.go b/core/Session/Project.go index fe2896f..8e6aa88 100644 --- a/core/Session/Project.go +++ b/core/Session/Project.go @@ -1,18 +1,9 @@ package session import ( - consts "textualize/core/Consts" + "textualize/entities" ) -type Project struct { - Id string - OrganizationId string - Name string - Settings ProjectSettings -} +type Project entities.Project -type ProjectSettings struct { - DefaultProcessLanguage consts.Language - DefaultTranslateTargetLanguage consts.Language - IsHosted bool -} +type ProjectSettings entities.ProjectSettings diff --git a/core/Session/Session.go b/core/Session/Session.go index 0bb1da8..df0a974 100644 --- a/core/Session/Session.go +++ b/core/Session/Session.go @@ -1,10 +1,8 @@ package session -type Session struct { - Project Project - Organization Organization - User User -} +import "textualize/entities" + +type Session entities.Session var sessionInstance *Session @@ -22,7 +20,7 @@ func InitializeModule(newSession Session) *Session { return sessionInstance } -func (s *Session) UpdateCurrentUser(updatedUser User) User { - s.User = User(updatedUser) +func (s *Session) UpdateCurrentUser(updatedUser entities.User) entities.User { + s.User = entities.User(updatedUser) return s.User } diff --git a/storage/Entities/Document.go b/entities/Document.go similarity index 97% rename from storage/Entities/Document.go rename to entities/Document.go index 743cce6..ef6c9fe 100644 --- a/storage/Entities/Document.go +++ b/entities/Document.go @@ -1,4 +1,4 @@ -package storage +package entities type DocumentCollection struct { Documents []Document `json:"documents"` diff --git a/storage/Entities/Group.go b/entities/Group.go similarity index 95% rename from storage/Entities/Group.go rename to entities/Group.go index 496304e..c1ffe38 100644 --- a/storage/Entities/Group.go +++ b/entities/Group.go @@ -1,4 +1,4 @@ -package storage +package entities type Group struct { Id string `json:"id"` diff --git a/storage/Entities/Language.go b/entities/Language.go similarity index 90% rename from storage/Entities/Language.go rename to entities/Language.go index 25cde8c..5dbf4f5 100644 --- a/storage/Entities/Language.go +++ b/entities/Language.go @@ -1,4 +1,4 @@ -package storage +package entities type Language struct { DisplayName string `json:"displayName"` diff --git a/entities/Organization.go b/entities/Organization.go new file mode 100644 index 0000000..690426a --- /dev/null +++ b/entities/Organization.go @@ -0,0 +1,8 @@ +package entities + +type Organization struct { + Id string `json:"id"` + Name string `json:"name"` + LogoPath string `json:"logoPath"` + Users []User `json:"users"` +} diff --git a/storage/Entities/ProcessedText.go b/entities/ProcessedText.go similarity index 98% rename from storage/Entities/ProcessedText.go rename to entities/ProcessedText.go index 24ffb1f..69f9cda 100644 --- a/storage/Entities/ProcessedText.go +++ b/entities/ProcessedText.go @@ -1,4 +1,4 @@ -package storage +package entities type ProcessedBoundingBox struct { X0 int32 `json:"x0"` diff --git a/storage/Entities/Project.go b/entities/Project.go similarity index 96% rename from storage/Entities/Project.go rename to entities/Project.go index ef50cb7..dcb12a2 100644 --- a/storage/Entities/Project.go +++ b/entities/Project.go @@ -1,4 +1,4 @@ -package storage +package entities type Project struct { Id string `json:"id"` diff --git a/entities/Session.go b/entities/Session.go new file mode 100644 index 0000000..b015f93 --- /dev/null +++ b/entities/Session.go @@ -0,0 +1,7 @@ +package entities + +type Session struct { + Project Project `json:"project"` + Organization Organization `json:"organization"` + User User `json:"user"` +} diff --git a/storage/Entities/User.go b/entities/User.go similarity index 94% rename from storage/Entities/User.go rename to entities/User.go index 3104f96..ea25713 100644 --- a/storage/Entities/User.go +++ b/entities/User.go @@ -1,4 +1,4 @@ -package storage +package entities type User struct { Id string `json:"id"` diff --git a/entities/UserMarkdown.go b/entities/UserMarkdown.go new file mode 100644 index 0000000..65b28a8 --- /dev/null +++ b/entities/UserMarkdown.go @@ -0,0 +1,11 @@ +package entities + +type UserMarkdown struct { + Id string `json:"id"` + DocumentId string `json:"documentId"` + Value string `json:"value"` +} + +type UserMarkdownCollection struct { + Values []UserMarkdown `json:"values"` +} diff --git a/frontend/components/DocumentCanvas/AreaTextPreview.tsx b/frontend/components/DocumentCanvas/AreaTextPreview.tsx index 0aaa9ea..6752578 100644 --- a/frontend/components/DocumentCanvas/AreaTextPreview.tsx +++ b/frontend/components/DocumentCanvas/AreaTextPreview.tsx @@ -1,12 +1,12 @@ import React from 'react' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { entities } from '../../wailsjs/wailsjs/go/models' import classNames from '../../utils/classNames' type Props = { - areas: ipc.Area[] - processedArea?: ipc.ProcessedArea + areas: entities.Area[] + processedArea?: entities.ProcessedArea zoomLevel: number - setWordToEdit: (props: { word: ipc.ProcessedWord, areaId: string }) => void + setWordToEdit: (props: { word: entities.ProcessedWord, areaId: string }) => void } const AreaTextPreview = ({ areas, processedArea, zoomLevel, setWordToEdit }: Props) => { diff --git a/frontend/components/DocumentCanvas/EditProcessedWord.tsx b/frontend/components/DocumentCanvas/EditProcessedWord.tsx index c3ee198..c65b0fe 100644 --- a/frontend/components/DocumentCanvas/EditProcessedWord.tsx +++ b/frontend/components/DocumentCanvas/EditProcessedWord.tsx @@ -1,15 +1,15 @@ import React, { useRef } from 'react' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { ipc, entities } from '../../wailsjs/wailsjs/go/models' import classNames from '../../utils/classNames' import onEnterHandler from '../../utils/onEnterHandler' import { useProject } from '../../context/Project/provider' type Props = { zoomLevel: number - processedArea?: ipc.ProcessedArea - wordToEdit?: ipc.ProcessedWord - setWordToEdit: (props?: { word: ipc.ProcessedWord, areaId: string }) => void - setHoveredProcessedArea: (area?: ipc.ProcessedArea) => void + processedArea?: entities.ProcessedArea + wordToEdit?: entities.ProcessedWord + setWordToEdit: (props?: { word: entities.ProcessedWord, areaId: string }) => void + setHoveredProcessedArea: (area?: entities.ProcessedArea) => void } const EditProcessedWord = ({ setWordToEdit, zoomLevel, wordToEdit, processedArea, setHoveredProcessedArea }: Props) => { diff --git a/frontend/components/DocumentCanvas/UiCanvas.tsx b/frontend/components/DocumentCanvas/UiCanvas.tsx index b97b448..ddcccf8 100644 --- a/frontend/components/DocumentCanvas/UiCanvas.tsx +++ b/frontend/components/DocumentCanvas/UiCanvas.tsx @@ -2,7 +2,7 @@ import React, { WheelEvent, useEffect, useRef, useState } from 'react' import { useProject } from '../../context/Project/provider' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { entities } from '../../wailsjs/wailsjs/go/models' import createUiCanvasInteractions from './createUiCanvasInteractions' import processImageArea from '../../useCases/processImageArea' import AreaTextPreview from './AreaTextPreview' @@ -30,8 +30,8 @@ const UiCanvas = (props: Props) => { } = useProject() const canvas = useRef(null) const [hoverOverAreaId, setHoverOverAreaId] = useState('') - const [wordToEdit, setWordToEdit] = useState<{ word: ipc.ProcessedWord, areaId: string } | undefined>() - const [hoveredProcessedArea, setHoveredProcessedArea] = useState() + const [wordToEdit, setWordToEdit] = useState<{ word: entities.ProcessedWord, areaId: string } | undefined>() + const [hoveredProcessedArea, setHoveredProcessedArea] = useState() const areas = getSelectedDocument()?.areas || [] const { width, height, zoomDetails, setZoomLevel } = props diff --git a/frontend/components/DocumentCanvas/createUiCanvasInteractions.ts b/frontend/components/DocumentCanvas/createUiCanvasInteractions.ts index a0454f5..4bbd903 100644 --- a/frontend/components/DocumentCanvas/createUiCanvasInteractions.ts +++ b/frontend/components/DocumentCanvas/createUiCanvasInteractions.ts @@ -1,5 +1,5 @@ import isInBounds from '../../utils/isInBounds' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { entities } from '../../wailsjs/wailsjs/go/models' type MouseCoordinates = { @@ -75,7 +75,7 @@ const createUiCanvasInteractions = (uiCanvas: HTMLCanvasElement) => { if (shouldAttemptToZoomIn) setZoomCallBack(currentZoomLevel + zoomStep) else if (currentZoomLevel > (zoomStep * 2)) setZoomCallBack(currentZoomLevel - zoomStep) }, - onHoverOverArea: (mouseX: number, mouseY: number, zoomLevel: number, areas: ipc.Area[], callback: HoverOverAreaCallback) => { + onHoverOverArea: (mouseX: number, mouseY: number, zoomLevel: number, areas: entities.Area[], callback: HoverOverAreaCallback) => { if (!areas.length) return const domRect = uiCanvas.getBoundingClientRect() diff --git a/frontend/components/project/Main.tsx b/frontend/components/project/Main.tsx index 3c25370..0986a7b 100644 --- a/frontend/components/project/Main.tsx +++ b/frontend/components/project/Main.tsx @@ -7,7 +7,7 @@ import { useNavigation } from '../../context/Navigation/provider' import { mainPages } from '../../context/Navigation/types' import { useProject } from '../../context/Project/provider' import { GetAllLocalProjects } from '../../wailsjs/wailsjs/go/ipc/Channel' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { entities } from '../../wailsjs/wailsjs/go/models' import NewProjectModal from './NewProjectModal' import ProjectListModal from './ProjectListModal' @@ -17,7 +17,7 @@ const MainProject = () => { const [isProjectListModal, setIsProjectListModal] = useState(false) const [canPopoverBeOpen, setCanPopoverBeOpen] = useState(true) - const [avalibleProjects, setAvalibleProjects] = useState([]) + const [avalibleProjects, setAvalibleProjects] = useState([]) const { createNewProject, requestSelectProjectByName } = useProject() const { setSelectedMainPage } = useNavigation() diff --git a/frontend/components/project/ProjectListModal.tsx b/frontend/components/project/ProjectListModal.tsx index 3e9cd78..abd5deb 100644 --- a/frontend/components/project/ProjectListModal.tsx +++ b/frontend/components/project/ProjectListModal.tsx @@ -1,6 +1,6 @@ -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { entities } from '../../wailsjs/wailsjs/go/models' -type Props = { projects: ipc.Project[], onSelectProjectHandler: (projectName: string) => void } +type Props = { projects: entities.Project[], onSelectProjectHandler: (projectName: string) => void } const ProjectListModal = (props: Props) => { return ( diff --git a/frontend/components/workspace/LanguageSelect.tsx b/frontend/components/workspace/LanguageSelect.tsx index 25cf704..8e19067 100644 --- a/frontend/components/workspace/LanguageSelect.tsx +++ b/frontend/components/workspace/LanguageSelect.tsx @@ -5,17 +5,17 @@ import { useEffect, useState } from 'react' import { useProject } from '../../context/Project/provider' import classNames from '../../utils/classNames' import getSupportedLanguages from '../../utils/getSupportedLanguages' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { entities } from '../../wailsjs/wailsjs/go/models' type forAreaType = { shouldUpdateArea?: true, shouldUpdateDocument?: never } type forDocumentType = { shouldUpdateDocument?: true, shouldUpdateArea?: never } -type Props = (forAreaType | forDocumentType) & { defaultLanguage?: ipc.Language } +type Props = (forAreaType | forDocumentType) & { defaultLanguage?: entities.Language } const LanguageSelect = (props?: Props) => { const { requestUpdateDocument, getSelectedDocument } = useProject() - const [languages, setLanguages] = useState([]) + const [languages, setLanguages] = useState([]) const [query, setQuery] = useState('') - const [selectedLanguage, setSelectedLanguage] = useState(props?.defaultLanguage) + const [selectedLanguage, setSelectedLanguage] = useState(props?.defaultLanguage) const filteredLanguages = query === '' @@ -47,7 +47,7 @@ const LanguageSelect = (props?: Props) => { style={{'maxWidth': '240px', 'height': '30px'}} className="rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm" onChange={(event) => setQuery(event.target.value)} - displayValue={(language: ipc.Language) => language?.displayName} + displayValue={(language: entities.Language) => language?.displayName} placeholder='Document Language' /> diff --git a/frontend/components/workspace/Sidebar/navigationProps.ts b/frontend/components/workspace/Sidebar/navigationProps.ts index 3c14fc1..f357b1f 100644 --- a/frontend/components/workspace/Sidebar/navigationProps.ts +++ b/frontend/components/workspace/Sidebar/navigationProps.ts @@ -1,7 +1,7 @@ -import { ipc } from '../../../wailsjs/wailsjs/go/models' +import { entities } from '../../../wailsjs/wailsjs/go/models' import { SidebarGroup } from './types' -const getNavigationProps = (documents: ipc.Document[], groups: ipc.Group[]) : SidebarGroup[] => { +const getNavigationProps = (documents: entities.Document[], groups: entities.Group[]) : SidebarGroup[] => { const groupsWithDocuments = groups.map(g => { const childrenDocuments = documents .filter(d => d.groupId === g.id) diff --git a/frontend/context/Project/createAreaProviderMethods.ts b/frontend/context/Project/createAreaProviderMethods.ts index f1177f0..2ce0443 100644 --- a/frontend/context/Project/createAreaProviderMethods.ts +++ b/frontend/context/Project/createAreaProviderMethods.ts @@ -1,10 +1,10 @@ import { saveDocuments } from '../../useCases/saveData' import { GetProcessedAreasByDocumentId, RequestAddArea, RequestAddProcessedArea, RequestChangeAreaOrder, RequestDeleteAreaById, RequestUpdateArea } from '../../wailsjs/wailsjs/go/ipc/Channel' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { entities, ipc } from '../../wailsjs/wailsjs/go/models' import { AddAreaProps, AreaProps } from './types' type Dependencies = { - documents: ipc.Document[] + documents: entities.Document[] updateDocuments: () => Promise selectedDocumentId: string } @@ -12,7 +12,7 @@ type Dependencies = { const createAreaProviderMethods = (dependencies: Dependencies) => { const { documents, updateDocuments, selectedDocumentId } = dependencies - const getAreaById = (areaId: string): ipc.Area | undefined => ( + const getAreaById = (areaId: string): entities.Area | undefined => ( documents.map(d => d.areas).flat().find(a => a.id === areaId) ) @@ -29,7 +29,7 @@ const createAreaProviderMethods = (dependencies: Dependencies) => { } const getProcessedAreasByDocumentId = async (documentId: string) => { - let response: ipc.ProcessedArea[] = [] + let response: entities.ProcessedArea[] = [] try { response = await GetProcessedAreasByDocumentId(documentId) } catch (err) { @@ -38,15 +38,15 @@ const createAreaProviderMethods = (dependencies: Dependencies) => { return response } - const requestAddArea = async (documentId: string, area: AddAreaProps): Promise => { - const response = await RequestAddArea(documentId, new ipc.Area(area)) + const requestAddArea = async (documentId: string, area: AddAreaProps): Promise => { + const response = await RequestAddArea(documentId, new entities.Area(area)) if (response.id) await updateDocuments() saveDocuments() return response } - const requestUpdateArea = async (updatedArea: AreaProps): Promise => { - const response = await RequestUpdateArea(new ipc.Area(updatedArea)) + const requestUpdateArea = async (updatedArea: AreaProps): Promise => { + const response = await RequestUpdateArea(new entities.Area(updatedArea)) if (response.id) await updateDocuments() saveDocuments() @@ -60,7 +60,7 @@ const createAreaProviderMethods = (dependencies: Dependencies) => { return wasSuccessfulDeletion } - const requestAddProcessedArea = async (processedArea: ipc.ProcessedArea) => await RequestAddProcessedArea(processedArea) + const requestAddProcessedArea = async (processedArea: entities.ProcessedArea) => await RequestAddProcessedArea(processedArea) const requestChangeAreaOrder = async (areaId: string, newOrder: number) => { const response = await RequestChangeAreaOrder(areaId, newOrder) diff --git a/frontend/context/Project/createDocumentMethods.ts b/frontend/context/Project/createDocumentMethods.ts index 0acb077..f86b355 100644 --- a/frontend/context/Project/createDocumentMethods.ts +++ b/frontend/context/Project/createDocumentMethods.ts @@ -1,20 +1,20 @@ import { saveGroups } from '../../useCases/saveData' import { RequestAddDocument, RequestAddDocumentGroup, RequestChangeGroupOrder, RequestDeleteDocumentAndChildren, RequestUpdateDocument, RequestUpdateProcessedWordById } from '../../wailsjs/wailsjs/go/ipc/Channel' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { ipc, entities } from '../../wailsjs/wailsjs/go/models' import { UpdateDocumentRequest } from './types' type Dependencies = { selectedDocumentId: string - documents: ipc.Document[] + documents: entities.Document[] saveDocuments: () => Promise updateDocuments: () => Promise - groups: ipc.Group[] + groups: entities.Group[] } const createDocumentProviderMethods = (dependencies: Dependencies) => { const { selectedDocumentId, documents, saveDocuments, updateDocuments, groups } = dependencies - const getGroupById = (groupId: string): ipc.Group | undefined => ( + const getGroupById = (groupId: string): entities.Group | undefined => ( groups.find(g => g.id === groupId) ) @@ -42,7 +42,7 @@ const createDocumentProviderMethods = (dependencies: Dependencies) => { } const requestUpdateDocument = async (documentProps: UpdateDocumentRequest) => { - const response = await RequestUpdateDocument(new ipc.Document(documentProps)) + const response = await RequestUpdateDocument(new entities.Document(documentProps)) await updateDocuments() saveDocuments() return response diff --git a/frontend/context/Project/createSessionProviderMethods.ts b/frontend/context/Project/createSessionProviderMethods.ts index 2bc8f5b..f74ad3c 100644 --- a/frontend/context/Project/createSessionProviderMethods.ts +++ b/frontend/context/Project/createSessionProviderMethods.ts @@ -1,9 +1,9 @@ import { CreateNewProject, RequestChangeSessionProjectByName, RequestChooseUserAvatar, RequestUpdateCurrentUser } from '../../wailsjs/wailsjs/go/ipc/Channel' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { ipc, entities } from '../../wailsjs/wailsjs/go/models' import { UserProps } from './types' type Dependencies = { - updateSession: () => Promise + updateSession: () => Promise updateDocuments: () => Promise } @@ -17,7 +17,7 @@ const createSessionProviderMethods = (dependencies: Dependencies) => { } const requestUpdateCurrentUser = async (userProps: UserProps) => { - const response = await RequestUpdateCurrentUser(new ipc.User(userProps)) + const response = await RequestUpdateCurrentUser(new entities.User(userProps)) await updateSession() return response } diff --git a/frontend/context/Project/createUserMarkdownProviderMethods.ts b/frontend/context/Project/createUserMarkdownProviderMethods.ts index 16e8dcb..c77fb19 100644 --- a/frontend/context/Project/createUserMarkdownProviderMethods.ts +++ b/frontend/context/Project/createUserMarkdownProviderMethods.ts @@ -1,13 +1,13 @@ import { saveUserProcessedMarkdown } from '../../useCases/saveData' import { GetUserMarkdownByDocumentId, RequestUpdateDocumentUserMarkdown } from '../../wailsjs/wailsjs/go/ipc/Channel' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { ipc, entities } from '../../wailsjs/wailsjs/go/models' type Dependencies = {} const createUserMarkdownProviderMethods = (dependencies?: Dependencies) => { const requestUpdateDocumentUserMarkdown = async (documentId: string, markdown: string) => { - let response: ipc.UserMarkdown = new ipc.UserMarkdown() + let response = new entities.UserMarkdown() try { response = await RequestUpdateDocumentUserMarkdown(documentId, markdown) await saveUserProcessedMarkdown() @@ -17,8 +17,8 @@ const createUserMarkdownProviderMethods = (dependencies?: Dependencies) => { return response } - const getUserMarkdownByDocumentId = async (documentId: string): Promise => { - let response: ipc.UserMarkdown = new ipc.UserMarkdown({}) + const getUserMarkdownByDocumentId = async (documentId: string): Promise => { + let response = new entities.UserMarkdown({}) try { response = await GetUserMarkdownByDocumentId(documentId) } catch (err) { diff --git a/frontend/context/Project/makeDefaultProject.ts b/frontend/context/Project/makeDefaultProject.ts index 0f60498..0604fe3 100644 --- a/frontend/context/Project/makeDefaultProject.ts +++ b/frontend/context/Project/makeDefaultProject.ts @@ -1,33 +1,33 @@ -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { entities } from '../../wailsjs/wailsjs/go/models' import { ProjectContextType, UserProps } from './types' const makeDefaultProject = (): ProjectContextType => ({ id: '', - documents: [] as ipc.Document[], - groups: [] as ipc.Group[], + documents: [] as entities.Document[], + groups: [] as entities.Group[], selectedAreaId: '', selectedDocumentId: '', - getSelectedDocument: () => new ipc.Document(), + getSelectedDocument: () => new entities.Document(), getAreaById: (areaId) => undefined, - getProcessedAreasByDocumentId: (documentId) => Promise.resolve([new ipc.ProcessedArea()]), - requestAddProcessedArea: (processesArea) => Promise.resolve(new ipc.ProcessedArea()), - requestAddArea: (documentId, area) => Promise.resolve(new ipc.Area()), - requestUpdateArea: (updatedArea) => Promise.resolve(new ipc.Area()), + getProcessedAreasByDocumentId: (documentId) => Promise.resolve([new entities.ProcessedArea()]), + requestAddProcessedArea: (processesArea) => Promise.resolve(new entities.ProcessedArea()), + requestAddArea: (documentId, area) => Promise.resolve(new entities.Area()), + requestUpdateArea: (updatedArea) => Promise.resolve(new entities.Area()), requestDeleteAreaById: (areaId) => Promise.resolve(false), - requestAddDocument: (groupId, documentName) => Promise.resolve(new ipc.Document()), + requestAddDocument: (groupId, documentName) => Promise.resolve(new entities.Document()), requestDeleteDocumentById: (documentId) => Promise.resolve(false), - requestAddDocumentGroup: (groupName: string) => Promise.resolve(new ipc.Group()), - requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise.resolve(new ipc.UserMarkdown()), - getUserMarkdownByDocumentId: (documentId) => Promise.resolve(new ipc.UserMarkdown), + requestAddDocumentGroup: (groupName: string) => Promise.resolve(new entities.Group()), + requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise.resolve(new entities.UserMarkdown()), + getUserMarkdownByDocumentId: (documentId) => Promise.resolve(new entities.UserMarkdown), setSelectedAreaId: (id) => {}, setSelectedDocumentId: (id) => {}, - currentSession: new ipc.Session(), - createNewProject: (name: string) => Promise.resolve(new ipc.Session()), - requestUpdateCurrentUser: (updatedUserProps: UserProps) => Promise.resolve(new ipc.User()), + currentSession: new entities.Session(), + createNewProject: (name: string) => Promise.resolve(new entities.Session()), + requestUpdateCurrentUser: (updatedUserProps: UserProps) => Promise.resolve(new entities.User()), requestChooseUserAvatar: () => Promise.resolve(''), - requestUpdateDocument: ({}) => Promise.resolve(new ipc.Document), - requestChangeAreaOrder: (areaId: string, newOrder: number) => Promise.resolve(new ipc.Document()), - requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise.resolve(new ipc.Group()), + requestUpdateDocument: ({}) => Promise.resolve(new entities.Document), + requestChangeAreaOrder: (areaId: string, newOrder: number) => Promise.resolve(new entities.Document()), + requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise.resolve(new entities.Group()), getGroupById: (groupId) => undefined, requestSelectProjectByName: (projectName) => Promise.resolve(false), requestUpdateProcessedWordById: (wordId, newTestValue) => Promise.resolve(false), diff --git a/frontend/context/Project/provider.tsx b/frontend/context/Project/provider.tsx index deb8702..6c678e4 100644 --- a/frontend/context/Project/provider.tsx +++ b/frontend/context/Project/provider.tsx @@ -2,7 +2,7 @@ import { createContext, ReactNode, useContext, useEffect, useState } from 'react' import { GetCurrentSession, GetDocuments, } from '../../wailsjs/wailsjs/go/ipc/Channel' -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { entities } from '../../wailsjs/wailsjs/go/models' import { ProjectContextType, ProjectProps } from './types' import makeDefaultProject from './makeDefaultProject' import { saveDocuments } from '../../useCases/saveData' @@ -19,11 +19,11 @@ export function useProject() { type Props = { children: ReactNode, projectProps: ProjectProps } export function ProjectProvider({ children, projectProps }: Props) { - const [documents, setDocuments] = useState(projectProps.documents) - const [groups, setGroups] = useState(projectProps.groups) + const [documents, setDocuments] = useState(projectProps.documents) + const [groups, setGroups] = useState(projectProps.groups) const [selectedAreaId, setSelectedAreaId] = useState('') const [selectedDocumentId, setSelectedDocumentId] = useState('') - const [currentSession, setCurrentSession] = useState(new ipc.Session()) + const [currentSession, setCurrentSession] = useState(new entities.Session()) const updateDocuments = async () => { const response = await GetDocuments() diff --git a/frontend/context/Project/types.ts b/frontend/context/Project/types.ts index d9286c1..38c518a 100644 --- a/frontend/context/Project/types.ts +++ b/frontend/context/Project/types.ts @@ -1,9 +1,9 @@ -import { ipc } from '../../wailsjs/wailsjs/go/models' +import { ipc, entities } from '../../wailsjs/wailsjs/go/models' export type ProjectProps = { id: string, - documents: ipc.Document[], - groups: ipc.Group[], + documents: entities.Document[], + groups: entities.Group[], } export type AddAreaProps = { @@ -32,36 +32,36 @@ export type UpdateDocumentRequest = { groupId?: string, name?: string, path?: string, - areas?: ipc.Area[] - defaultLanguage?: ipc.Language + areas?: entities.Area[] + defaultLanguage?: entities.Language } export type ProjectContextType = { - getSelectedDocument: () => ipc.Document | undefined - getAreaById: (areaId: string) => ipc.Area | undefined - getProcessedAreasByDocumentId: (documentId: string) => Promise - requestAddProcessedArea: (processedArea: ipc.ProcessedArea) => Promise - requestAddArea: (documentId: string, area: AddAreaProps) => Promise - requestUpdateArea: (area: AreaProps) => Promise + getSelectedDocument: () => entities.Document | undefined + getAreaById: (areaId: string) => entities.Area | undefined + getProcessedAreasByDocumentId: (documentId: string) => Promise + requestAddProcessedArea: (processedArea: entities.ProcessedArea) => Promise + requestAddArea: (documentId: string, area: AddAreaProps) => Promise + requestUpdateArea: (area: AreaProps) => Promise requestDeleteAreaById: (areaId: string) => Promise - requestAddDocument: (groupId: string, documentName: string) => Promise + requestAddDocument: (groupId: string, documentName: string) => Promise requestDeleteDocumentById: (documentId: string) => Promise - requestAddDocumentGroup: (groupName: string) => Promise - requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise - getUserMarkdownByDocumentId: (documentId: string) => Promise + requestAddDocumentGroup: (groupName: string) => Promise + requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise + getUserMarkdownByDocumentId: (documentId: string) => Promise selectedAreaId: string setSelectedAreaId: (id: string) => void selectedDocumentId: string setSelectedDocumentId: (id: string) => void - currentSession: ipc.Session - createNewProject: (name: string) => Promise - requestUpdateCurrentUser: (updatedUserProps: UserProps) => Promise + currentSession: entities.Session + createNewProject: (name: string) => Promise + requestUpdateCurrentUser: (updatedUserProps: UserProps) => Promise requestChooseUserAvatar: () => Promise - requestUpdateDocument: (request: UpdateDocumentRequest) => Promise - requestChangeAreaOrder: (areaId: string, newOrder: number) => Promise - requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise - getGroupById: (groupId: string) => ipc.Group | undefined + requestUpdateDocument: (request: UpdateDocumentRequest) => Promise + requestChangeAreaOrder: (areaId: string, newOrder: number) => Promise + requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise + getGroupById: (groupId: string) => entities.Group | undefined requestSelectProjectByName: (projectName: string) => Promise requestUpdateProcessedWordById: (wordId: string, newTextValue: string) => Promise - getProcessedAreaById: (areaId: string) => Promise + getProcessedAreaById: (areaId: string) => Promise } & ProjectProps \ No newline at end of file diff --git a/frontend/useCases/processImageArea.ts b/frontend/useCases/processImageArea.ts index 89d788b..33a1d69 100644 --- a/frontend/useCases/processImageArea.ts +++ b/frontend/useCases/processImageArea.ts @@ -1,6 +1,6 @@ import { createScheduler, createWorker } from 'tesseract.js' import { GetAreaById, GetDocumentById, RequestAddProcessedArea, RequestSaveProcessedTextCollection } from '../wailsjs/wailsjs/go/ipc/Channel' -import { ipc } from '../wailsjs/wailsjs/go/models' +import { entities } from '../wailsjs/wailsjs/go/models' import loadImage from './loadImage' import { saveProcessedText } from './saveData' @@ -31,27 +31,27 @@ const processImageArea = async (documentId: string, areaId: string) => { } }) - const addProcessesAreaRequest = await RequestAddProcessedArea(new ipc.ProcessedArea({ + const addProcessesAreaRequest = await RequestAddProcessedArea(new entities.ProcessedArea({ id: foundArea.id, documentId, order: foundArea.order, fullText: result.data.text, - lines: result.data.lines.map((l: any) => new ipc.ProcessedLine({ + lines: result.data.lines.map((l: any) => new entities.ProcessedLine({ fullText: l.text, - words: l.words.map((w: any) => new ipc.ProcessedWord({ + words: l.words.map((w: any) => new entities.ProcessedWord({ fullText: w.text, direction: w.direction, confidence: w.confidence, - boundingBox: new ipc.ProcessedBoundingBox({ + boundingBox: new entities.ProcessedBoundingBox({ x0: w.bbox.x0, y0: w.bbox.y0, x1: w.bbox.x1, y1: w.bbox.y1, }), - symbols: w.symbols.map((s: any) => new ipc.ProcessedSymbol({ + symbols: w.symbols.map((s: any) => new entities.ProcessedSymbol({ fullText: s.text, confidence: s.confidence, - boundingBox: new ipc.ProcessedBoundingBox({ + boundingBox: new entities.ProcessedBoundingBox({ x0: s.bbox.x0, y0: s.bbox.y0, x1: s.bbox.x1, diff --git a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts index 9a00487..5522215 100755 --- a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts +++ b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts @@ -1,40 +1,41 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +import {entities} from '../models'; import {ipc} from '../models'; -export function CreateNewProject(arg1:string):Promise; +export function CreateNewProject(arg1:string):Promise; -export function GetAllLocalProjects():Promise>; +export function GetAllLocalProjects():Promise>; -export function GetAreaById(arg1:string):Promise; +export function GetAreaById(arg1:string):Promise; -export function GetCurrentSession():Promise; +export function GetCurrentSession():Promise; -export function GetCurrentUser():Promise; +export function GetCurrentUser():Promise; -export function GetDocumentById(arg1:string):Promise; +export function GetDocumentById(arg1:string):Promise; export function GetDocuments():Promise; -export function GetProcessedAreasByDocumentId(arg1:string):Promise>; +export function GetProcessedAreasByDocumentId(arg1:string):Promise>; -export function GetProjectByName(arg1:string):Promise; +export function GetProjectByName(arg1:string):Promise; -export function GetSuppportedLanguages():Promise>; +export function GetSuppportedLanguages():Promise>; -export function GetUserMarkdownByDocumentId(arg1:string):Promise; +export function GetUserMarkdownByDocumentId(arg1:string):Promise; -export function RequestAddArea(arg1:string,arg2:ipc.Area):Promise; +export function RequestAddArea(arg1:string,arg2:entities.Area):Promise; -export function RequestAddDocument(arg1:string,arg2:string):Promise; +export function RequestAddDocument(arg1:string,arg2:string):Promise; -export function RequestAddDocumentGroup(arg1:string):Promise; +export function RequestAddDocumentGroup(arg1:string):Promise; -export function RequestAddProcessedArea(arg1:ipc.ProcessedArea):Promise; +export function RequestAddProcessedArea(arg1:entities.ProcessedArea):Promise; -export function RequestChangeAreaOrder(arg1:string,arg2:number):Promise; +export function RequestChangeAreaOrder(arg1:string,arg2:number):Promise; -export function RequestChangeGroupOrder(arg1:string,arg2:number):Promise; +export function RequestChangeGroupOrder(arg1:string,arg2:number):Promise; export function RequestChangeSessionProjectByName(arg1:string):Promise; @@ -52,12 +53,12 @@ export function RequestSaveLocalUserProcessedMarkdownCollection():Promise; -export function RequestUpdateArea(arg1:ipc.Area):Promise; +export function RequestUpdateArea(arg1:entities.Area):Promise; -export function RequestUpdateCurrentUser(arg1:ipc.User):Promise; +export function RequestUpdateCurrentUser(arg1:entities.User):Promise; -export function RequestUpdateDocument(arg1:ipc.Document):Promise; +export function RequestUpdateDocument(arg1:entities.Document):Promise; -export function RequestUpdateDocumentUserMarkdown(arg1:string,arg2:string):Promise; +export function RequestUpdateDocumentUserMarkdown(arg1:string,arg2:string):Promise; export function RequestUpdateProcessedWordById(arg1:string,arg2:string):Promise; diff --git a/frontend/wailsjs/wailsjs/go/models.ts b/frontend/wailsjs/wailsjs/go/models.ts index ec72d83..3170e4f 100755 --- a/frontend/wailsjs/wailsjs/go/models.ts +++ b/frontend/wailsjs/wailsjs/go/models.ts @@ -1,4 +1,4 @@ -export namespace ipc { +export namespace entities { export class Language { displayName: string; @@ -122,39 +122,6 @@ export namespace ipc { this.order = source["order"]; } } - export class GetDocumentsResponse { - documents: Document[]; - groups: Group[]; - - static createFrom(source: any = {}) { - return new GetDocumentsResponse(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.documents = this.convertValues(source["documents"], Document); - this.groups = this.convertValues(source["groups"], Group); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class User { id: string; @@ -507,3 +474,40 @@ export namespace ipc { } +export namespace ipc { + + export class GetDocumentsResponse { + documents: entities.Document[]; + groups: entities.Group[]; + + static createFrom(source: any = {}) { + return new GetDocumentsResponse(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.documents = this.convertValues(source["documents"], entities.Document); + this.groups = this.convertValues(source["groups"], entities.Group); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + +} + diff --git a/ipc/Documents.go b/ipc/Documents.go index 8c2489c..bfdb1d4 100644 --- a/ipc/Documents.go +++ b/ipc/Documents.go @@ -3,47 +3,23 @@ package ipc import ( "sort" app "textualize/core/App" - consts "textualize/core/Consts" document "textualize/core/Document" session "textualize/core/Session" + "textualize/entities" storage "textualize/storage" - storageEntity "textualize/storage/Entities" "github.com/google/uuid" "github.com/wailsapp/wails/v2/pkg/runtime" ) type GetDocumentsResponse struct { - Documents []Document `json:"documents"` - Groups []Group `json:"groups"` + Documents []entities.Document `json:"documents"` + Groups []entities.Group `json:"groups"` } -func (c *Channel) GetDocumentById(id string) Document { +func (c *Channel) GetDocumentById(id string) entities.Document { foundDocument := document.GetDocumentCollection().GetDocumentById(id) - var jsonAreas []Area - - for _, a := range foundDocument.Areas { - jsonAreas = append(jsonAreas, Area{ - Id: a.Id, - Name: a.Name, - StartX: a.StartX, - StartY: a.StartY, - EndX: a.EndX, - EndY: a.EndY, - Order: a.Order, - Language: Language(a.Language), - }) - } - response := Document{ - Id: foundDocument.Id, - Name: foundDocument.Name, - GroupId: foundDocument.GroupId, - Path: foundDocument.Path, - ProjectId: foundDocument.ProjectId, - Areas: jsonAreas, - DefaultLanguage: Language(foundDocument.DefaultLanguage), - } - return response + return entities.Document(*foundDocument) } func (c *Channel) GetDocuments() GetDocumentsResponse { @@ -51,63 +27,33 @@ func (c *Channel) GetDocuments() GetDocumentsResponse { groups := document.GetGroupCollection().Groups response := GetDocumentsResponse{ - Groups: make([]Group, 0), - Documents: make([]Document, 0), + Groups: make([]entities.Group, 0), + Documents: make([]entities.Document, 0), } for _, d := range documents { - jsonAreas := make([]Area, 0) - for _, a := range d.Areas { - jsonAreas = append(jsonAreas, Area{ - Id: a.Id, - Name: a.Name, - StartX: a.StartX, - StartY: a.StartY, - EndX: a.EndX, - EndY: a.EndY, - Order: a.Order, - Language: Language(a.Language), - }) - } - - sort.Slice(jsonAreas, func(i, j int) bool { - return jsonAreas[i].Order < jsonAreas[j].Order + sortedAreas := d.Areas + sort.Slice(sortedAreas, func(i, j int) bool { + return sortedAreas[i].Order < sortedAreas[j].Order }) - jsonDocument := Document{ - Id: d.Id, - GroupId: d.GroupId, - Name: d.Name, - Path: d.Path, - ProjectId: d.ProjectId, - Areas: jsonAreas, - DefaultLanguage: Language(d.DefaultLanguage), - } + jsonDocument := entities.Document(d) + d.Areas = sortedAreas response.Documents = append(response.Documents, jsonDocument) } - jsonGroups := make([]Group, 0) - for _, g := range groups { - jsonGroup := Group{ - Id: g.Id, - ParentId: g.ParentId, - ProjectId: g.ProjectId, - Name: g.Name, - Order: g.Order, - } - jsonGroups = append(jsonGroups, jsonGroup) + if len(groups) > 0 { + sortedGroups := groups + sort.Slice(sortedGroups, func(i, j int) bool { + return sortedGroups[i].Order < sortedGroups[j].Order + }) + response.Groups = sortedGroups } - sort.Slice(jsonGroups, func(i, j int) bool { - return jsonGroups[i].Order < jsonGroups[j].Order - }) - - response.Groups = jsonGroups - return response } -func (c *Channel) RequestAddDocument(groupId string, documentName string) Document { +func (c *Channel) RequestAddDocument(groupId string, documentName string) entities.Document { filePath, err := runtime.OpenFileDialog(app.GetInstance().Context, runtime.OpenDialogOptions{ Title: "Select an Image", Filters: []runtime.FileFilter{ @@ -120,7 +66,7 @@ func (c *Channel) RequestAddDocument(groupId string, documentName string) Docume if err != nil { runtime.LogError(app.GetInstance().Context, err.Error()) - return Document{} + return entities.Document{} } newDocument := document.Entity{ @@ -133,15 +79,7 @@ func (c *Channel) RequestAddDocument(groupId string, documentName string) Docume document.GetDocumentCollection().AddDocument(newDocument) - documentResponse := Document{ - Id: newDocument.Id, - Name: newDocument.Name, - Path: newDocument.Path, - GroupId: newDocument.GroupId, - ProjectId: newDocument.ProjectId, - } - - return documentResponse + return entities.Document(newDocument) } func (c *Channel) deleteDocumentById(documentId string) bool { @@ -164,11 +102,11 @@ func (c *Channel) deleteDocumentById(documentId string) bool { return true } -func (c *Channel) RequestUpdateDocumentUserMarkdown(documentId string, markdown string) UserMarkdown { +func (c *Channel) RequestUpdateDocumentUserMarkdown(documentId string, markdown string) entities.UserMarkdown { markdownCollection := document.GetUserMarkdownCollection() markdownToUpdate := markdownCollection.GetUserMarkdownByDocumentId(documentId) - newMarkdown := document.UserMarkdown{ + newMarkdown := entities.UserMarkdown{ DocumentId: documentId, Value: markdown, } @@ -179,11 +117,7 @@ func (c *Channel) RequestUpdateDocumentUserMarkdown(documentId string, markdown } updatedMarkdown := markdownCollection.UpdateUserMarkdown(newMarkdown) - return UserMarkdown{ - Id: updatedMarkdown.Id, - DocumentId: updatedMarkdown.DocumentId, - Value: updatedMarkdown.Value, - } + return entities.UserMarkdown(updatedMarkdown) } func (c *Channel) deleteDocumentUserMarkdown(documentId string) bool { @@ -206,26 +140,15 @@ func (c *Channel) deleteDocumentUserMarkdown(documentId string) bool { return true } -func (c *Channel) GetUserMarkdownByDocumentId(documentId string) UserMarkdown { +func (c *Channel) GetUserMarkdownByDocumentId(documentId string) entities.UserMarkdown { foundUserMarkdown := document.GetUserMarkdownCollection().GetUserMarkdownByDocumentId((documentId)) - - response := UserMarkdown{} - - if foundUserMarkdown != nil { - response = UserMarkdown{ - Id: foundUserMarkdown.Id, - DocumentId: foundUserMarkdown.DocumentId, - Value: foundUserMarkdown.Value, - } - } - - return response + return entities.UserMarkdown(*foundUserMarkdown) } -func (c *Channel) RequestAddDocumentGroup(name string) Group { +func (c *Channel) RequestAddDocumentGroup(name string) entities.Group { groupCollection := document.GetGroupCollection() - newGroup := document.Group{ + newGroup := entities.Group{ Id: uuid.NewString(), Name: name, ProjectId: session.GetInstance().Project.Id, @@ -234,60 +157,41 @@ func (c *Channel) RequestAddDocumentGroup(name string) Group { groupCollection.AddDocumentGroup(newGroup) - response := Group{ - Id: newGroup.Id, - Name: newGroup.Name, - ParentId: newGroup.ParentId, - ProjectId: newGroup.ProjectId, - Order: newGroup.Order, - } - - return response + return newGroup } -func (c *Channel) RequestChangeGroupOrder(groupId string, newOrder int) Group { +func (c *Channel) RequestChangeGroupOrder(groupId string, newOrder int) entities.Group { groupCollection := document.GetGroupCollection() for _, g := range groupCollection.Groups { if g.Id == groupId { - // document.GetGroupCollection().Groups[index].Order = newOrder document.GetGroupCollection().GetGroupById(groupId).Order = newOrder } else if g.Order >= newOrder { - // document.GetGroupCollection().Groups[index].Order = g.Order + 1 document.GetGroupCollection().GetGroupById(groupId).Order = g.Order + 1 } } - return Group(*document.GetGroupCollection().GetGroupById(groupId)) + return *document.GetGroupCollection().GetGroupById(groupId) } -func (c *Channel) GetAreaById(areaId string) Area { +func (c *Channel) GetAreaById(areaId string) entities.Area { foundDocument := document.GetDocumentCollection().GetDocumentByAreaId(areaId) if len(foundDocument.Areas) == 0 { - return Area{} + return entities.Area{} } - var foundArea document.Area + var foundArea entities.Area for i, a := range foundDocument.Areas { if a.Id == areaId { foundArea = foundDocument.Areas[i] } } - return Area{ - Id: foundArea.Id, - Name: foundArea.Name, - StartX: foundArea.StartX, - EndX: foundArea.EndX, - StartY: foundArea.StartY, - EndY: foundArea.EndY, - Order: foundArea.Order, - Language: Language(foundArea.Language), - } + return foundArea } -func (c *Channel) RequestAddArea(documentId string, area Area) Area { +func (c *Channel) RequestAddArea(documentId string, area entities.Area) entities.Area { foundDocument := document.GetDocumentCollection().GetDocumentById(documentId) var id string @@ -302,7 +206,7 @@ func (c *Channel) RequestAddArea(documentId string, area Area) Area { order = len(foundDocument.Areas) } - newArea := document.Area{ + newArea := entities.Area{ Id: id, Name: area.Name, StartX: area.StartX, @@ -310,30 +214,26 @@ func (c *Channel) RequestAddArea(documentId string, area Area) Area { StartY: area.StartY, EndY: area.EndY, Order: order, - Language: consts.Language(area.Language), + Language: entities.Language(area.Language), } foundDocument.AddArea(newArea) - responseArea := area - responseArea.Id = id - - return responseArea + return newArea } -func (c *Channel) RequestUpdateArea(updatedArea Area) Area { +func (c *Channel) RequestUpdateArea(updatedArea entities.Area) entities.Area { documentOfArea := document.GetDocumentCollection().GetDocumentByAreaId(updatedArea.Id) if documentOfArea.Id == "" { - return Area{} + return entities.Area{} } areaToUpdate := documentOfArea.GetAreaById(updatedArea.Id) if areaToUpdate.Id == "" { - return Area{} + return entities.Area{} } - // TODO: add more prop changes when needed if updatedArea.Name != "" { areaToUpdate.Name = updatedArea.Name } @@ -341,16 +241,7 @@ func (c *Channel) RequestUpdateArea(updatedArea Area) Area { areaToUpdate.Order = updatedArea.Order } - return Area{ - Id: areaToUpdate.Id, - Name: areaToUpdate.Name, - StartX: areaToUpdate.StartX, - StartY: areaToUpdate.StartY, - EndX: areaToUpdate.EndX, - EndY: areaToUpdate.EndY, - Order: areaToUpdate.Order, - Language: Language(areaToUpdate.Language), - } + return *areaToUpdate } func (c *Channel) RequestDeleteAreaById(areaId string) bool { @@ -379,11 +270,11 @@ func (c *Channel) RequestDeleteAreaById(areaId string) bool { } -func (c *Channel) RequestUpdateDocument(updatedDocument Document) Document { +func (c *Channel) RequestUpdateDocument(updatedDocument entities.Document) entities.Document { documentToUpdate := document.GetDocumentCollection().GetDocumentById(updatedDocument.Id) if documentToUpdate == nil { - return Document{} + return entities.Document{} } if updatedDocument.Id != "" { @@ -399,20 +290,20 @@ func (c *Channel) RequestUpdateDocument(updatedDocument Document) Document { documentToUpdate.Path = updatedDocument.Path } if updatedDocument.DefaultLanguage.DisplayName != "" { - documentToUpdate.DefaultLanguage = consts.Language(updatedDocument.DefaultLanguage) + documentToUpdate.DefaultLanguage = updatedDocument.DefaultLanguage } return updatedDocument } -func (c *Channel) RequestChangeAreaOrder(areaId string, newOrder int) Document { +func (c *Channel) RequestChangeAreaOrder(areaId string, newOrder int) entities.Document { documentOfArea := document.GetDocumentCollection().GetDocumentByAreaId((areaId)) if documentOfArea == nil { - return Document{} + return entities.Document{} } - var foundArea document.Area + var foundArea entities.Area for _, a := range documentOfArea.Areas { if a.Id == areaId { foundArea = a @@ -421,7 +312,7 @@ func (c *Channel) RequestChangeAreaOrder(areaId string, newOrder int) Document { } if foundArea.Id == "" { - return Document{} + return entities.Document{} } processedAreasCollection := document.GetProcessedAreaCollection() @@ -455,37 +346,18 @@ func (c *Channel) RequestSaveDocumentCollection() bool { return false } - var documentsToWrite []storageEntity.Document - for _, d := range documentCollection.Documents { - var areasToWrite []storageEntity.Area - for _, a := range d.Areas { - areasToWrite = append(areasToWrite, storageEntity.Area{ - Id: a.Id, - Name: a.Name, - StartX: a.StartX, - StartY: a.StartY, - EndX: a.EndX, - EndY: a.EndY, - Language: storageEntity.Language(a.Language), - Order: a.Order, - }) - } - - documentsToWrite = append(documentsToWrite, storageEntity.Document{ - Id: d.Id, - GroupId: d.GroupId, - Name: d.Name, - Path: d.Path, - ProjectId: d.ProjectId, - Areas: areasToWrite, - DefaultLanguage: storageEntity.Language(d.DefaultLanguage), - }) + documentCount := len(documentCollection.Documents) + writableDocuments := make([]entities.Document, documentCount) + for i := 0; i < documentCount; i++ { + writableDocuments[i] = entities.Document(documentCollection.Documents[i]) } - successfulWrite := storage.GetDriver().WriteDocumentCollection(storageEntity.DocumentCollection{ - Documents: documentsToWrite, - ProjectId: fullProject.Id, - }, projectName) + successfulWrite := storage.GetDriver().WriteDocumentCollection( + entities.DocumentCollection{ + ProjectId: fullProject.Id, + Documents: writableDocuments, + }, + projectName) return successfulWrite } @@ -500,15 +372,16 @@ func (c *Channel) RequestSaveGroupCollection() bool { return false } - var groupsToWrite []storageEntity.Group - for _, g := range groupCollection.Groups { - groupsToWrite = append(groupsToWrite, storageEntity.Group(g)) + groupCount := len(groupCollection.Groups) + writableGroups := make([]entities.Group, groupCount) + for i := 0; i < groupCount; i++ { + writableGroups[i] = entities.Group(groupCollection.Groups[i]) } - successfulWrite := storage.GetDriver().WriteGroupCollection(storageEntity.GroupCollection{ + successfulWrite := storage.GetDriver().WriteGroupCollection(entities.GroupCollection{ Id: groupCollection.Id, ProjectId: groupCollection.ProjectId, - Groups: groupsToWrite, + Groups: writableGroups, }, projectName) return successfulWrite @@ -518,53 +391,18 @@ func (c *Channel) RequestSaveProcessedTextCollection() bool { processedAreaCollection := document.GetProcessedAreaCollection() projectName := c.GetCurrentSession().Project.Name - areasToWrite := make([]storageEntity.ProcessedArea, 0) - for _, a := range processedAreaCollection.Areas { - linesOfAreaToWrite := make([]storageEntity.ProcessedLine, 0) - for _, l := range a.Lines { - wordsOfLineToWrite := make([]storageEntity.ProcessedWord, 0) - - for _, w := range l.Words { - symbolsOfWordToWrite := make([]storageEntity.ProcessedSymbol, 0) - - for _, s := range w.Symbols { - symbolsOfWordToWrite = append(symbolsOfWordToWrite, storageEntity.ProcessedSymbol{ - Text: s.Text, - Confidence: s.Confidence, - BoundingBox: storageEntity.ProcessedBoundingBox(s.BoundingBox), - }) - } - - wordsOfLineToWrite = append(wordsOfLineToWrite, storageEntity.ProcessedWord{ - Id: w.Id, - FullText: w.FullText, - Confidence: w.Confidence, - Direction: w.Direction, - BoundingBox: storageEntity.ProcessedBoundingBox(w.BoundingBox), - Symbols: symbolsOfWordToWrite, - }) - } - - linesOfAreaToWrite = append(linesOfAreaToWrite, storageEntity.ProcessedLine{ - FullText: l.FullText, - Words: wordsOfLineToWrite, - }) - } - - areasToWrite = append(areasToWrite, storageEntity.ProcessedArea{ - Id: a.Id, - DocumentId: a.DocumentId, - FullText: a.FullText, - Order: a.Order, - Lines: linesOfAreaToWrite, - }) + processedAreasCount := len(processedAreaCollection.Areas) + writableProcessedAreasAreas := make([]entities.ProcessedArea, processedAreasCount) + for i := 0; i < processedAreasCount; i++ { + writableProcessedAreasAreas[i] = entities.ProcessedArea(processedAreaCollection.Areas[i]) } - processedAreaCollectionToWrite := storageEntity.ProcessedTextCollection{ - Areas: areasToWrite, - } - - successfulWrite := storage.GetDriver().WriteProcessedTextCollection(processedAreaCollectionToWrite, projectName) + successfulWrite := storage.GetDriver().WriteProcessedTextCollection( + entities.ProcessedTextCollection{ + Areas: writableProcessedAreasAreas, + }, + projectName, + ) return successfulWrite } @@ -578,14 +416,18 @@ func (c *Channel) RequestSaveLocalUserProcessedMarkdownCollection() bool { return false } - var valuesToWrite []storageEntity.ProcessedUserMarkdown - for _, v := range userProcessedMarkdownCollection.Values { - valuesToWrite = append(valuesToWrite, storageEntity.ProcessedUserMarkdown(v)) + groupCount := len(userProcessedMarkdownCollection.Values) + writableMarkdownValues := make([]entities.ProcessedUserMarkdown, groupCount) + for i := 0; i < groupCount; i++ { + writableMarkdownValues[i] = entities.ProcessedUserMarkdown(userProcessedMarkdownCollection.Values[i]) } - successfulWrite := storage.GetDriver().WriteProcessedUserMarkdownCollection(storageEntity.ProcessedUserMarkdownCollection{ - Values: valuesToWrite, - }, projectName) + successfulWrite := storage.GetDriver().WriteProcessedUserMarkdownCollection( + entities.ProcessedUserMarkdownCollection{ + Values: writableMarkdownValues, + }, + projectName, + ) return successfulWrite } diff --git a/ipc/JsonEntities.go b/ipc/JsonEntities.go deleted file mode 100644 index 56a7d48..0000000 --- a/ipc/JsonEntities.go +++ /dev/null @@ -1,128 +0,0 @@ -package ipc - -type Document struct { - Id string `json:"id"` - GroupId string `json:"groupId"` - Name string `json:"name"` - Path string `json:"path"` - ProjectId string `json:"projectId"` - Areas []Area `json:"areas"` - DefaultLanguage Language `json:"defaultLanguage"` -} - -type DocumentCollection struct { - Documents []Document `json:"documents"` - ProjectId string `json:"projectId"` -} - -type Group struct { - Id string `json:"id"` - ParentId string `json:"parentId"` - ProjectId string `json:"projectId"` - Name string `json:"name"` - Order int `json:"order"` -} - -type GroupCollection struct { - Id string `json:"id"` - Groups []Group `json:"groups"` - ProjectId string `json:"projectId"` -} - -type Area struct { - Id string `json:"id"` - Name string `json:"name"` - StartX int `json:"startX"` - StartY int `json:"startY"` - EndX int `json:"endX"` - EndY int `json:"endY"` - Language Language `json:"language"` - Order int `json:"order"` -} - -type ProcessedBoundingBox struct { - X0 int32 `json:"x0"` - Y0 int32 `json:"y0"` - X1 int32 `json:"x1"` - Y1 int32 `json:"y1"` -} - -type ProcessedSymbol struct { - Text string `json:"text"` - Confidence float32 `json:"confidence"` - BoundingBox ProcessedBoundingBox `json:"boundingBox"` -} - -type ProcessedWord struct { - Id string `json:"id"` - FullText string `json:"fullText"` - Symbols []ProcessedSymbol `json:"symbols"` - Confidence float32 `json:"confidence"` - Direction string `json:"direction"` - BoundingBox ProcessedBoundingBox `json:"boundingBox"` -} - -type ProcessedLine struct { - FullText string `json:"fullText"` - Words []ProcessedWord `json:"words"` -} - -type ProcessedArea struct { - Id string `json:"id"` - DocumentId string `json:"documentId"` - FullText string `json:"fullText"` - Order int `json:"order"` - Lines []ProcessedLine `json:"lines"` -} - -type UserMarkdown struct { - Id string `json:"id"` - DocumentId string `json:"documentId"` - Value string `json:"value"` -} - -type UserMarkdownCollection struct { - Values []UserMarkdown `json:"values"` -} - -type User struct { - Id string `json:"id"` - LocalId string `json:"localId"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` - AvatarPath string `json:"avatarPath"` - AuthToken string `json:"authToken"` - Email string `json:"email"` -} - -type Organization struct { - Id string `json:"id"` - Name string `json:"name"` - LogoPath string `json:"logoPath"` - Users []User `json:"users"` -} - -type Project struct { - Id string `json:"id"` - OrganizationId string `json:"organizationId"` - Name string `json:"name"` - Settings ProjectSettings `json:"settings"` -} - -type ProjectSettings struct { - DefaultProcessLanguage Language `json:"defaultProcessLanguage"` - DefaultTranslateTargetLanguage Language `json:"defaultTranslateTargetLanguage"` - IsHosted bool `json:"isHosted"` -} - -type Session struct { - Project Project `json:"project"` - Organization Organization `json:"organization"` - User User `json:"user"` -} - -type Language struct { - DisplayName string `json:"displayName"` - ProcessCode string `json:"processCode"` - TranslateCode string `json:"translateCode"` -} diff --git a/ipc/ProcessedDocument.go b/ipc/ProcessedDocument.go index 220885e..13a480a 100644 --- a/ipc/ProcessedDocument.go +++ b/ipc/ProcessedDocument.go @@ -3,180 +3,39 @@ package ipc import ( "sort" document "textualize/core/Document" + "textualize/entities" "github.com/google/uuid" ) -func serializeBoundingBox(bbox document.ProcessedBoundingBox) ProcessedBoundingBox { - return ProcessedBoundingBox{ - X0: bbox.X0, - Y0: bbox.Y0, - X1: bbox.X1, - Y1: bbox.Y1, - } -} - -func serializeSymbol(symbol document.ProcessedSymbol) ProcessedSymbol { - return ProcessedSymbol{ - Text: symbol.Text, - Confidence: symbol.Confidence, - BoundingBox: serializeBoundingBox(symbol.BoundingBox), - } -} - -func serialzeWord(word document.ProcessedWord) ProcessedWord { - var symbols []ProcessedSymbol - - for _, symbol := range word.Symbols { - symbols = append(symbols, serializeSymbol(symbol)) - } - - return ProcessedWord{ - Id: word.Id, - FullText: word.FullText, - Symbols: symbols, - Confidence: word.Confidence, - Direction: word.Direction, - BoundingBox: serializeBoundingBox(word.BoundingBox), - } -} - -func serializeLine(line document.ProcessedLine) ProcessedLine { - var words []ProcessedWord - - for _, word := range line.Words { - words = append(words, serialzeWord((word))) - } - - return ProcessedLine{ - FullText: line.FullText, - Words: words, - } -} - -func serializeProcessedArea(area document.ProcessedArea) ProcessedArea { - var lines []ProcessedLine - - for _, line := range area.Lines { - lines = append(lines, serializeLine(line)) - } - - return ProcessedArea{ - Id: area.Id, - DocumentId: area.DocumentId, - FullText: area.FullText, - Order: area.Order, - Lines: lines, - } -} - -func (c *Channel) GetProcessedAreasByDocumentId(id string) []ProcessedArea { +func (c *Channel) GetProcessedAreasByDocumentId(id string) []entities.ProcessedArea { areas := document.GetProcessedAreaCollection().GetAreasByDocumentId(id) - var response []ProcessedArea - - for _, a := range areas { - response = append(response, serializeProcessedArea(*a)) + areaCount := len(areas) + readableAreas := make([]entities.ProcessedArea, areaCount) + for i := 0; i < areaCount; i++ { + readableAreas[i] = entities.ProcessedArea(*areas[i]) } - sort.Slice(response, func(i, j int) bool { - return response[i].Order < response[j].Order + sortedAreas := readableAreas + sort.Slice(sortedAreas, func(i, j int) bool { + return sortedAreas[i].Order < sortedAreas[j].Order }) - return response + return sortedAreas } -func deserializeBoundingBox(bbox ProcessedBoundingBox) document.ProcessedBoundingBox { - return document.ProcessedBoundingBox{ - X0: bbox.X0, - Y0: bbox.Y0, - X1: bbox.X1, - Y1: bbox.Y1, - } -} +func (c *Channel) RequestAddProcessedArea(processedArea entities.ProcessedArea) entities.ProcessedArea { -func deserializeSymbol(symbol ProcessedSymbol) document.ProcessedSymbol { - return document.ProcessedSymbol{ - Text: symbol.Text, - Confidence: symbol.Confidence, - BoundingBox: deserializeBoundingBox(symbol.BoundingBox), - } -} - -func deserialzeWord(word ProcessedWord) document.ProcessedWord { - var symbols []document.ProcessedSymbol - - for _, symbol := range word.Symbols { - symbols = append(symbols, deserializeSymbol(symbol)) - } - var wordId string - if word.Id == "" { - wordId = uuid.NewString() - } else { - wordId = word.Id + for lineIndex, line := range processedArea.Lines { + for wordIndex, word := range line.Words { + if word.Id == "" { + processedArea.Lines[lineIndex].Words[wordIndex].Id = uuid.NewString() + } + } } - return document.ProcessedWord{ - Id: wordId, - FullText: word.FullText, - Symbols: symbols, - Confidence: word.Confidence, - Direction: word.Direction, - BoundingBox: deserializeBoundingBox(word.BoundingBox), - } -} - -func deserializeLine(line ProcessedLine) document.ProcessedLine { - var words []document.ProcessedWord - - for _, word := range line.Words { - words = append(words, deserialzeWord((word))) - } - - return document.ProcessedLine{ - FullText: line.FullText, - Words: words, - } -} - -func deserializeProcessedArea(area ProcessedArea) document.ProcessedArea { - var lines []document.ProcessedLine - - for _, line := range area.Lines { - lines = append(lines, deserializeLine(line)) - } - - return document.ProcessedArea{ - Id: area.Id, - DocumentId: area.DocumentId, - Order: area.Order, - FullText: area.FullText, - Lines: lines, - } -} - -func (c *Channel) RequestAddProcessedArea(processedArea ProcessedArea) ProcessedArea { - // doesAreaAlreadyExist := false - // processedAreasOfDocuments := document.GetProcessedAreaCollection().GetAreasByDocumentId(processedArea.DocumentId) - - // for _, a := range processedAreasOfDocuments { - // if a.Order == processedArea.Order { - // doesAreaAlreadyExist = true - // break - // } - // } - - deserializedProcessedArea := deserializeProcessedArea(processedArea) - - // if doesAreaAlreadyExist { - // storedProcessedArea := document.GetProcessedAreaCollection().GetAreaById(processedArea.Id) - // if storedProcessedArea.Id != "" { - // storedProcessedArea = &deserializedProcessedArea - // } - // } else { - document.GetProcessedAreaCollection().AddProcessedArea((deserializedProcessedArea)) - // } - + document.GetProcessedAreaCollection().AddProcessedArea(processedArea) return processedArea } @@ -214,7 +73,7 @@ func (c *Channel) RequestUpdateProcessedWordById(wordId string, newTextValue str } wordProps := areas[areaOfWordIndex].Lines[lineOfWordIndex].Words[foundWordIndex] - areas[areaOfWordIndex].Lines[lineOfWordIndex].Words[foundWordIndex] = document.ProcessedWord{ + areas[areaOfWordIndex].Lines[lineOfWordIndex].Words[foundWordIndex] = entities.ProcessedWord{ Id: wordProps.Id, Direction: wordProps.Direction, FullText: newTextValue, diff --git a/ipc/Session.go b/ipc/Session.go index 9d046e5..3bb4b1d 100644 --- a/ipc/Session.go +++ b/ipc/Session.go @@ -5,40 +5,40 @@ import ( consts "textualize/core/Consts" document "textualize/core/Document" session "textualize/core/Session" + "textualize/entities" storage "textualize/storage" - storageEntity "textualize/storage/Entities" "github.com/wailsapp/wails/v2/pkg/runtime" "github.com/google/uuid" ) -func (c *Channel) GetCurrentSession() Session { +func (c *Channel) GetCurrentSession() entities.Session { currentSession := session.GetInstance() - var sessionUsers []User + var sessionUsers []entities.User for _, u := range currentSession.Organization.Users { - sessionUsers = append(sessionUsers, User(u)) + sessionUsers = append(sessionUsers, entities.User(u)) } currentProject := currentSession.Project - currentDefaultProcessLanguage := Language(currentProject.Settings.DefaultProcessLanguage) - currentDefaultTranslateTargetLanguage := Language(currentProject.Settings.DefaultTranslateTargetLanguage) - project := Project{ + currentDefaultProcessLanguage := entities.Language(currentProject.Settings.DefaultProcessLanguage) + currentDefaultTranslateTargetLanguage := entities.Language(currentProject.Settings.DefaultTranslateTargetLanguage) + project := entities.Project{ Id: currentProject.Id, Name: currentProject.Name, OrganizationId: currentProject.OrganizationId, - Settings: ProjectSettings{ + Settings: entities.ProjectSettings{ DefaultProcessLanguage: currentDefaultProcessLanguage, DefaultTranslateTargetLanguage: currentDefaultTranslateTargetLanguage, IsHosted: currentProject.Settings.IsHosted, }, } - return Session{ - Project: Project(project), - User: User(currentSession.User), - Organization: Organization{ + return entities.Session{ + Project: project, + User: currentSession.User, + Organization: entities.Organization{ Id: currentSession.Organization.Id, Name: currentSession.Project.Name, LogoPath: currentSession.Organization.LogoPath, @@ -47,23 +47,23 @@ func (c *Channel) GetCurrentSession() Session { } } -func (c *Channel) CreateNewProject(name string) Session { +func (c *Channel) CreateNewProject(name string) entities.Session { currentSession := session.GetInstance() - newProject := session.Project{ + newProject := entities.Project{ Id: uuid.NewString(), OrganizationId: currentSession.Project.OrganizationId, Name: name, } - successfulProjectWrite := storage.GetDriver().WriteProjectData(storageEntity.Project{ + successfulProjectWrite := storage.GetDriver().WriteProjectData(entities.Project{ Id: newProject.Id, OrganizationId: newProject.OrganizationId, Name: newProject.Name, }) if !successfulProjectWrite { - return Session{} + return entities.Session{} } currentSession.Project = newProject @@ -71,14 +71,14 @@ func (c *Channel) CreateNewProject(name string) Session { return c.GetCurrentSession() } -func (c *Channel) GetCurrentUser() User { - return User(session.GetInstance().User) +func (c *Channel) GetCurrentUser() entities.User { + return session.GetInstance().User } -func (c *Channel) RequestUpdateCurrentUser(updatedUserRequest User) User { +func (c *Channel) RequestUpdateCurrentUser(updatedUserRequest entities.User) entities.User { sessionInstance := session.GetInstance() - sessionUser := session.User(sessionInstance.User) + sessionUser := entities.User(sessionInstance.User) if sessionUser.LocalId == "" { sessionUser.LocalId = uuid.NewString() @@ -95,14 +95,14 @@ func (c *Channel) RequestUpdateCurrentUser(updatedUserRequest User) User { sessionUser.AvatarPath = updatedUserRequest.AvatarPath - successfulUserWrite := storage.GetDriver().WriteUserData(storageEntity.User(sessionUser)) + successfulUserWrite := storage.GetDriver().WriteUserData(sessionUser) if !successfulUserWrite { - return User{} + return entities.User{} } sessionInstance.UpdateCurrentUser(sessionUser) - return User(sessionInstance.User) + return sessionInstance.User } func (c *Channel) RequestChooseUserAvatar() string { @@ -124,43 +124,14 @@ func (c *Channel) RequestChooseUserAvatar() string { } } -func (c *Channel) GetAllLocalProjects() []Project { +func (c *Channel) GetAllLocalProjects() []entities.Project { readLocalProjects := storage.GetDriver().ReadAllProjects() - response := make([]Project, 0) - - for _, p := range readLocalProjects { - response = append(response, Project{ - Id: p.Id, - OrganizationId: p.OrganizationId, - Name: p.Name, - Settings: ProjectSettings{ - DefaultProcessLanguage: Language(p.Settings.DefaultProcessLanguage), - DefaultTranslateTargetLanguage: Language(p.Settings.DefaultTranslateTargetLanguage), - IsHosted: p.Settings.IsHosted, - }, - }) - } - - return response + return readLocalProjects } -func (c *Channel) GetProjectByName(projectName string) Project { +func (c *Channel) GetProjectByName(projectName string) entities.Project { foundProject := storage.GetDriver().ReadProjectDataByName(projectName) - - if foundProject.Id == "" { - return Project{} - } - - return Project{ - Id: foundProject.Id, - Name: foundProject.Name, - OrganizationId: foundProject.OrganizationId, - Settings: ProjectSettings{ - DefaultProcessLanguage: Language(foundProject.Settings.DefaultProcessLanguage), - DefaultTranslateTargetLanguage: Language(foundProject.Settings.DefaultTranslateTargetLanguage), - IsHosted: foundProject.Settings.IsHosted, - }, - } + return foundProject } func (c *Channel) RequestChangeSessionProjectByName(projectName string) bool { @@ -171,139 +142,57 @@ func (c *Channel) RequestChangeSessionProjectByName(projectName string) bool { return false } - session.GetInstance().Project = session.Project{ - Id: foundProject.Id, - Name: foundProject.Name, - OrganizationId: foundProject.OrganizationId, - Settings: session.ProjectSettings{ - DefaultProcessLanguage: consts.Language(foundProject.Settings.DefaultProcessLanguage), - DefaultTranslateTargetLanguage: consts.Language(foundProject.Settings.DefaultTranslateTargetLanguage), - IsHosted: foundProject.Settings.IsHosted, - }, - } + session.GetInstance().Project = foundProject localDocumentCollection := storageDriver.ReadDocumentCollection(projectName) - newDocuments := make([]document.Entity, 0) - for _, d := range localDocumentCollection.Documents { - newAreas := make([]document.Area, 0) - for _, a := range d.Areas { - newAreas = append(newAreas, document.Area{ - Id: a.Id, - Name: a.Name, - StartX: a.StartX, - StartY: a.StartY, - EndX: a.EndX, - EndY: a.EndY, - Language: consts.Language(a.Language), - Order: a.Order, - }) - } - newDocuments = append(newDocuments, document.Entity{ - Id: d.Id, - GroupId: d.GroupId, - Name: d.Name, - Path: d.Path, - ProjectId: d.ProjectId, - Areas: newAreas, - DefaultLanguage: consts.Language(d.DefaultLanguage), - }) + documentCount := len(localDocumentCollection.Documents) + readableDocuments := make([]document.Entity, documentCount) + for i := 0; i < documentCount; i++ { + readableDocuments[i] = document.Entity(localDocumentCollection.Documents[i]) } - newDocumentColllection := document.DocumentCollection{ - Documents: newDocuments, + document.SetDocumentCollection(document.DocumentCollection{ + Documents: readableDocuments, ProjectId: foundProject.Id, - } - document.SetDocumentCollection(newDocumentColllection) + }) localGroupsCollection := storageDriver.ReadGroupCollection(projectName) - newGroups := make([]document.Group, 0) - for _, g := range localGroupsCollection.Groups { - newGroups = append(newGroups, document.Group(g)) + groupCount := len(localGroupsCollection.Groups) + readableGroups := make([]entities.Group, groupCount) + for i := 0; i < groupCount; i++ { + readableGroups[i] = entities.Group(localGroupsCollection.Groups[i]) } - newGroupCollection := document.GroupCollection{ + document.SetGroupCollection(document.GroupCollection{ Id: localGroupsCollection.Id, ProjectId: localGroupsCollection.ProjectId, - Groups: newGroups, - } - document.SetGroupCollection(newGroupCollection) + Groups: readableGroups, + }) // Processed Texts - localProcessedAreaCollection := storageDriver.ReadProcessedTextCollection(projectName) - newAreas := make([]document.ProcessedArea, 0) - for _, a := range localProcessedAreaCollection.Areas { - linesOfArea := make([]document.ProcessedLine, 0) - for _, l := range a.Lines { - wordsOfLine := make([]document.ProcessedWord, 0) - - for _, w := range l.Words { - symbolsOfWord := make([]document.ProcessedSymbol, 0) - - for _, s := range w.Symbols { - symbolsOfWord = append(symbolsOfWord, document.ProcessedSymbol{ - Text: s.Text, - Confidence: s.Confidence, - BoundingBox: document.ProcessedBoundingBox(s.BoundingBox), - }) - } - - wordsOfLine = append(wordsOfLine, document.ProcessedWord{ - FullText: w.FullText, - Confidence: w.Confidence, - Direction: w.Direction, - BoundingBox: document.ProcessedBoundingBox(w.BoundingBox), - Symbols: symbolsOfWord, - }) - } - - linesOfArea = append(linesOfArea, document.ProcessedLine{ - FullText: l.FullText, - Words: wordsOfLine, - }) - } - - newAreas = append(newAreas, document.ProcessedArea{ - Id: a.Id, - DocumentId: a.DocumentId, - FullText: a.FullText, - Order: a.Order, - Lines: linesOfArea, - }) + areaCount := len(localProcessedAreaCollection.Areas) + readableAreas := make([]entities.ProcessedArea, areaCount) + for i := 0; i < areaCount; i++ { + readableAreas[i] = entities.ProcessedArea(localProcessedAreaCollection.Areas[i]) } - document.SetProcessedAreaCollection(document.ProcessedAreaCollection{ - Areas: newAreas, + Areas: readableAreas, }) // UserProcessedMarkdown - localUserProcessedMarkdown := storageDriver.ReadProcessedUserMarkdownCollection(projectName) - - newUserProcessedMarkdown := make([]document.UserMarkdown, 0) - for _, v := range localUserProcessedMarkdown.Values { - newUserProcessedMarkdown = append(newUserProcessedMarkdown, document.UserMarkdown{ - Id: v.Id, - DocumentId: v.DocumentId, - Value: v.Value, - }) + userProcessedMarkdownCount := len(localUserProcessedMarkdown.Values) + readableUserProcessedMarkdown := make([]entities.UserMarkdown, userProcessedMarkdownCount) + for i := 0; i < userProcessedMarkdownCount; i++ { + readableUserProcessedMarkdown[i] = entities.UserMarkdown(localUserProcessedMarkdown.Values[i]) } - document.SetUserMarkdownCollection(document.UserMarkdownCollection{ - Values: newUserProcessedMarkdown, + Values: readableUserProcessedMarkdown, }) - // End UserProcessedMarkdown - return session.GetInstance().Project.Id == foundProject.Id } -func (c *Channel) GetSuppportedLanguages() []Language { +func (c *Channel) GetSuppportedLanguages() []entities.Language { supportedLanguages := consts.GetSuppportedLanguages() - - var response []Language - - for _, l := range supportedLanguages { - response = append(response, Language(l)) - } - - return response + return supportedLanguages } diff --git a/storage/Local/DocumentDriver.go b/storage/Local/DocumentDriver.go index 5936eb8..b535ef2 100644 --- a/storage/Local/DocumentDriver.go +++ b/storage/Local/DocumentDriver.go @@ -2,20 +2,20 @@ package storage import ( "encoding/json" - entity "textualize/storage/Entities" + "textualize/entities" ) -func (d LocalDriver) WriteDocumentCollection(documentCollection entity.DocumentCollection, projectName string) bool { +func (d LocalDriver) WriteDocumentCollection(documentCollection entities.DocumentCollection, projectName string) bool { jsonData, _ := json.MarshalIndent(documentCollection, "", " ") writeError := WriteDataToAppDir(jsonData, "/projects/"+projectName+"/", "Documents.json") return writeError == nil } -func (d LocalDriver) ReadDocumentCollection(projectName string) entity.DocumentCollection { - documentCollectionData := entity.DocumentCollection{} +func (d LocalDriver) ReadDocumentCollection(projectName string) entities.DocumentCollection { + documentCollectionData := entities.DocumentCollection{} readError := AssignFileDataToStruct("/projects/"+projectName+"/Documents.json", &documentCollectionData) if readError != nil { - return entity.DocumentCollection{} + return entities.DocumentCollection{} } return documentCollectionData diff --git a/storage/Local/GroupDriver.go b/storage/Local/GroupDriver.go index 7a2fdab..dda8564 100644 --- a/storage/Local/GroupDriver.go +++ b/storage/Local/GroupDriver.go @@ -2,20 +2,20 @@ package storage import ( "encoding/json" - entity "textualize/storage/Entities" + "textualize/entities" ) -func (d LocalDriver) WriteGroupCollection(collection entity.GroupCollection, projectName string) bool { +func (d LocalDriver) WriteGroupCollection(collection entities.GroupCollection, projectName string) bool { jsonData, _ := json.MarshalIndent(collection, "", " ") writeError := WriteDataToAppDir(jsonData, "/projects/"+projectName+"/", "Groups.json") return writeError == nil } -func (d LocalDriver) ReadGroupCollection(projectName string) entity.GroupCollection { - collectionData := entity.GroupCollection{} +func (d LocalDriver) ReadGroupCollection(projectName string) entities.GroupCollection { + collectionData := entities.GroupCollection{} readError := AssignFileDataToStruct("/projects/"+projectName+"/Groups.json", &collectionData) if readError != nil { - return entity.GroupCollection{} + return entities.GroupCollection{} } return collectionData diff --git a/storage/Local/ProcessedTextDriver.go b/storage/Local/ProcessedTextDriver.go index 957d5cf..71847fb 100644 --- a/storage/Local/ProcessedTextDriver.go +++ b/storage/Local/ProcessedTextDriver.go @@ -2,36 +2,36 @@ package storage import ( "encoding/json" - entity "textualize/storage/Entities" + "textualize/entities" ) -func (d LocalDriver) WriteProcessedTextCollection(collection entity.ProcessedTextCollection, projectName string) bool { +func (d LocalDriver) WriteProcessedTextCollection(collection entities.ProcessedTextCollection, projectName string) bool { jsonData, _ := json.MarshalIndent(collection, "", " ") writeError := WriteDataToAppDir(jsonData, "/projects/"+projectName+"/", "ProcessedTexts.json") return writeError == nil } -func (d LocalDriver) ReadProcessedTextCollection(projectName string) entity.ProcessedTextCollection { - collectionData := entity.ProcessedTextCollection{} +func (d LocalDriver) ReadProcessedTextCollection(projectName string) entities.ProcessedTextCollection { + collectionData := entities.ProcessedTextCollection{} readError := AssignFileDataToStruct("/projects/"+projectName+"/ProcessedTexts.json", &collectionData) if readError != nil { - return entity.ProcessedTextCollection{} + return entities.ProcessedTextCollection{} } return collectionData } -func (d LocalDriver) WriteProcessedUserMarkdownCollection(collection entity.ProcessedUserMarkdownCollection, projectName string) bool { +func (d LocalDriver) WriteProcessedUserMarkdownCollection(collection entities.ProcessedUserMarkdownCollection, projectName string) bool { jsonData, _ := json.MarshalIndent(collection, "", " ") writeError := WriteDataToAppDir(jsonData, "/projects/"+projectName+"/", "UserProcessedMarkdown.json") return writeError == nil } -func (d LocalDriver) ReadProcessedUserMarkdownCollection(projectName string) entity.ProcessedUserMarkdownCollection { - collectionData := entity.ProcessedUserMarkdownCollection{} +func (d LocalDriver) ReadProcessedUserMarkdownCollection(projectName string) entities.ProcessedUserMarkdownCollection { + collectionData := entities.ProcessedUserMarkdownCollection{} readError := AssignFileDataToStruct("/projects/"+projectName+"/UserProcessedMarkdown.json", &collectionData) if readError != nil { - return entity.ProcessedUserMarkdownCollection{} + return entities.ProcessedUserMarkdownCollection{} } return collectionData diff --git a/storage/Local/ProjectDriver.go b/storage/Local/ProjectDriver.go index b1ef89a..54a854f 100644 --- a/storage/Local/ProjectDriver.go +++ b/storage/Local/ProjectDriver.go @@ -3,27 +3,27 @@ package storage import ( "encoding/json" "os" - storage "textualize/storage/Entities" + "textualize/entities" ) -func (d LocalDriver) WriteProjectData(project storage.Project) bool { +func (d LocalDriver) WriteProjectData(project entities.Project) bool { jsonData, _ := json.MarshalIndent(project, "", " ") writeError := WriteDataToAppDir(jsonData, "/projects/"+project.Name+"/", "Project.json") return writeError == nil } -func (d LocalDriver) ReadProjectDataByName(projectName string) storage.Project { - projectData := storage.Project{} +func (d LocalDriver) ReadProjectDataByName(projectName string) entities.Project { + projectData := entities.Project{} readError := AssignFileDataToStruct("/projects/"+projectName+"/Project.json", &projectData) if readError != nil { - return storage.Project{} + return entities.Project{} } return projectData } -func (d LocalDriver) ReadAllProjects() []storage.Project { - localProjects := make([]storage.Project, 0) +func (d LocalDriver) ReadAllProjects() []entities.Project { + localProjects := make([]entities.Project, 0) subdirectory := "/projects/" isLocalStorageDirectoryCreated := createLocalStorageSubDirIfNeeded(subdirectory) diff --git a/storage/Local/UserDriver.go b/storage/Local/UserDriver.go index 32935f3..c6aabd8 100644 --- a/storage/Local/UserDriver.go +++ b/storage/Local/UserDriver.go @@ -2,20 +2,20 @@ package storage import ( "encoding/json" - storage "textualize/storage/Entities" + "textualize/entities" ) -func (d LocalDriver) WriteUserData(user storage.User) bool { +func (d LocalDriver) WriteUserData(user entities.User) bool { jsonData, _ := json.MarshalIndent(user, "", " ") writeError := WriteDataToAppDir(jsonData, "/", "User.json") return writeError == nil } -func (d LocalDriver) ReadUserData() storage.User { - userData := storage.User{} +func (d LocalDriver) ReadUserData() entities.User { + userData := entities.User{} readError := AssignFileDataToStruct("/User.json", &userData) if readError != nil { - return storage.User{} + return entities.User{} } return userData diff --git a/storage/Storage.go b/storage/Storage.go index bc7eb4a..d31a7a4 100644 --- a/storage/Storage.go +++ b/storage/Storage.go @@ -1,24 +1,24 @@ package storage import ( - entity "textualize/storage/Entities" + "textualize/entities" local "textualize/storage/Local" ) type Driver interface { - WriteUserData(entity.User) bool - ReadUserData() entity.User - WriteProjectData(entity.Project) bool - ReadProjectDataByName(string) entity.Project - ReadAllProjects() []entity.Project - WriteDocumentCollection(entity.DocumentCollection, string) bool - ReadDocumentCollection(string) entity.DocumentCollection - WriteGroupCollection(entity.GroupCollection, string) bool - ReadGroupCollection(string) entity.GroupCollection - WriteProcessedTextCollection(entity.ProcessedTextCollection, string) bool - ReadProcessedTextCollection(string) entity.ProcessedTextCollection - WriteProcessedUserMarkdownCollection(entity.ProcessedUserMarkdownCollection, string) bool - ReadProcessedUserMarkdownCollection(string) entity.ProcessedUserMarkdownCollection + WriteUserData(entities.User) bool + ReadUserData() entities.User + WriteProjectData(entities.Project) bool + ReadProjectDataByName(string) entities.Project + ReadAllProjects() []entities.Project + WriteDocumentCollection(entities.DocumentCollection, string) bool + ReadDocumentCollection(string) entities.DocumentCollection + WriteGroupCollection(entities.GroupCollection, string) bool + ReadGroupCollection(string) entities.GroupCollection + WriteProcessedTextCollection(entities.ProcessedTextCollection, string) bool + ReadProcessedTextCollection(string) entities.ProcessedTextCollection + WriteProcessedUserMarkdownCollection(entities.ProcessedUserMarkdownCollection, string) bool + ReadProcessedUserMarkdownCollection(string) entities.ProcessedUserMarkdownCollection } var driverInstance Driver -- 2.47.2 From c01ba92021ded3f931b92e4e571209d8a2ddaaee Mon Sep 17 00:00:00 2001 From: Joshua Shoemaker Date: Fri, 26 May 2023 19:13:18 -0500 Subject: [PATCH 2/4] refact: fixed front end type, removed dead code --- .../createUiCanvasInteractions.ts | 24 +-- frontend/components/DocumentCanvas/types.ts | 19 +++ .../workspace/Sidebar/DocumentLineItem.tsx | 48 +----- .../workspace/Sidebar/GroupLineItem.tsx | 143 +----------------- .../workspace/Sidebar/makeDefaultSidebar.ts | 4 + .../workspace/Sidebar/navigationProps.ts | 4 +- 6 files changed, 28 insertions(+), 214 deletions(-) create mode 100644 frontend/components/DocumentCanvas/types.ts diff --git a/frontend/components/DocumentCanvas/createUiCanvasInteractions.ts b/frontend/components/DocumentCanvas/createUiCanvasInteractions.ts index 4bbd903..1840341 100644 --- a/frontend/components/DocumentCanvas/createUiCanvasInteractions.ts +++ b/frontend/components/DocumentCanvas/createUiCanvasInteractions.ts @@ -1,28 +1,6 @@ import isInBounds from '../../utils/isInBounds' import { entities } from '../../wailsjs/wailsjs/go/models' - - -type MouseCoordinates = { - startMouseX: number, startMouseY: number, endMouseX: number, endMouseY: number -} - -type RectangleCoordinates = { - startX: number, startY: number, endX: number, endY: number -} - -type AddAreaToStoreCallback = - (startX: number, startY: number, endX: number, endY: number) - => Promise - -type SetZoomCallback = (newZoomLevel: number) => void - -type ZoomDetails = { - currentZoomLevel: number, - maxZoomLevel: number, - zoomStep: number -} - -type HoverOverAreaCallback = (areaId?: string) => void +import { AddAreaToStoreCallback, HoverOverAreaCallback, MouseCoordinates, RectangleCoordinates, SetZoomCallback, ZoomDetails } from './types' /** * @param uiCanvas diff --git a/frontend/components/DocumentCanvas/types.ts b/frontend/components/DocumentCanvas/types.ts new file mode 100644 index 0000000..cf61d59 --- /dev/null +++ b/frontend/components/DocumentCanvas/types.ts @@ -0,0 +1,19 @@ +export type MouseCoordinates = { + startMouseX: number, startMouseY: number, endMouseX: number, endMouseY: number +} + +export type RectangleCoordinates = { + startX: number, startY: number, endX: number, endY: number +} + +export type AddAreaToStoreCallback = (startX: number, startY: number, endX: number, endY: number) => Promise + +export type SetZoomCallback = (newZoomLevel: number) => void + +export type ZoomDetails = { + currentZoomLevel: number, + maxZoomLevel: number, + zoomStep: number +} + +export type HoverOverAreaCallback = (areaId?: string) => void \ No newline at end of file diff --git a/frontend/components/workspace/Sidebar/DocumentLineItem.tsx b/frontend/components/workspace/Sidebar/DocumentLineItem.tsx index cc0e25a..aa2890f 100644 --- a/frontend/components/workspace/Sidebar/DocumentLineItem.tsx +++ b/frontend/components/workspace/Sidebar/DocumentLineItem.tsx @@ -56,7 +56,7 @@ const DocumentLineItem = (props: { document: SidebarDocument, groupId: string, i return (
  • - {!props.document.areas.length + {!props.document.areas?.length ?
    onDocumentClickHandler(props.document.id)} @@ -149,52 +149,6 @@ const DocumentLineItem = (props: { document: SidebarDocument, groupId: string, i diff --git a/frontend/components/workspace/Sidebar/GroupLineItem.tsx b/frontend/components/workspace/Sidebar/GroupLineItem.tsx index 45839fa..4900d73 100644 --- a/frontend/components/workspace/Sidebar/GroupLineItem.tsx +++ b/frontend/components/workspace/Sidebar/GroupLineItem.tsx @@ -1,17 +1,14 @@ 'use client' import { DocumentPlusIcon, PlusIcon, XMarkIcon } from '@heroicons/react/24/outline' -import React, { useRef, useState } from 'react' +import React, { useRef } from 'react' import { useProject } from '../../../context/Project/provider' import classNames from '../../../utils/classNames' import onEnterHandler from '../../../utils/onEnterHandler' -import AddGroupInput from './AddGroupInput' import DocumentLineItem from './DocumentLineItem' import { useSidebar } from './provider' import { SidebarGroup } from './types' - - const GroupLineItem = (props: { group: SidebarGroup, dragOverGroupId?: string }) => { const { requestAddDocument, @@ -58,10 +55,6 @@ const GroupLineItem = (props: { group: SidebarGroup, dragOverGroupId?: string }) setDragOverGroupId(groupId) } - const onGroupDragStart = (groupId: string) => { - setSelectedGroupId(groupId) - } - const onGroupDropEnd = (groupId: string) => { if (!groupId || groupId == dragOverGroupId) return @@ -136,140 +129,6 @@ const GroupLineItem = (props: { group: SidebarGroup, dragOverGroupId?: string })
      {props.group.documents.map((d, index) => ( - //
    • - // {!d.areas.length - // ? - //
      onDocumentClickHandler(d.id)} - // onDoubleClick={() => onDocumentDoubleClickHandler(d.id)} - // className={classNames( - // d.id === selectedDocumentId - // ? 'bg-gray-900 text-white' - // : 'text-gray-300 hover:bg-gray-700 hover:text-white', - // 'group items-center py-2 text-base font-medium rounded-b-md pl-10', - // index !== 0 ? 'rounded-t-md' : '', - // )}> - // {selectedDocumentId === d.id && isEditDocumentNameInputShowing - // ? { - // onEnterHandler(event, - // () => onConfirmDocumentNameChangeHandler(event.currentTarget.value)) - // }} - // ref={editDocumentNameTextInput} - // /> - // : - // {d.name} - // - // } - //
      - // :
      - // onDocumentClickHandler(d.id)} - // onDoubleClick={() => onDocumentDoubleClickHandler(d.id)} - // className={classNames( - // d.id === selectedDocumentId - // ? 'bg-gray-900 text-white' - // : 'text-gray-300 hover:bg-gray-700 hover:text-white', - // 'group items-center py-2 text-base font-medium rounded-b-md pl-6', - // index !== 0 ? 'rounded-t-md' : '', - - // )}> - // {selectedDocumentId === d.id && isEditDocumentNameInputShowing - // ? { - // onEnterHandler(event, - // () => onConfirmDocumentNameChangeHandler(event.currentTarget.value)) - // }} - // ref={editDocumentNameTextInput} - // /> - // : - // {d.name} - // - // } - // - // - //
      - // } - //
    • ))} {renderAddNewDocument(props.group.id)} diff --git a/frontend/components/workspace/Sidebar/makeDefaultSidebar.ts b/frontend/components/workspace/Sidebar/makeDefaultSidebar.ts index cc45a09..1db8b35 100644 --- a/frontend/components/workspace/Sidebar/makeDefaultSidebar.ts +++ b/frontend/components/workspace/Sidebar/makeDefaultSidebar.ts @@ -16,6 +16,10 @@ const makeDefaultSidebar = (): SidebarContextType => ({ setIsAddNewGroupInputShowing: (_: boolean) => {}, isEditAreaNameInputShowing: false, setIsEditAreaNameInputShowing: (_: boolean) => {}, + dragOverGroupId: '', + setDragOverGroupId: (_: string) => {}, + dragOverAreaId: '', + setDragOverAreaId: (_: string) => {}, }) export default makeDefaultSidebar \ No newline at end of file diff --git a/frontend/components/workspace/Sidebar/navigationProps.ts b/frontend/components/workspace/Sidebar/navigationProps.ts index f357b1f..6144101 100644 --- a/frontend/components/workspace/Sidebar/navigationProps.ts +++ b/frontend/components/workspace/Sidebar/navigationProps.ts @@ -8,7 +8,7 @@ const getNavigationProps = (documents: entities.Document[], groups: entities.Gro .map(d => ({ id: d.id, name: d.name, - areas: d.areas.map(a => ({ id: a.id, name: a.name, order: a.order }))//.sort((a, b) => a.order - b.order) + areas: d.areas?.map(a => ({ id: a.id, name: a.name, order: a.order }))//.sort((a, b) => a.order - b.order) })) return { @@ -23,7 +23,7 @@ const getNavigationProps = (documents: entities.Document[], groups: entities.Gro .map(d => ({ id: d.id, name: d.name, - areas: d.areas.map(a => ({ id: a.id, name: a.name, order: a.order }))//.sort((a, b) => a.order - b.order) + areas: d.areas?.map(a => ({ id: a.id, name: a.name, order: a.order }))//.sort((a, b) => a.order - b.order) })) return [ -- 2.47.2 From 8b55241ee4db1868e32e2da0dbf9898ed56c77f2 Mon Sep 17 00:00:00 2001 From: Joshua Shoemaker Date: Fri, 26 May 2023 19:17:28 -0500 Subject: [PATCH 3/4] removed test image folder --- test/newspaper.png | Bin 71811 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/newspaper.png diff --git a/test/newspaper.png b/test/newspaper.png deleted file mode 100644 index 91d58794abac5ec243d2ca0e2eb418bab2abef3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71811 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 -- 2.47.2 From 7335b636f4233369331c9c27d18f4e1c67ceca59 Mon Sep 17 00:00:00 2001 From: Joshua Shoemaker Date: Fri, 26 May 2023 19:23:05 -0500 Subject: [PATCH 4/4] refact: removed dead structs --- core/Consts/Consts.go | 6 ------ core/Document/ProcessedText.go | 10 ---------- core/Document/UserMarkdown.go | 2 -- 3 files changed, 18 deletions(-) diff --git a/core/Consts/Consts.go b/core/Consts/Consts.go index 0d4d37a..4f0aef0 100644 --- a/core/Consts/Consts.go +++ b/core/Consts/Consts.go @@ -2,12 +2,6 @@ package consts import "textualize/entities" -// type Language struct { -// DisplayName string -// ProcessCode string -// TranslateCode string -// } - func GetSuppportedLanguages() []entities.Language { return []entities.Language{ { diff --git a/core/Document/ProcessedText.go b/core/Document/ProcessedText.go index 1423ffd..ecfc701 100644 --- a/core/Document/ProcessedText.go +++ b/core/Document/ProcessedText.go @@ -2,16 +2,6 @@ package document import "textualize/entities" -type ProcessedBoundingBox entities.ProcessedBoundingBox - -type ProcessedSymbol entities.ProcessedSymbol - -type ProcessedWord entities.ProcessedWord - -type ProcessedLine entities.ProcessedLine - -type ProcessedArea entities.ProcessedArea - type ProcessedAreaCollection struct { Areas []entities.ProcessedArea } diff --git a/core/Document/UserMarkdown.go b/core/Document/UserMarkdown.go index a3278a6..154dfe3 100644 --- a/core/Document/UserMarkdown.go +++ b/core/Document/UserMarkdown.go @@ -2,8 +2,6 @@ package document import "textualize/entities" -type UserMarkdown entities.UserMarkdown - type UserMarkdownCollection struct { Values []entities.UserMarkdown } -- 2.47.2