feat: default document language select
This commit is contained in:
parent
290bc8481d
commit
8eada10e87
@ -21,3 +21,24 @@ func GetInstance() *App {
|
||||
func (a *App) Startup(ctx context.Context) {
|
||||
a.Context = ctx
|
||||
}
|
||||
|
||||
type Language struct {
|
||||
DisplayName string
|
||||
ProcessCode string
|
||||
TranslateCode string
|
||||
}
|
||||
|
||||
func GetSuppportedLanguages() []Language {
|
||||
return []Language{
|
||||
{
|
||||
DisplayName: "English",
|
||||
ProcessCode: "eng",
|
||||
TranslateCode: "en",
|
||||
},
|
||||
{
|
||||
DisplayName: "Hebrew",
|
||||
ProcessCode: "heb",
|
||||
TranslateCode: "he",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package document
|
||||
|
||||
import app "textualize/core/App"
|
||||
|
||||
type Entity struct {
|
||||
Id string
|
||||
GroupId string
|
||||
@ -7,6 +9,7 @@ type Entity struct {
|
||||
Path string
|
||||
ProjectId string
|
||||
Areas []Area
|
||||
DefaultLanguage app.Language
|
||||
}
|
||||
|
||||
type Area struct {
|
||||
@ -16,6 +19,7 @@ type Area struct {
|
||||
StartY int
|
||||
EndX int
|
||||
EndY int
|
||||
Language app.Language
|
||||
}
|
||||
|
||||
func (e *Entity) AddArea(a Area) {
|
||||
|
||||
@ -1,6 +1,18 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
app "textualize/core/App"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
Id string
|
||||
OrganizationId string
|
||||
Name string
|
||||
Settings ProjectSettings
|
||||
}
|
||||
|
||||
type ProjectSettings struct {
|
||||
DefaultProcessLanguage app.Language
|
||||
DefaultTranslateTargetLanguage app.Language
|
||||
IsHosted bool
|
||||
}
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
"rules": {
|
||||
"@next/next/no-img-element": "off",
|
||||
"quotes": ["warn", "single"],
|
||||
"semi": ["warn", "never"]
|
||||
"semi": ["warn", "never"],
|
||||
"react-hooks/exhaustive-deps": "off"
|
||||
},
|
||||
"ignorePatterns": ["wailsjs/*"]
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
import { useRef } from 'react'
|
||||
import { Switch } from '@headlessui/react'
|
||||
import { useRef, useState } from 'react'
|
||||
import classNames from '../../utils/classNames'
|
||||
|
||||
|
||||
type Props = {
|
||||
onCreateNewProjectHandler: (projectName: string) => void
|
||||
@ -6,16 +9,17 @@ type Props = {
|
||||
|
||||
const NewProjectModal = (props: Props) => {
|
||||
const projectNameRef = useRef<HTMLInputElement>(null)
|
||||
const [isHostedProjectSelected, setIsHostedProjectSelected] = useState(false)
|
||||
|
||||
return (
|
||||
<div className=" p-8 absolute top-2/4 -translate-y-1/2 left-2/4 -translate-x-1/2 z-50 bg-white shadow sm:rounded-lg">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg font-medium leading-6 text-gray-900">Name Your New Project</h3>
|
||||
<div className="mt-2 max-w-xl text-sm text-gray-500">
|
||||
<p>A unique name will be best. This <b>can</b> be changed later.</p>
|
||||
<p>A unique name makes the project easier to identify later. This <b>can</b> be changed later.</p>
|
||||
</div>
|
||||
<form className="mt-5 sm:flex sm:items-center">
|
||||
<div className="w-full sm:max-w-xs">
|
||||
<form className="mt-3 items-center">
|
||||
<div className="w-full mb-5">
|
||||
<label htmlFor="name" className="sr-only">
|
||||
Project Name
|
||||
</label>
|
||||
@ -25,17 +29,46 @@ const NewProjectModal = (props: Props) => {
|
||||
id="name"
|
||||
autoFocus
|
||||
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm text-gray-900"
|
||||
placeholder="New Project"
|
||||
placeholder="Project Name"
|
||||
ref={projectNameRef}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Switch.Group as="div" className="flex items-center justify-between mb-5">
|
||||
<span className="flex flex-grow flex-col">
|
||||
<Switch.Label as="span" className="text-sm font-medium text-gray-900" passive>
|
||||
Hosted Project
|
||||
</Switch.Label>
|
||||
<Switch.Description as="span" className="text-sm text-gray-500">
|
||||
A hosted project can store and process data on a server. This <b>can</b> be added later. <em className='text-xs'>Currently unavalible</em>
|
||||
</Switch.Description>
|
||||
</span>
|
||||
<Switch
|
||||
checked={isHostedProjectSelected}
|
||||
onChange={setIsHostedProjectSelected}
|
||||
disabled
|
||||
className={classNames(
|
||||
isHostedProjectSelected ? 'bg-indigo-600' : 'bg-gray-200',
|
||||
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2'
|
||||
)}
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={classNames(
|
||||
isHostedProjectSelected ? 'translate-x-5' : 'translate-x-0',
|
||||
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out'
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</Switch.Group>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
onClick={() => {
|
||||
if (!projectNameRef.current?.value) return
|
||||
props.onCreateNewProjectHandler(projectNameRef.current?.value)
|
||||
}}
|
||||
className="mt-3 inline-flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
className="inline-flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm"
|
||||
>
|
||||
Start
|
||||
</button>
|
||||
|
||||
@ -5,10 +5,7 @@ import { Fragment } from 'react'
|
||||
import { useNavigation } from '../../context/Navigation/provider'
|
||||
import { mainPages } from '../../context/Navigation/types'
|
||||
import UserAvatar from './UserAvatar'
|
||||
|
||||
function classNames(...classes: any[]) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
import classNames from '../../utils/classNames'
|
||||
|
||||
const Search = () => {
|
||||
const { setSelectedMainPage } = useNavigation()
|
||||
@ -75,7 +72,7 @@ const Search = () => {
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
<Menu.Items className="absolute right-0 z-10 mt-1 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
{userNavigation.map((item) => (
|
||||
<Menu.Item key={item.name}>
|
||||
{({ active }) => (
|
||||
|
||||
@ -4,6 +4,7 @@ import React, { useEffect, useRef } from 'react'
|
||||
import { useProject } from '../../context/Project/provider'
|
||||
import loadImage from '../../useCases/loadImage'
|
||||
import processImageArea from '../../useCases/processImageArea'
|
||||
import LanguageSelect from './LanguageSelect'
|
||||
|
||||
const DocumentRenderer = () => {
|
||||
const { getSelectedDocument, requestAddArea } = useProject()
|
||||
@ -146,13 +147,17 @@ const DocumentRenderer = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDocument?.path) applyDocumentToCanvas(selectedDocument.path)
|
||||
}, [selectedDocument?.id])
|
||||
|
||||
useEffect(() => {
|
||||
applyAreasToCanvas()
|
||||
}, [areas, areas?.length])
|
||||
})
|
||||
|
||||
return <div className="relative">
|
||||
return <div className='relative'>
|
||||
<div className='flex justify-between mt-2'>
|
||||
<h1 className="text-2xl font-semibold text-gray-900">
|
||||
{getSelectedDocument()?.name}
|
||||
</h1>
|
||||
<LanguageSelect shouldUpdateDocument defaultLanguage={selectedDocument?.defaultLanguage} />
|
||||
</div>
|
||||
<div className="relative mt-2">
|
||||
<canvas
|
||||
className="absolute border-4 border-dashed border-gray-200"
|
||||
ref={documentCanvas}
|
||||
@ -169,6 +174,7 @@ const DocumentRenderer = () => {
|
||||
onMouseMove={handleMouseMove}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
export default DocumentRenderer
|
||||
|
||||
87
frontend/components/workspace/LanguageSelect.tsx
Normal file
87
frontend/components/workspace/LanguageSelect.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import { Combobox } from '@headlessui/react'
|
||||
import { LanguageIcon } from '@heroicons/react/20/solid'
|
||||
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/24/outline'
|
||||
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'
|
||||
|
||||
type forAreaType = { shouldUpdateArea?: true, shouldUpdateDocument?: never }
|
||||
type forDocumentType = { shouldUpdateDocument?: true, shouldUpdateArea?: never }
|
||||
type Props = (forAreaType | forDocumentType) & { defaultLanguage?: ipc.Language }
|
||||
|
||||
const LanguageSelect = (props?: Props) => {
|
||||
const { requestUpdateDocument, getSelectedDocument } = useProject()
|
||||
const [languages, setLanguages] = useState<ipc.Language[]>([])
|
||||
const [query, setQuery] = useState('')
|
||||
const [selectedLanguage, setSelectedLanguage] = useState<ipc.Language | undefined>(props?.defaultLanguage)
|
||||
|
||||
|
||||
const filteredLanguages = query === ''
|
||||
? languages
|
||||
: languages.filter(l => {
|
||||
return l.displayName.toLowerCase().includes(query.toLowerCase())
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (languages.length === 0) {
|
||||
getSupportedLanguages().then(response => {
|
||||
setLanguages(response)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (props?.shouldUpdateDocument && selectedLanguage?.displayName) {
|
||||
const currentDocument = { ...getSelectedDocument() }
|
||||
currentDocument.defaultLanguage = selectedLanguage
|
||||
requestUpdateDocument(currentDocument)
|
||||
}
|
||||
}, [selectedLanguage])
|
||||
|
||||
|
||||
return <Combobox as="div" value={selectedLanguage} onChange={setSelectedLanguage}>
|
||||
<div className="inline-block relative">
|
||||
<Combobox.Input
|
||||
className="w-full 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}
|
||||
placeholder='Document Language'
|
||||
/>
|
||||
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none">
|
||||
<LanguageIcon className="h-5 w-5 text-gray-400" />
|
||||
<ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||
</Combobox.Button>
|
||||
|
||||
{filteredLanguages.length > 0 && (
|
||||
<Combobox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
||||
{filteredLanguages.map((l) => (
|
||||
<Combobox.Option
|
||||
key={l.displayName}
|
||||
value={l}
|
||||
className={({ active }) => classNames(
|
||||
'relative cursor-default select-none py-2 pl-3 pr-9',
|
||||
active ? 'bg-indigo-600 text-white' : 'text-gray-900'
|
||||
)}>
|
||||
{({ active, selected }) => <>
|
||||
<span className={classNames('block truncate', selected && 'font-semibold')}>{l.displayName}</span>
|
||||
{selected && (
|
||||
<span className={classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-indigo-600'
|
||||
)}>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
</Combobox.Option>
|
||||
))}
|
||||
</Combobox.Options>
|
||||
)}
|
||||
</div>
|
||||
</Combobox>
|
||||
}
|
||||
|
||||
export default LanguageSelect
|
||||
@ -22,9 +22,9 @@ const renderSelectedWorkSpace = () => {
|
||||
<div className="mx-auto px-4 sm:px-6 md:px-8">
|
||||
<div className="py-1">
|
||||
<div className="mx-auto px-4 sm:px-6 md:px-8">
|
||||
<h1 className="text-2xl font-semibold text-gray-900">
|
||||
{getSelectedDocument()?.name || 'Image Processor'}
|
||||
</h1>
|
||||
{!getSelectedDocument()?.id ? <h1 className="text-2xl mt-2 font-semibold text-gray-900">
|
||||
Image Processor
|
||||
</h1> : ''}
|
||||
</div>
|
||||
{ renderSelectedWorkSpace() }
|
||||
</div>
|
||||
|
||||
@ -1,9 +1,22 @@
|
||||
import { DocumentPlusIcon } from '@heroicons/react/24/outline'
|
||||
import { useProject } from '../../context/Project/provider'
|
||||
|
||||
export default function NoSelectedDocument() {
|
||||
const { requestAddDocument, setSelectedDocumentId } = useProject()
|
||||
|
||||
const onAddDocumentClickHandler = async () => {
|
||||
const documentName = 'Untitled Document'
|
||||
|
||||
const response = await requestAddDocument('', documentName)
|
||||
if (!response.id) return
|
||||
|
||||
setSelectedDocumentId(response.id)
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onAddDocumentClickHandler()}
|
||||
className="relative block w-full rounded-lg border-4 border-dashed border-gray-200 p-12 text-center hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
>
|
||||
<svg
|
||||
|
||||
@ -4,6 +4,7 @@ import React, { useRef, useState } from 'react'
|
||||
import { PlusIcon, XMarkIcon, DocumentPlusIcon } from '@heroicons/react/20/solid'
|
||||
import { ipc } from '../../wailsjs/wailsjs/go/models'
|
||||
import { useProject } from '../../context/Project/provider'
|
||||
import classNames from '../../utils/classNames'
|
||||
|
||||
type GroupNavigationItem = {
|
||||
id: string,
|
||||
@ -43,29 +44,28 @@ const getNavigationProps = (documents: ipc.Document[], groups: ipc.Group[]): Gro
|
||||
return [
|
||||
...groupsWithDocuments,
|
||||
{
|
||||
id: 'Uncategorized',
|
||||
id: '',
|
||||
name: 'Uncategorized',
|
||||
documents: documentsWithoutGroup
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function classNames(...classes: any[]) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
function Sidebar() {
|
||||
const [selectedGroupId, setSelectedGroupId] = useState('')
|
||||
const [isAddNewDocumentInputShowing, setIsAddNewDocumentInputShowing] = useState(false)
|
||||
const [isEditDocumentNameInputShowing, setIsEditDocumentNameInputShowing] = useState(false)
|
||||
const [isAddNewGroupInputShowing, setIsAddNewGroupInputShowing] = useState(false)
|
||||
const [isEditAreaNameInputShowing, setIsEditAreaNameInputShowing] = useState(false)
|
||||
const addDocumentTextInput = useRef<HTMLInputElement>(null)
|
||||
const addGroupTextInput = useRef<HTMLInputElement>(null)
|
||||
const editAreaNameTextInput = useRef<HTMLInputElement>(null)
|
||||
const editDocumentNameTextInput = useRef<HTMLInputElement>(null)
|
||||
|
||||
const {
|
||||
documents,
|
||||
groups,
|
||||
getSelectedDocument,
|
||||
getAreaById,
|
||||
requestUpdateArea,
|
||||
requestAddDocument,
|
||||
@ -75,6 +75,7 @@ function Sidebar() {
|
||||
selectedDocumentId,
|
||||
setSelectedDocumentId,
|
||||
currentSession,
|
||||
requestUpdateDocument,
|
||||
} = useProject()
|
||||
|
||||
const navigation = getNavigationProps(documents, groups)
|
||||
@ -115,7 +116,7 @@ function Sidebar() {
|
||||
setSelectedAreaId(areaId)
|
||||
}
|
||||
|
||||
const onAreaDoubleclick = (areaId: string) => {
|
||||
const onAreaDoubleClick = (areaId: string) => {
|
||||
const documentIdOfArea = getDocumentIdFromAreaId(areaId)
|
||||
setIsEditAreaNameInputShowing(true)
|
||||
console.log('double click')
|
||||
@ -132,6 +133,14 @@ function Sidebar() {
|
||||
setIsAddNewGroupInputShowing(false)
|
||||
}
|
||||
|
||||
const onDocumentDoubleClickHandler = (docuemntId: string) => {
|
||||
setIsEditDocumentNameInputShowing(true)
|
||||
}
|
||||
|
||||
const onDocumentInputBlur = () => {
|
||||
setIsEditDocumentNameInputShowing(false)
|
||||
}
|
||||
|
||||
const onCancelAddGroupClickHandler = () => {
|
||||
setIsAddNewGroupInputShowing(false)
|
||||
}
|
||||
@ -153,6 +162,17 @@ function Sidebar() {
|
||||
setIsEditAreaNameInputShowing(false)
|
||||
}
|
||||
|
||||
const onConfirmDocumentNameChangeHandler = async (documentName: string) => {
|
||||
const documentToUpdate = { ...getSelectedDocument() }
|
||||
if (documentToUpdate) {
|
||||
documentToUpdate.name = documentName
|
||||
requestUpdateDocument(documentToUpdate)
|
||||
.then(response => console.log('onConfirmDocumentNameChangeHandler response: ', response))
|
||||
.catch(console.error)
|
||||
}
|
||||
setIsEditDocumentNameInputShowing(false)
|
||||
}
|
||||
|
||||
const onConfirmAddDocumentClickHandler = async (groupId: string) => {
|
||||
const documentName = addDocumentTextInput.current?.value
|
||||
if (!documentName) return
|
||||
@ -294,8 +314,10 @@ function Sidebar() {
|
||||
{group.documents.map((d, index) => (
|
||||
<li className='p-0 m-0' key={d.id}>
|
||||
{!d.areas.length
|
||||
? <div
|
||||
?
|
||||
<div
|
||||
onClick={() => onDocumentClickHandler(d.id)}
|
||||
onDoubleClick={() => onDocumentDoubleClickHandler(d.id)}
|
||||
className={classNames(
|
||||
d.id === selectedDocumentId
|
||||
? 'bg-gray-900 text-white'
|
||||
@ -303,7 +325,22 @@ function Sidebar() {
|
||||
'group items-center py-2 text-base font-medium rounded-b-md pl-10',
|
||||
index !== 0 ? 'rounded-t-md' : '',
|
||||
)}>
|
||||
<a
|
||||
{selectedDocumentId === d.id && isEditDocumentNameInputShowing
|
||||
? <input
|
||||
type="text"
|
||||
name="documentName"
|
||||
id="documentName"
|
||||
autoFocus
|
||||
className="h-8 text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 block w-full rounded-none rounded-l-md border-late-700 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
||||
defaultValue={d.name}
|
||||
onBlur={onDocumentInputBlur}
|
||||
onKeyDown={(event) => {
|
||||
onEnterHandler(event,
|
||||
() => onConfirmDocumentNameChangeHandler(event.currentTarget.value))
|
||||
}}
|
||||
ref={editDocumentNameTextInput}
|
||||
/>
|
||||
: <a
|
||||
role='button'
|
||||
className={classNames(
|
||||
d.id === selectedDocumentId
|
||||
@ -314,10 +351,12 @@ function Sidebar() {
|
||||
>
|
||||
{d.name}
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
: <details>
|
||||
<summary
|
||||
onClick={() => onDocumentClickHandler(d.id)}
|
||||
onDoubleClick={() => onDocumentDoubleClickHandler(d.id)}
|
||||
className={classNames(
|
||||
d.id === selectedDocumentId
|
||||
? 'bg-gray-900 text-white'
|
||||
@ -326,7 +365,22 @@ function Sidebar() {
|
||||
index !== 0 ? 'rounded-t-md' : '',
|
||||
|
||||
)}>
|
||||
<a
|
||||
{selectedDocumentId === d.id && isEditDocumentNameInputShowing
|
||||
? <input // TODO: this
|
||||
type="text"
|
||||
name="documentName"
|
||||
id="documentName"
|
||||
autoFocus
|
||||
className="h-8 text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 block w-full rounded-none rounded-l-md border-late-700 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
||||
defaultValue={d.name}
|
||||
onBlur={onDocumentInputBlur}
|
||||
onKeyDown={(event) => {
|
||||
onEnterHandler(event,
|
||||
() => onConfirmDocumentNameChangeHandler(event.currentTarget.value))
|
||||
}}
|
||||
ref={editDocumentNameTextInput}
|
||||
/>
|
||||
: <a
|
||||
role='button'
|
||||
className={classNames(
|
||||
d.id === selectedDocumentId
|
||||
@ -337,6 +391,7 @@ function Sidebar() {
|
||||
>
|
||||
{d.name}
|
||||
</a>
|
||||
}
|
||||
</summary>
|
||||
<ul>
|
||||
{d.areas.map((a, index) => (
|
||||
@ -359,7 +414,7 @@ function Sidebar() {
|
||||
: <a
|
||||
role='button'
|
||||
onClick={() => onAreaClick(a.id)}
|
||||
onDoubleClick={() => onAreaDoubleclick(a.id)}
|
||||
onDoubleClick={() => onAreaDoubleClick(a.id)}
|
||||
className={classNames('text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
'group w-full flex items-center pr-2 py-2 text-left font-medium pl-8 text-xs',
|
||||
'rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 py-2 select-none',
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { DiffEditor } from '@monaco-editor/react'
|
||||
import { loader, DiffEditor } from '@monaco-editor/react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useProject } from '../../context/Project/provider'
|
||||
import type { DiffOnMount } from '@monaco-editor/react/'
|
||||
@ -6,18 +6,29 @@ import TextEditorButtons from './TextEditorButtons'
|
||||
import createDiffEditorInteractions from '../../useCases/createDiffEditorInteractions'
|
||||
import TextPreview from './TextPreview'
|
||||
import createDebounce from '../../utils/createDebounce'
|
||||
import LanguageSelect from './LanguageSelect'
|
||||
|
||||
loader.config({
|
||||
paths: {
|
||||
vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.34.0/min/vs',
|
||||
},
|
||||
})
|
||||
|
||||
let editorInteractions: ReturnType<typeof createDiffEditorInteractions>
|
||||
const editorHeightOffset = 234
|
||||
|
||||
const TextEditor = () => {
|
||||
const { selectedDocumentId, getProcessedAreasByDocumentId, requestUpdateDocumentUserMarkdown, getUserMarkdownByDocumentId } = useProject()
|
||||
const [editorHeight, setEditorHeight] = useState(window.innerHeight - 200)
|
||||
const { getSelectedDocument, getProcessedAreasByDocumentId, requestUpdateDocumentUserMarkdown, getUserMarkdownByDocumentId } = useProject()
|
||||
const [editorHeight, setEditorHeight] = useState(window.innerHeight - editorHeightOffset)
|
||||
const [editorValue, setEditorValue] = useState('')
|
||||
const [isEditorReady, setIsEditorReady] = useState(false)
|
||||
const [isPreviewOpen, setIsPreviewOpen] = useState(false)
|
||||
const [modifiedEditorValue, setModifiedEditorValue] = useState('')
|
||||
|
||||
const handleEditorDidMount: DiffOnMount = async (editor, monaco) => {
|
||||
const selectedDocument = getSelectedDocument()
|
||||
const selectedDocumentId = selectedDocument?.id || ''
|
||||
|
||||
const handleEditorDidMount: DiffOnMount = async (editor, _) => {
|
||||
const currentDocumentId = selectedDocumentId
|
||||
|
||||
editorInteractions = createDiffEditorInteractions(editor)
|
||||
@ -66,18 +77,23 @@ const TextEditor = () => {
|
||||
}, [selectedDocumentId, getProcessedAreasByDocumentId])
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
setEditorHeight(window.innerHeight - 200)
|
||||
setEditorHeight(window.innerHeight - editorHeightOffset)
|
||||
})
|
||||
|
||||
return <div className='relative m-0 p-0'>
|
||||
return <div className='m-0 p-0 relative'>
|
||||
<span className="flex z-0 rounded-md shadow-sm mb-2 mt-2 justify-between">
|
||||
{isEditorReady
|
||||
? <TextEditorButtons
|
||||
? <>
|
||||
<TextEditorButtons
|
||||
isPreviewOpen={isPreviewOpen}
|
||||
togglePreview={() => setIsPreviewOpen(!isPreviewOpen)}
|
||||
editorInteractions={editorInteractions}
|
||||
/>
|
||||
<LanguageSelect shouldUpdateDocument defaultLanguage={selectedDocument?.defaultLanguage} />
|
||||
</>
|
||||
: ''
|
||||
}
|
||||
</span>
|
||||
<DiffEditor
|
||||
original={editorValue}
|
||||
modified={modifiedEditorValue}
|
||||
@ -87,6 +103,7 @@ const TextEditor = () => {
|
||||
options={{
|
||||
renderMarginRevertIcon: true,
|
||||
enableSplitViewResizing: false,
|
||||
glyphMargin: true,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { ListBulletIcon, MinusIcon } from '@heroicons/react/20/solid'
|
||||
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline'
|
||||
import createDiffEditorInteractions, { MarkdownOperator } from '../../useCases/createDiffEditorInteractions'
|
||||
|
||||
function classNames(...classes: any[]) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
import classNames from '../../utils/classNames'
|
||||
|
||||
type Props = {
|
||||
editorInteractions: ReturnType<typeof createDiffEditorInteractions>
|
||||
@ -14,7 +11,8 @@ type Props = {
|
||||
|
||||
const TextEditorButtons = (props: Props) => {
|
||||
const { editorInteractions, togglePreview } = props
|
||||
return <span className="isolate inline-flex rounded-md shadow-sm">
|
||||
|
||||
return <span className="inline-flex rounded-md shadow-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={togglePreview}
|
||||
@ -143,7 +141,7 @@ const TextEditorButtons = (props: Props) => {
|
||||
type="button"
|
||||
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.DIVIDER)}
|
||||
className={classNames(
|
||||
'text-sm relative inline-flex items-center rounded-r-md border',
|
||||
'text-sm relative inline-flex items-center border',
|
||||
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
||||
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
||||
'focus:ring-indigo-500 italic',
|
||||
@ -151,6 +149,8 @@ const TextEditorButtons = (props: Props) => {
|
||||
<span className="sr-only">Divider</span>
|
||||
<MinusIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
|
||||
</span>
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ type Props = { markdown: string, height: number }
|
||||
|
||||
const TextPreview = (props: Props) => (
|
||||
<div
|
||||
className='absolute w-[calc(50%-14px)] top-[30px] bg-white overflow-y-scroll p-4 m-0'
|
||||
className='absolute w-[calc(50%-14px)] top-[46px] bg-white overflow-y-scroll p-4 m-0'
|
||||
style={{ 'height': `${props.height}px` }}>
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
import { useNavigation } from '../../context/Navigation/provider'
|
||||
import { workspaces } from '../../context/Navigation/types'
|
||||
import classNames from '../../utils/classNames'
|
||||
|
||||
const tabs = [
|
||||
{ displayName: 'Processor', type: workspaces.PROCESSOR },
|
||||
{ displayName: 'Text Editor', type: workspaces.TEXTEDITOR },
|
||||
{ displayName: 'Translator', type: workspaces.TRANSLATOR },
|
||||
{ displayName: 'Details', type: workspaces.DETAILS },
|
||||
{ displayName: 'Translator', type: workspaces.TRANSLATOR, disabled: true, },
|
||||
{ displayName: 'Details', type: workspaces.DETAILS, disabled: true, },
|
||||
]
|
||||
|
||||
function classNames(...classes: string[]) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
const getTabClasses = (isTabSelected: boolean, isDisabled?: boolean) => {
|
||||
if (isDisabled) return classNames('cursor-not-allowed w-1/4 py-4 px-1 text-center border-b-2 font-medium text-gray-200 text-sm')
|
||||
|
||||
const baseClasses = 'cursor-pointer w-1/4 py-4 px-1 text-center border-b-2 font-medium text-sm'
|
||||
if (isTabSelected) return classNames(baseClasses, 'border-indigo-500 text-indigo-600')
|
||||
else return classNames(baseClasses, 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300')
|
||||
}
|
||||
|
||||
export default function ToolTabs() {
|
||||
@ -24,14 +29,12 @@ export default function ToolTabs() {
|
||||
<nav className="-mb-px flex" aria-label="Tabs">
|
||||
{tabs.map((tab) => (
|
||||
<a
|
||||
aria-disabled={tab.disabled}
|
||||
key={tab.displayName}
|
||||
onClick={_ => setSelectedWorkspace(tab.type)}
|
||||
className={classNames(
|
||||
getIsSelectedTab(tab.type)
|
||||
? 'border-indigo-500 text-indigo-600'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300',
|
||||
'cursor-pointer w-1/4 py-4 px-1 text-center border-b-2 font-medium text-sm'
|
||||
)}
|
||||
onClick={_ => {
|
||||
if (!tab.disabled) setSelectedWorkspace(tab.type)
|
||||
}}
|
||||
className={getTabClasses(getIsSelectedTab(tab.type), tab.disabled)}
|
||||
aria-current={getIsSelectedTab(tab.type) ? 'page' : undefined}
|
||||
>
|
||||
{tab.displayName}
|
||||
|
||||
@ -23,6 +23,7 @@ const makeDefaultProject = (): ProjectContextType => ({
|
||||
createNewProject: (name: string) => Promise.resolve(new ipc.Session()),
|
||||
requestUpdateCurrentUser: (updatedUserProps: UserProps) => Promise.resolve(new ipc.User()),
|
||||
requestChooseUserAvatar: () => Promise.resolve(''),
|
||||
requestUpdateDocument: ({}) => Promise.resolve(new ipc.Document),
|
||||
})
|
||||
|
||||
export default makeDefaultProject
|
||||
|
||||
@ -7,9 +7,10 @@ import {
|
||||
RequestAddDocument, RequestAddDocumentGroup, RequestAddProcessedArea,
|
||||
RequestUpdateArea, RequestUpdateCurrentUser, RequestUpdateDocumentUserMarkdown,
|
||||
RequestChooseUserAvatar,
|
||||
RequestUpdateDocument,
|
||||
} from '../../wailsjs/wailsjs/go/ipc/Channel'
|
||||
import { ipc } from '../../wailsjs/wailsjs/go/models'
|
||||
import { AddAreaProps, AreaProps, ProjectContextType, ProjectProps, UserProps } from './types'
|
||||
import { AddAreaProps, AreaProps, ProjectContextType, ProjectProps, UpdateDocumentRequest, UserProps } from './types'
|
||||
import makeDefaultProject from './makeDefaultProject'
|
||||
|
||||
const ProjectContext = createContext<ProjectContextType>(makeDefaultProject())
|
||||
@ -122,6 +123,13 @@ export function ProjectProvider({ children, projectProps }: Props) {
|
||||
return filePathResponse
|
||||
}
|
||||
|
||||
const requestUpdateDocument = async (docuemntProps: UpdateDocumentRequest) => {
|
||||
console.log('request: ', docuemntProps)
|
||||
const response = await RequestUpdateDocument(new ipc.Document(docuemntProps))
|
||||
await updateDocuments()
|
||||
return response
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!documents.length && !groups.length) updateDocuments()
|
||||
}, [documents.length, groups.length])
|
||||
@ -148,6 +156,7 @@ export function ProjectProvider({ children, projectProps }: Props) {
|
||||
createNewProject,
|
||||
requestUpdateCurrentUser,
|
||||
requestChooseUserAvatar,
|
||||
requestUpdateDocument,
|
||||
}
|
||||
|
||||
return <ProjectContext.Provider value={value}>
|
||||
|
||||
@ -26,6 +26,16 @@ export type UserProps = {
|
||||
email?: string
|
||||
}
|
||||
|
||||
export type UpdateDocumentRequest = {
|
||||
id?: string,
|
||||
projectId?: string,
|
||||
groupId?: string,
|
||||
name?: string,
|
||||
path?: string,
|
||||
areas?: ipc.Area[]
|
||||
defaultLanguage?: ipc.Language
|
||||
}
|
||||
|
||||
export type ProjectContextType = {
|
||||
getSelectedDocument: () => ipc.Document | undefined
|
||||
getAreaById: (areaId: string) => ipc.Area | undefined
|
||||
@ -45,4 +55,5 @@ export type ProjectContextType = {
|
||||
createNewProject: (name: string) => Promise<ipc.Session>
|
||||
requestUpdateCurrentUser: (updatedUserProps: UserProps) => Promise<ipc.User>
|
||||
requestChooseUserAvatar: () => Promise<string>
|
||||
requestUpdateDocument: (request: UpdateDocumentRequest) => Promise<ipc.Document>
|
||||
} & ProjectProps
|
||||
@ -1,5 +1,4 @@
|
||||
import { NextPage } from 'next'
|
||||
import { useEffect } from 'react'
|
||||
import MainHead from '../components/head'
|
||||
import MainProject from '../components/project/Main'
|
||||
import User from '../components/settings/User'
|
||||
@ -12,12 +11,7 @@ import { useProject } from '../context/Project/provider'
|
||||
const Home: NextPage = () => {
|
||||
|
||||
const { currentSession } = useProject()
|
||||
const { selectedMainPage, setSelectedMainPage } = useNavigation()
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentSession?.user?.localId) setSelectedMainPage(mainPages.EDITUSER)
|
||||
else if (!currentSession?.project?.id) setSelectedMainPage(mainPages.SELECTPROJECT)
|
||||
},)
|
||||
const { selectedMainPage } = useNavigation()
|
||||
|
||||
const renderSelectedMainPage = () => {
|
||||
if (selectedMainPage === mainPages.SELECTPROJECT) return <MainProject />
|
||||
@ -31,12 +25,10 @@ const Home: NextPage = () => {
|
||||
else return <MainProject />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
return <>
|
||||
<MainHead />
|
||||
{renderSelectedMainPage()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
|
||||
@ -10,7 +10,7 @@ export enum MarkdownOperator {
|
||||
ITALLICS = '_',
|
||||
BOLD = '**',
|
||||
BULLET = '* ',
|
||||
DIVIDER = '\n\n---\n\n'
|
||||
DIVIDER = '\n\n---\n'
|
||||
}
|
||||
|
||||
const wrapperOperators = [
|
||||
@ -43,8 +43,6 @@ const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEdito
|
||||
})
|
||||
|
||||
if (operator == MarkdownOperator.DIVIDER) {
|
||||
console.log('lineOfCursor:', lineOfCursor)
|
||||
console.log('lengthOfLine:', lengthOfLine)
|
||||
range = {
|
||||
startLineNumber,
|
||||
startColumn: lengthOfLine,
|
||||
@ -72,12 +70,9 @@ const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEdito
|
||||
newText = `${operator}${modifiedEditor.getModel()?.getValueInRange(range)}`
|
||||
}
|
||||
|
||||
modifiedEditor.executeEdits('editor', [{
|
||||
range,
|
||||
text: newText
|
||||
}])
|
||||
|
||||
modifiedEditor.executeEdits('editor', [{ range, text: newText }])
|
||||
modifiedEditor.pushUndoStop()
|
||||
modifiedEditor.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
frontend/utils/classNames.ts
Normal file
3
frontend/utils/classNames.ts
Normal file
@ -0,0 +1,3 @@
|
||||
const classNames = (...classes: any[]) => classes.filter(Boolean).join(' ')
|
||||
|
||||
export default classNames
|
||||
8
frontend/utils/getSupportedLanguages.ts
Normal file
8
frontend/utils/getSupportedLanguages.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { GetSuppportedLanguages } from '../wailsjs/wailsjs/go/ipc/Channel'
|
||||
|
||||
const getSupportedLanguages = async () => {
|
||||
const response = await GetSuppportedLanguages()
|
||||
return response
|
||||
}
|
||||
|
||||
export default getSupportedLanguages
|
||||
4
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
4
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
@ -14,6 +14,8 @@ export function GetDocuments():Promise<ipc.GetDocumentsResponse>;
|
||||
|
||||
export function GetProcessedAreasByDocumentId(arg1:string):Promise<Array<ipc.ProcessedArea>>;
|
||||
|
||||
export function GetSuppportedLanguages():Promise<Array<ipc.Language>>;
|
||||
|
||||
export function GetUserMarkdownByDocumentId(arg1:string):Promise<ipc.UserMarkdown>;
|
||||
|
||||
export function RequestAddArea(arg1:string,arg2:ipc.Area):Promise<ipc.Area>;
|
||||
@ -30,4 +32,6 @@ export function RequestUpdateArea(arg1:ipc.Area):Promise<ipc.Area>;
|
||||
|
||||
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>;
|
||||
|
||||
@ -26,6 +26,10 @@ export function GetProcessedAreasByDocumentId(arg1) {
|
||||
return window['go']['ipc']['Channel']['GetProcessedAreasByDocumentId'](arg1);
|
||||
}
|
||||
|
||||
export function GetSuppportedLanguages() {
|
||||
return window['go']['ipc']['Channel']['GetSuppportedLanguages']();
|
||||
}
|
||||
|
||||
export function GetUserMarkdownByDocumentId(arg1) {
|
||||
return window['go']['ipc']['Channel']['GetUserMarkdownByDocumentId'](arg1);
|
||||
}
|
||||
@ -58,6 +62,10 @@ export function RequestUpdateCurrentUser(arg1) {
|
||||
return window['go']['ipc']['Channel']['RequestUpdateCurrentUser'](arg1);
|
||||
}
|
||||
|
||||
export function RequestUpdateDocument(arg1) {
|
||||
return window['go']['ipc']['Channel']['RequestUpdateDocument'](arg1);
|
||||
}
|
||||
|
||||
export function RequestUpdateDocumentUserMarkdown(arg1, arg2) {
|
||||
return window['go']['ipc']['Channel']['RequestUpdateDocumentUserMarkdown'](arg1, arg2);
|
||||
}
|
||||
|
||||
@ -1,5 +1,21 @@
|
||||
export namespace ipc {
|
||||
|
||||
export class Language {
|
||||
displayName: string;
|
||||
processCode: string;
|
||||
translateCode: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Language(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.displayName = source["displayName"];
|
||||
this.processCode = source["processCode"];
|
||||
this.translateCode = source["translateCode"];
|
||||
}
|
||||
}
|
||||
export class Area {
|
||||
id: string;
|
||||
name: string;
|
||||
@ -7,6 +23,7 @@ export namespace ipc {
|
||||
startY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
language: Language;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Area(source);
|
||||
@ -20,6 +37,25 @@ export namespace ipc {
|
||||
this.startY = source["startY"];
|
||||
this.endX = source["endX"];
|
||||
this.endY = source["endY"];
|
||||
this.language = this.convertValues(source["language"], Language);
|
||||
}
|
||||
|
||||
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 Document {
|
||||
@ -29,6 +65,7 @@ export namespace ipc {
|
||||
path: string;
|
||||
projectId: string;
|
||||
areas: Area[];
|
||||
defaultLanguage: Language;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Document(source);
|
||||
@ -42,6 +79,7 @@ export namespace ipc {
|
||||
this.path = source["path"];
|
||||
this.projectId = source["projectId"];
|
||||
this.areas = this.convertValues(source["areas"], Area);
|
||||
this.defaultLanguage = this.convertValues(source["defaultLanguage"], Language);
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
@ -113,6 +151,7 @@ export namespace ipc {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class User {
|
||||
id: string;
|
||||
localId: string;
|
||||
@ -335,9 +374,45 @@ export namespace ipc {
|
||||
|
||||
|
||||
|
||||
export class ProjectSettings {
|
||||
defaultProcessLanguage: Language;
|
||||
defaultTranslateTargetLanguage: Language;
|
||||
IsHosted: boolean;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new ProjectSettings(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.defaultProcessLanguage = this.convertValues(source["defaultProcessLanguage"], Language);
|
||||
this.defaultTranslateTargetLanguage = this.convertValues(source["defaultTranslateTargetLanguage"], Language);
|
||||
this.IsHosted = source["IsHosted"];
|
||||
}
|
||||
|
||||
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 Project {
|
||||
id: string;
|
||||
organizationId: string;
|
||||
name: string;
|
||||
settings: ProjectSettings;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Project(source);
|
||||
@ -346,9 +421,30 @@ export namespace ipc {
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.id = source["id"];
|
||||
this.organizationId = source["organizationId"];
|
||||
this.name = source["name"];
|
||||
this.settings = this.convertValues(source["settings"], ProjectSettings);
|
||||
}
|
||||
|
||||
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 Session {
|
||||
project: Project;
|
||||
organization: Organization;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package ipc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
app "textualize/core/App"
|
||||
document "textualize/core/Document"
|
||||
session "textualize/core/Session"
|
||||
@ -19,7 +20,15 @@ func (c *Channel) GetDocumentById(id string) Document {
|
||||
var jsonAreas []Area
|
||||
|
||||
for _, a := range foundDocument.Areas {
|
||||
jsonAreas = append(jsonAreas, Area(a))
|
||||
jsonAreas = append(jsonAreas, Area{
|
||||
Id: a.Id,
|
||||
Name: a.Name,
|
||||
StartX: a.StartX,
|
||||
StartY: a.StartY,
|
||||
EndX: a.EndX,
|
||||
EndY: a.EndY,
|
||||
Language: Language(a.Language),
|
||||
})
|
||||
}
|
||||
response := Document{
|
||||
Id: foundDocument.Id,
|
||||
@ -44,7 +53,15 @@ func (c *Channel) GetDocuments() GetDocumentsResponse {
|
||||
for _, d := range documents {
|
||||
jsonAreas := make([]Area, 0)
|
||||
for _, a := range d.Areas {
|
||||
jsonAreas = append(jsonAreas, Area(a))
|
||||
jsonAreas = append(jsonAreas, Area{
|
||||
Id: a.Id,
|
||||
Name: a.Name,
|
||||
StartX: a.StartX,
|
||||
StartY: a.StartY,
|
||||
EndX: a.EndX,
|
||||
EndY: a.EndY,
|
||||
Language: Language(a.Language),
|
||||
})
|
||||
}
|
||||
|
||||
jsonDocument := Document{
|
||||
@ -54,6 +71,7 @@ func (c *Channel) GetDocuments() GetDocumentsResponse {
|
||||
Path: d.Path,
|
||||
ProjectId: d.ProjectId,
|
||||
Areas: jsonAreas,
|
||||
DefaultLanguage: Language(d.DefaultLanguage),
|
||||
}
|
||||
response.Documents = append(response.Documents, jsonDocument)
|
||||
}
|
||||
@ -182,9 +200,14 @@ func (c *Channel) RequestAddArea(documentId string, area Area) Area {
|
||||
EndX: area.EndX,
|
||||
StartY: area.StartY,
|
||||
EndY: area.EndY,
|
||||
Language: app.Language(area.Language),
|
||||
}
|
||||
foundDocument.AddArea(newArea)
|
||||
return Area(newArea)
|
||||
|
||||
responseArea := area
|
||||
responseArea.Id = id
|
||||
|
||||
return responseArea
|
||||
}
|
||||
|
||||
func (c *Channel) RequestUpdateArea(updatedArea Area) Area {
|
||||
@ -206,11 +229,41 @@ func (c *Channel) RequestUpdateArea(updatedArea Area) Area {
|
||||
}
|
||||
|
||||
return Area{
|
||||
Id: updatedArea.Id,
|
||||
Name: updatedArea.Name,
|
||||
StartX: updatedArea.StartX,
|
||||
StartY: updatedArea.StartY,
|
||||
EndX: updatedArea.EndX,
|
||||
EndY: updatedArea.EndY,
|
||||
Id: areaToUpdate.Id,
|
||||
Name: areaToUpdate.Name,
|
||||
StartX: areaToUpdate.StartX,
|
||||
StartY: areaToUpdate.StartY,
|
||||
EndX: areaToUpdate.EndX,
|
||||
EndY: areaToUpdate.EndY,
|
||||
Language: Language(areaToUpdate.Language),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Channel) RequestUpdateDocument(updatedDocument Document) Document {
|
||||
documentToUpdate := document.GetDocumentCollection().GetDocumentById(updatedDocument.Id)
|
||||
|
||||
if documentToUpdate == nil {
|
||||
return Document{}
|
||||
}
|
||||
|
||||
if updatedDocument.Id != "" {
|
||||
documentToUpdate.Id = updatedDocument.Id
|
||||
}
|
||||
if updatedDocument.Name != "" {
|
||||
documentToUpdate.Name = updatedDocument.Name
|
||||
}
|
||||
if updatedDocument.GroupId != "" {
|
||||
documentToUpdate.GroupId = updatedDocument.GroupId
|
||||
}
|
||||
if updatedDocument.Path != "" {
|
||||
documentToUpdate.Path = updatedDocument.Path
|
||||
}
|
||||
if updatedDocument.DefaultLanguage.DisplayName != "" {
|
||||
documentToUpdate.DefaultLanguage = app.Language(updatedDocument.DefaultLanguage)
|
||||
}
|
||||
|
||||
fmt.Println("updated doc")
|
||||
fmt.Println(document.GetDocumentCollection().GetDocumentById(updatedDocument.Id))
|
||||
|
||||
return updatedDocument
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ type Document struct {
|
||||
Path string `json:"path"`
|
||||
ProjectId string `json:"projectId"`
|
||||
Areas []Area `json:"areas"`
|
||||
DefaultLanguage Language `json:"defaultLanguage"`
|
||||
}
|
||||
|
||||
type DocumentCollection struct {
|
||||
@ -34,6 +35,7 @@ type Area struct {
|
||||
StartY int `json:"startY"`
|
||||
EndX int `json:"endX"`
|
||||
EndY int `json:"endY"`
|
||||
Language Language `json:"language"`
|
||||
}
|
||||
|
||||
type ProcessedBoundingBox struct {
|
||||
@ -98,7 +100,15 @@ type Organization struct {
|
||||
|
||||
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 {
|
||||
@ -106,3 +116,9 @@ type Session struct {
|
||||
Organization Organization `json:"organization"`
|
||||
User User `json:"user"`
|
||||
}
|
||||
|
||||
type Language struct {
|
||||
DisplayName string `json:"displayName"`
|
||||
ProcessCode string `json:"processCode"`
|
||||
TranslateCode string `json:"translateCode"`
|
||||
}
|
||||
|
||||
@ -17,8 +17,22 @@ func (c *Channel) GetCurrentSession() Session {
|
||||
sessionUsers = append(sessionUsers, User(u))
|
||||
}
|
||||
|
||||
currentProject := currentSession.Project
|
||||
currentDefaultProcessLanguage := Language(currentProject.Settings.DefaultProcessLanguage)
|
||||
currentDefaultTranslateTargetLanguage := Language(currentProject.Settings.DefaultTranslateTargetLanguage)
|
||||
project := Project{
|
||||
Id: currentProject.Id,
|
||||
Name: currentProject.Name,
|
||||
OrganizationId: currentProject.OrganizationId,
|
||||
Settings: ProjectSettings{
|
||||
DefaultProcessLanguage: currentDefaultProcessLanguage,
|
||||
DefaultTranslateTargetLanguage: currentDefaultTranslateTargetLanguage,
|
||||
IsHosted: currentProject.Settings.IsHosted,
|
||||
},
|
||||
}
|
||||
|
||||
return Session{
|
||||
Project: Project(currentSession.Project),
|
||||
Project: Project(project),
|
||||
User: User(currentSession.User),
|
||||
Organization: Organization{
|
||||
Id: currentSession.Organization.Id,
|
||||
@ -34,6 +48,7 @@ func (c *Channel) CreateNewProject(name string) Session {
|
||||
|
||||
currentSession.Project = session.Project{
|
||||
Id: uuid.NewString(),
|
||||
OrganizationId: currentSession.Project.OrganizationId,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
@ -86,3 +101,15 @@ func (c *Channel) RequestChooseUserAvatar() string {
|
||||
return filePath
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Channel) GetSuppportedLanguages() []Language {
|
||||
supportedLanguages := app.GetSuppportedLanguages()
|
||||
|
||||
var response []Language
|
||||
|
||||
for _, l := range supportedLanguages {
|
||||
response = append(response, Language(l))
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user