From 8eada10e87c2d86232cfd39e20787cafd0ad9d3f Mon Sep 17 00:00:00 2001 From: Joshua Shoemaker Date: Mon, 20 Feb 2023 21:47:14 -0600 Subject: [PATCH] feat: default document language select --- core/App/app.go | 21 ++++ core/Document/Document.go | 28 +++-- core/Session/Project.go | 16 ++- frontend/.eslintrc.json | 3 +- .../components/project/NewProjectModal.tsx | 45 ++++++- frontend/components/utils/Search.tsx | 7 +- .../components/workspace/DocumentRenderer.tsx | 46 ++++--- .../components/workspace/LanguageSelect.tsx | 87 +++++++++++++ frontend/components/workspace/Main.tsx | 6 +- .../workspace/NoSelectedDocument.tsx | 13 ++ frontend/components/workspace/Sidebar.tsx | 119 +++++++++++++----- frontend/components/workspace/TextEditor.tsx | 45 ++++--- .../workspace/TextEditorButtons.tsx | 12 +- frontend/components/workspace/TextPreview.tsx | 2 +- frontend/components/workspace/ToolTabs.tsx | 25 ++-- .../context/Project/makeDefaultProject.ts | 1 + frontend/context/Project/provider.tsx | 11 +- frontend/context/Project/types.ts | 11 ++ frontend/pages/index.tsx | 24 ++-- .../useCases/createDiffEditorInteractions.ts | 11 +- frontend/utils/classNames.ts | 3 + frontend/utils/getSupportedLanguages.ts | 8 ++ frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts | 4 + frontend/wailsjs/wailsjs/go/ipc/Channel.js | 8 ++ frontend/wailsjs/wailsjs/go/models.ts | 96 ++++++++++++++ ipc/Documents.go | 95 ++++++++++---- ipc/JsonEntities.go | 44 ++++--- ipc/Session.go | 33 ++++- 28 files changed, 648 insertions(+), 176 deletions(-) create mode 100644 frontend/components/workspace/LanguageSelect.tsx create mode 100644 frontend/utils/classNames.ts create mode 100644 frontend/utils/getSupportedLanguages.ts diff --git a/core/App/app.go b/core/App/app.go index 93f3d12..86fdb1e 100644 --- a/core/App/app.go +++ b/core/App/app.go @@ -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", + }, + } +} diff --git a/core/Document/Document.go b/core/Document/Document.go index 1d746f6..2cc13ee 100644 --- a/core/Document/Document.go +++ b/core/Document/Document.go @@ -1,21 +1,25 @@ package document +import app "textualize/core/App" + type Entity struct { - Id string - GroupId string - Name string - Path string - ProjectId string - Areas []Area + Id string + GroupId string + Name string + Path string + ProjectId string + Areas []Area + DefaultLanguage app.Language } type Area struct { - Id string - Name string - StartX int - StartY int - EndX int - EndY int + Id string + Name string + StartX int + StartY int + EndX int + EndY int + Language app.Language } func (e *Entity) AddArea(a Area) { diff --git a/core/Session/Project.go b/core/Session/Project.go index 17b069c..f56a645 100644 --- a/core/Session/Project.go +++ b/core/Session/Project.go @@ -1,6 +1,18 @@ package session +import ( + app "textualize/core/App" +) + type Project struct { - Id string - Name string + Id string + OrganizationId string + Name string + Settings ProjectSettings +} + +type ProjectSettings struct { + DefaultProcessLanguage app.Language + DefaultTranslateTargetLanguage app.Language + IsHosted bool } diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index 2509fdf..668d51e 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -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/*"] } diff --git a/frontend/components/project/NewProjectModal.tsx b/frontend/components/project/NewProjectModal.tsx index a072084..232a103 100644 --- a/frontend/components/project/NewProjectModal.tsx +++ b/frontend/components/project/NewProjectModal.tsx @@ -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(null) + const [isHostedProjectSelected, setIsHostedProjectSelected] = useState(false) return (

Name Your New Project

-

A unique name will be best. This can be changed later.

+

A unique name makes the project easier to identify later. This can be changed later.

-
-
+ +
@@ -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} />
+ + + + + Hosted Project + + + A hosted project can store and process data on a server. This can be added later. Currently unavalible + + + + + + diff --git a/frontend/components/utils/Search.tsx b/frontend/components/utils/Search.tsx index 861aa34..7a026f5 100644 --- a/frontend/components/utils/Search.tsx +++ b/frontend/components/utils/Search.tsx @@ -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" > - + {userNavigation.map((item) => ( {({ active }) => ( diff --git a/frontend/components/workspace/DocumentRenderer.tsx b/frontend/components/workspace/DocumentRenderer.tsx index 35cf211..90cc452 100644 --- a/frontend/components/workspace/DocumentRenderer.tsx +++ b/frontend/components/workspace/DocumentRenderer.tsx @@ -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,28 +147,33 @@ const DocumentRenderer = () => { useEffect(() => { if (selectedDocument?.path) applyDocumentToCanvas(selectedDocument.path) - }, [selectedDocument?.id]) - - useEffect(() => { applyAreasToCanvas() - }, [areas, areas?.length]) + }) - return
- - - + return
+
+

+ {getSelectedDocument()?.name} +

+ +
+
+ + + +
} diff --git a/frontend/components/workspace/LanguageSelect.tsx b/frontend/components/workspace/LanguageSelect.tsx new file mode 100644 index 0000000..145e778 --- /dev/null +++ b/frontend/components/workspace/LanguageSelect.tsx @@ -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([]) + const [query, setQuery] = useState('') + const [selectedLanguage, setSelectedLanguage] = useState(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 +
+ setQuery(event.target.value)} + displayValue={(language: ipc.Language) => language?.displayName} + placeholder='Document Language' + /> + + + + + {filteredLanguages.length > 0 && ( + + {filteredLanguages.map((l) => ( + classNames( + 'relative cursor-default select-none py-2 pl-3 pr-9', + active ? 'bg-indigo-600 text-white' : 'text-gray-900' + )}> + {({ active, selected }) => <> + {l.displayName} + {selected && ( + + + )} + + } + + ))} + + )} +
+
+} + +export default LanguageSelect diff --git a/frontend/components/workspace/Main.tsx b/frontend/components/workspace/Main.tsx index 3292dbc..64c57fa 100644 --- a/frontend/components/workspace/Main.tsx +++ b/frontend/components/workspace/Main.tsx @@ -22,9 +22,9 @@ const renderSelectedWorkSpace = () => {
-

- {getSelectedDocument()?.name || 'Image Processor'} -

+ {!getSelectedDocument()?.id ?

+ Image Processor +

: ''}
{ renderSelectedWorkSpace() }
diff --git a/frontend/components/workspace/NoSelectedDocument.tsx b/frontend/components/workspace/NoSelectedDocument.tsx index 1dc10db..1bebea6 100644 --- a/frontend/components/workspace/NoSelectedDocument.tsx +++ b/frontend/components/workspace/NoSelectedDocument.tsx @@ -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 (
+ :
+ 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} + + }