feat: update processed word

This commit is contained in:
Joshua Shoemaker 2023-05-22 21:24:34 -05:00
parent 1f8ffc01b1
commit a323a387e5
12 changed files with 173 additions and 9 deletions

View File

@ -14,6 +14,7 @@ type ProcessedSymbol struct {
}
type ProcessedWord struct {
Id string
FullText string
Symbols []ProcessedSymbol
Confidence float32

View File

@ -9,22 +9,32 @@ import classNames from '../../utils/classNames'
import LanguageSelect from './LanguageSelect'
import isInBounds from '../../utils/isInBounds'
import { ipc } from '../../wailsjs/wailsjs/go/models'
import onEnterHandler from '../../utils/onEnterHandler'
const zoomStep = 0.025
const maxZoomLevel = 4
const DocumentRenderer = () => {
const { getSelectedDocument, requestAddArea, selectedAreaId, setSelectedAreaId, getProcessedAreasByDocumentId } = useProject()
const {
getSelectedDocument,
requestAddArea,
selectedAreaId,
setSelectedAreaId,
getProcessedAreasByDocumentId,
requestUpdateProcessedWordById
} = useProject()
const selectedDocument = getSelectedDocument()
const areas = selectedDocument?.areas
const documentCanvas = useRef<HTMLCanvasElement>(null)
const areaCanvas = useRef<HTMLCanvasElement>(null)
const uiCanvas = useRef<HTMLCanvasElement>(null)
const drawingCanvas = useRef<HTMLCanvasElement>(null)
const editWordInput = useRef<HTMLInputElement>(null)
const [zoomLevel, setZoomLevel] = useState(1)
const [hoverOverAreaId, setHoverOverAreaId] = useState('')
const [hoveredProcessedArea, setHoveredProcessedArea] = useState<ipc.ProcessedArea | undefined>()
const [wordToEdit, setWordToEdit] = useState<{ word: ipc.ProcessedWord, areaId: string } | undefined>()
let downClickX = 0
let downClickY = 0
@ -134,10 +144,16 @@ const DocumentRenderer = () => {
}
const getProcessedAreaById = async (areaId: string) => {
if (!selectedDocument || !selectedDocument.id || !areaId) return
const processedAreas = await getProcessedAreasByDocumentId(selectedDocument.id)
const foundProcessedArea = processedAreas.find(a => a.id === areaId)
return foundProcessedArea
try {
if (!selectedDocument || !selectedDocument.id || !areaId) return
const processedAreas = await getProcessedAreasByDocumentId(selectedDocument.id)
const foundProcessedArea = processedAreas.find(a => a.id === areaId)
console.log(foundProcessedArea)
return foundProcessedArea
} catch (err) {
console.error(err)
return
}
}
const handleHoverOverArea = (e: React.MouseEvent) => {
@ -155,7 +171,6 @@ const DocumentRenderer = () => {
setHoverOverAreaId(areaContainingCoords?.id || '')
getProcessedAreaById(areaContainingCoords?.id || '').then(response => {
console.log(response)
setHoveredProcessedArea(response)
})
}
@ -237,6 +252,19 @@ const DocumentRenderer = () => {
else if (zoomLevel > (zoomStep * 2)) setZoomLevel(zoomLevel - zoomStep)
}
const handleWordCorrectionSubmit = (wordId: string, newWordValue: string) => {
console.log(newWordValue)
requestUpdateProcessedWordById(wordId, newWordValue)
.then(res => {
console.log('res', res)
getProcessedAreaById(hoverOverAreaId|| '').then(response => {
setHoveredProcessedArea(response)
})
})
.catch(console.error)
setWordToEdit(undefined)
}
useEffect(() => {
if (selectedDocument?.path) applyDocumentToCanvas(selectedDocument.path)
})
@ -260,13 +288,17 @@ const DocumentRenderer = () => {
return <span
key={i}
dir={w.direction === 'RIGHT_TO_LEFT' ? 'rtl' : 'ltr'}
className='absolute text-center inline-block p-1 bg-opacity-60 bg-black text-white rounded-md shadow-zinc-900 shadow-2xl'
className={classNames('absolute text-center inline-block p-1 rounded-md shadow-zinc-900 shadow-2xl',
'hover:bg-opacity-60 hover:bg-black hover:text-white',
'bg-opacity-80 bg-slate-300 text-slate-500'
)}
style={{
fontSize: `${3.4 * zoomLevel}vmin`,
width,
top: Math.floor(w.boundingBox.y0 * zoomLevel) + height,
left: Math.floor(w.boundingBox.x0 * zoomLevel)
}}>
}}
onDoubleClick={() => setWordToEdit({ word: w, areaId: hoverArea.id })}>
{w.fullText}
</span>
})
@ -274,6 +306,56 @@ const DocumentRenderer = () => {
</div>
}
const renderEditWord = () => {
if (!wordToEdit) return <></>
const { word, areaId } = wordToEdit
const width = Math.floor((word.boundingBox.x1 - word.boundingBox.x0) * zoomLevel) + 2
const height = Math.floor(((word.boundingBox.y1 - word.boundingBox.y0) * zoomLevel) * 2) + 4
return <div
dir={word.direction === 'RIGHT_TO_LEFT' ? 'rtl' : 'ltr'}
className={classNames('absolute inline-block p-1 rounded-md',
'bg-opacity-60 bg-black text-white',
)}
style={{
width,
height,
top: Math.floor(word.boundingBox.y0 * zoomLevel) + (height / 2),
left: Math.floor(word.boundingBox.x0 * zoomLevel)
}}
onBlur={() => setWordToEdit(undefined)}
>
<div
className={classNames('text-center align-middle block p-1 rounded-md shadow-zinc-900 shadow-2xl',
'bg-opacity-60 bg-black text-white',
)}
style={{
fontSize: `${3.4 * zoomLevel}vmin`,
height: height / 2,
}}>
{word.fullText}
</div>
<input
type='text'
className='inline-block text-slate-900 p-0 m-0 w-full'
autoFocus
width={width}
ref={editWordInput}
placeholder={word.fullText}
defaultValue={word.fullText}
style={{
fontSize: `${3.4 * zoomLevel}vmin`,
height: height / 2,
}}
onFocus={(e) => e.currentTarget.select()}
onBlur={(e) => handleWordCorrectionSubmit(word.id, e.currentTarget.value)}
onKeyDown={(e) => onEnterHandler(e, () => handleWordCorrectionSubmit(word.id, e.currentTarget.value))}
/>
</div>
}
return <div className='relative'>
<div className='flex justify-between align-top mb-2'>
<div className='flex align-top'>
@ -315,6 +397,7 @@ const DocumentRenderer = () => {
onMouseMove={handleMouseMove}
/>
{renderAreaPreview()}
{renderEditWord()}
</div>
</div >
}

View File

@ -30,6 +30,7 @@ const makeDefaultProject = (): ProjectContextType => ({
requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise.resolve(new ipc.Group()),
getGroupById: (groupId) => undefined,
requestSelectProjectByName: (projectName) => Promise.resolve(false),
requestUpdateProcessedWordById: (wordId, newTestValue) => Promise.resolve(false),
})
export default makeDefaultProject

View File

@ -13,6 +13,7 @@ import {
RequestChangeGroupOrder,
RequestChangeSessionProjectByName,
RequestDeleteDocumentAndChildren,
RequestUpdateProcessedWordById
} from '../../wailsjs/wailsjs/go/ipc/Channel'
import { ipc } from '../../wailsjs/wailsjs/go/models'
import { AddAreaProps, AreaProps, ProjectContextType, ProjectProps, UpdateDocumentRequest, UserProps } from './types'
@ -35,7 +36,6 @@ export function ProjectProvider({ children, projectProps }: Props) {
const updateDocuments = async () => {
GetDocuments().then(response => {
console.log(response)
setDocuments(response.documents)
setGroups(response.groups)
Promise.resolve(response)
@ -184,6 +184,13 @@ export function ProjectProvider({ children, projectProps }: Props) {
return successfulResponse
}
const requestUpdateProcessedWordById = async (wordId: string, newTextValue: string) => {
const successfulResponse = await RequestUpdateProcessedWordById(wordId, newTextValue)
// if (successfulResponse) await updateDocuments()
return successfulResponse
}
useEffect(() => {
if (!documents.length && !groups.length) updateDocuments()
}, [documents.length, groups.length])
@ -224,6 +231,7 @@ export function ProjectProvider({ children, projectProps }: Props) {
requestChangeGroupOrder,
getGroupById,
requestSelectProjectByName,
requestUpdateProcessedWordById,
}
return <ProjectContext.Provider value={value}>

View File

@ -62,4 +62,5 @@ export type ProjectContextType = {
requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise<ipc.Group>
getGroupById: (groupId: string) => ipc.Group | undefined
requestSelectProjectByName: (projectName: string) => Promise<boolean>
requestUpdateProcessedWordById: (wordId: string, newTextValue: string) => Promise<boolean>
} & ProjectProps

View File

@ -59,3 +59,5 @@ export function RequestUpdateCurrentUser(arg1:ipc.User):Promise<ipc.User>;
export function RequestUpdateDocument(arg1:ipc.Document):Promise<ipc.Document>;
export function RequestUpdateDocumentUserMarkdown(arg1:string,arg2:string):Promise<ipc.UserMarkdown>;
export function RequestUpdateProcessedWordById(arg1:string,arg2:string):Promise<boolean>;

View File

@ -117,3 +117,7 @@ export function RequestUpdateDocument(arg1) {
export function RequestUpdateDocumentUserMarkdown(arg1, arg2) {
return window['go']['ipc']['Channel']['RequestUpdateDocumentUserMarkdown'](arg1, arg2);
}
export function RequestUpdateProcessedWordById(arg1, arg2) {
return window['go']['ipc']['Channel']['RequestUpdateProcessedWordById'](arg1, arg2);
}

View File

@ -269,6 +269,7 @@ export namespace ipc {
}
}
export class ProcessedWord {
id: string;
fullText: string;
symbols: ProcessedSymbol[];
confidence: number;
@ -281,6 +282,7 @@ export namespace ipc {
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.fullText = source["fullText"];
this.symbols = this.convertValues(source["symbols"], ProcessedSymbol);
this.confidence = source["confidence"];

View File

@ -536,6 +536,7 @@ func (c *Channel) RequestSaveProcessedTextCollection() bool {
}
wordsOfLineToWrite = append(wordsOfLineToWrite, storageEntity.ProcessedWord{
Id: w.Id,
FullText: w.FullText,
Confidence: w.Confidence,
Direction: w.Direction,

View File

@ -54,6 +54,7 @@ type ProcessedSymbol struct {
}
type ProcessedWord struct {
Id string `json:"id"`
FullText string `json:"fullText"`
Symbols []ProcessedSymbol `json:"symbols"`
Confidence float32 `json:"confidence"`

View File

@ -3,6 +3,8 @@ package ipc
import (
"sort"
document "textualize/core/Document"
"github.com/google/uuid"
)
func serializeBoundingBox(bbox document.ProcessedBoundingBox) ProcessedBoundingBox {
@ -30,6 +32,7 @@ func serialzeWord(word document.ProcessedWord) ProcessedWord {
}
return ProcessedWord{
Id: word.Id,
FullText: word.FullText,
Symbols: symbols,
Confidence: word.Confidence,
@ -106,8 +109,15 @@ func deserialzeWord(word ProcessedWord) document.ProcessedWord {
for _, symbol := range word.Symbols {
symbols = append(symbols, deserializeSymbol(symbol))
}
var wordId string
if word.Id == "" {
wordId = uuid.NewString()
} else {
wordId = word.Id
}
return document.ProcessedWord{
Id: wordId,
FullText: word.FullText,
Symbols: symbols,
Confidence: word.Confidence,
@ -169,3 +179,52 @@ func (c *Channel) RequestAddProcessedArea(processedArea ProcessedArea) Processed
return processedArea
}
func (c *Channel) RequestUpdateProcessedWordById(wordId string, newTextValue string) bool {
areas := document.GetProcessedAreaCollection().Areas
var areaOfWordIndex int = -1
var lineOfWordIndex int = -1
var foundWordIndex int = -1
for areaIndex, area := range areas {
for lineIndex, line := range area.Lines {
for wordIndex, word := range line.Words {
if word.Id == wordId {
areaOfWordIndex = areaIndex
lineOfWordIndex = lineIndex
foundWordIndex = wordIndex
break
}
}
if foundWordIndex >= 0 {
break
}
}
if foundWordIndex >= 0 {
break
}
}
if areaOfWordIndex < 0 || lineOfWordIndex < 0 || foundWordIndex < 0 {
return false
}
wordProps := areas[areaOfWordIndex].Lines[lineOfWordIndex].Words[foundWordIndex]
areas[areaOfWordIndex].Lines[lineOfWordIndex].Words[foundWordIndex] = document.ProcessedWord{
Id: wordProps.Id,
Direction: wordProps.Direction,
FullText: newTextValue,
BoundingBox: wordProps.BoundingBox,
}
if areas[areaOfWordIndex].Lines[lineOfWordIndex].Words[foundWordIndex].FullText == newTextValue {
successfulSave := c.RequestSaveDocumentCollection()
return successfulSave
} else {
return false
}
}

View File

@ -14,6 +14,7 @@ type ProcessedSymbol struct {
}
type ProcessedWord struct {
Id string `json:"id"`
FullText string `json:"fullText"`
Symbols []ProcessedSymbol `json:"symbols"`
Confidence float32 `json:"confidence"`