feat: write user and project to disk

does not contain documents or groups
This commit is contained in:
Joshua Shoemaker 2023-03-26 20:08:23 -05:00
parent 01529b731c
commit d9916b967a
24 changed files with 703 additions and 206 deletions

View File

@ -2,6 +2,10 @@ package app
import (
"context"
"fmt"
document "textualize/core/Document"
session "textualize/core/Session"
storage "textualize/storage/Local"
)
type App struct {
@ -20,25 +24,12 @@ func GetInstance() *App {
func (a *App) Startup(ctx context.Context) {
a.Context = ctx
}
localUserData := storage.ReadLocalUserData()
session.InitializeModule(session.Session{
User: session.User(localUserData),
})
type Language struct {
DisplayName string
ProcessCode string
TranslateCode string
}
document.InitizeModule()
func GetSuppportedLanguages() []Language {
return []Language{
{
DisplayName: "English",
ProcessCode: "eng",
TranslateCode: "en",
},
{
DisplayName: "Hebrew",
ProcessCode: "heb",
TranslateCode: "he",
},
}
fmt.Println(localUserData)
}

22
core/Consts/Consts.go Normal file
View File

@ -0,0 +1,22 @@
package consts
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",
},
}
}

View File

@ -1,7 +1,7 @@
package document
import (
app "textualize/core/App"
consts "textualize/core/Consts"
)
type Entity struct {
@ -11,7 +11,7 @@ type Entity struct {
Path string
ProjectId string
Areas []Area
DefaultLanguage app.Language
DefaultLanguage consts.Language
}
type Area struct {
@ -21,7 +21,7 @@ type Area struct {
StartY int
EndX int
EndY int
Language app.Language
Language consts.Language
Order int
}

View File

@ -5,6 +5,7 @@ type Group struct {
ParentId string
ProjectId string
Name string
Order int
}
type GroupCollection struct {
@ -26,3 +27,14 @@ func GetGroupCollection() *GroupCollection {
func (collection *GroupCollection) AddDocumentGroup(group Group) {
collection.Groups = append(collection.Groups, group)
}
func (collection *GroupCollection) GetGroupById(groupId string) *Group {
var foundGroup *Group
for index, g := range collection.Groups {
if g.Id == groupId {
foundGroup = &collection.Groups[index]
}
}
return foundGroup
}

View File

@ -30,7 +30,7 @@ type ProcessedArea struct {
Id string
DocumentId string
FullText string
Order int // TODO: make reorder feature
Order int
Lines []ProcessedLine
}

View File

@ -1,7 +1,7 @@
package session
import (
app "textualize/core/App"
consts "textualize/core/Consts"
)
type Project struct {
@ -12,7 +12,7 @@ type Project struct {
}
type ProjectSettings struct {
DefaultProcessLanguage app.Language
DefaultTranslateTargetLanguage app.Language
DefaultProcessLanguage consts.Language
DefaultTranslateTargetLanguage consts.Language
IsHosted bool
}

View File

@ -6,13 +6,19 @@ import { Fragment, useState } from 'react'
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 NewProjectModal from './NewProjectModal'
import ProjectListModal from './ProjectListModal'
const MainProject = () => {
const [isNewProjectModalOpen, setIsNewProjectModalOpen] = useState(false)
const [isProjectListModal, setIsProjectListModal] = useState(false)
const [canPopoverBeOpen, setCanPopoverBeOpen] = useState(true)
const { createNewProject } = useProject()
const [avalibleProjects, setAvalibleProjects] = useState<ipc.Project[]>([])
const { createNewProject, requestSelectProjectByName } = useProject()
const { setSelectedMainPage } = useNavigation()
const buttonOptions = [
@ -31,6 +37,11 @@ const MainProject = () => {
icon: <FolderOpenIcon className='w-10 h-9 stroke-slate-600' />,
onClick: () => {
setCanPopoverBeOpen(false)
GetAllLocalProjects().then(response => {
console.log(response)
setAvalibleProjects(response)
setIsProjectListModal(true)
})
},
},
{
@ -50,10 +61,20 @@ const MainProject = () => {
setSelectedMainPage(mainPages.WORKSPACE)
}
const onSelectProjectHandler = async (name: string) => {
const successfulResponse = await requestSelectProjectByName(name)
setIsProjectListModal(false)
setCanPopoverBeOpen(true)
if (successfulResponse) setSelectedMainPage(mainPages.WORKSPACE)
}
return <main className=" text-gray-100 h-screen overflow-y-scroll">
{isNewProjectModalOpen ? <NewProjectModal onCreateNewProjectHandler={onCreateNewProjectHandler} /> : ''}
{isProjectListModal ? <ProjectListModal onSelectProjectHandler={onSelectProjectHandler} projects={avalibleProjects} /> : '' }
<div className="py-20 px-6 sm:px-6 sm:py-32 lg:px-8">
<div className="mx-auto max-w-2xl text-center">

View File

@ -0,0 +1,49 @@
import { ipc } from '../../wailsjs/wailsjs/go/models'
type Props = { projects: ipc.Project[], onSelectProjectHandler: (projectName: string) => void }
const ProjectListModal = (props: Props) => {
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">Select your Existing Project</h3>
<div className="mt-6 flow-root">
<ul role="list" className="-my-5 divide-y divide-gray-200">
{props.projects.map((p) => (
<li key={p.id} className="py-4">
<div className="flex items-center space-x-4">
<div className="flex-shrink-0">
{/* <img className="h-8 w-8 rounded-full" src={p.imageUrl} alt="" /> */}
</div>
<div className="min-w-0 flex-1">
<h2 className="truncate text-xl text-gray-900 font-bold">{p.name}</h2>
<p className="truncate text-xs text-gray-400">{'id: ' + p.id}</p>
</div>
<div>
<a
href="#"
onClick={() => props.onSelectProjectHandler(p.name)}
className="inline-flex items-center rounded-full bg-white px-2.5 py-1 text-xs font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
>
Open
</a>
</div>
</div>
</li>
))}
</ul>
</div>
<div className="mt-6">
{/* <a
href="#"
className="flex w-full items-center justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0"
>
View all
</a> */}
</div>
</div>
</div>
)
}
export default ProjectListModal

View File

@ -19,6 +19,10 @@ const Search = () => {
name: 'Document Workspace',
onClick: () => { setSelectedMainPage(mainPages.WORKSPACE) }
},
{
name: 'Change Project',
onClick: () => { setSelectedMainPage(mainPages.SELECTPROJECT) }
},
{
name: 'Sign Out',
onClick: () => { setSelectedMainPage(mainPages.SELECTPROJECT) }

View File

@ -66,7 +66,8 @@ const AreaLineItem = (props: { area: SidebarArea, documentId: string, index: num
}
const onAreaDropEnd = (areaId: string) => {
const areaDroppedOn = getAreaById(areaId)
const areaDroppedOn = getAreaById(dragOverAreaId)
console.log(areaDroppedOn)
if (!areaDroppedOn) return
requestChangeAreaOrder(areaId, areaDroppedOn.order)
setDragOverAreaId('')

View File

@ -1,7 +1,7 @@
'use client'
import { DocumentPlusIcon, PlusIcon, XMarkIcon } from '@heroicons/react/24/outline'
import React, { useRef } from 'react'
import React, { useRef, useState } from 'react'
import { useProject } from '../../../context/Project/provider'
import classNames from '../../../utils/classNames'
import onEnterHandler from '../../../utils/onEnterHandler'
@ -14,9 +14,11 @@ import { SidebarGroup } from './types'
const GroupLineItem = (props: { group: SidebarGroup }) => {
const {
requestAddDocument
requestAddDocument,
requestChangeGroupOrder,
getGroupById,
} = useProject()
const {
selectedGroupId,
isAddNewDocumentInputShowing,
@ -28,6 +30,8 @@ const GroupLineItem = (props: { group: SidebarGroup }) => {
const addDocumentTextInput = useRef<HTMLInputElement>(null)
const [dragOverGroupId, setDragOverGroupId] = useState('')
const onConfirmAddDocumentClickHandler = async (groupId: string) => {
const documentName = addDocumentTextInput.current?.value
if (!documentName) return
@ -50,6 +54,22 @@ const GroupLineItem = (props: { group: SidebarGroup }) => {
setIsAddNewGroupInputShowing(false)
}
const onGroupDragOver = (groupId: string) => {
setDragOverGroupId(groupId)
}
const onGroupDragStart = (groupId: string) => {
setSelectedGroupId(groupId)
}
const onGroupDropEnd = (groupId: string) => {
const areaDroppedOn = getGroupById(groupId)
console.log('areaDroppedOn', areaDroppedOn)
if (!areaDroppedOn) return
const response = requestChangeGroupOrder(groupId, areaDroppedOn.order)
setDragOverGroupId('')
}
const renderAddNewDocument = (groupId: string) => {
return isAddNewDocumentInputShowing && selectedGroupId === groupId
? <div className="flex rounded-md shadow-sm">
@ -83,7 +103,8 @@ const GroupLineItem = (props: { group: SidebarGroup }) => {
<PlusIcon className="h-7 w-5" aria-hidden="true" />
</button>
</div>
: <a
:
<a
role='button'
className={classNames(
'text-gray-300 hover:bg-gray-700 hover:text-white',
@ -98,156 +119,160 @@ const GroupLineItem = (props: { group: SidebarGroup }) => {
}
return (<details key={props.group.name} open={props.group.id === selectedGroupId}>
<summary className={classNames(
props.group.id === selectedGroupId
? 'bg-gray-900 text-white'
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
'group items-center px-2 py-2 text-base font-medium rounded-t-md'
)}>
<a role='button'>{props.group.name}</a>
</summary>
<ul>
{props.group.documents.map((d, index) => (
<DocumentLineItem key={d.id} document={d} index={index} groupId={props.group.id} />
// <li className='p-0 m-0' key={d.id}>
// {!d.areas.length
// ?
// <div
// onClick={() => 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
// ? <input
// type="text"
// name="documentName"
// id="documentName"
// autoFocus
// className="h-8 w-[calc(100%-18px)] text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 inline-block 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
// ? 'bg-gray-900 text-white'
// : 'text-gray-300 hover:bg-gray-700 hover:text-white',
// 'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 '
// )}
// >
// {d.name}
// </a>
// }
// </div>
// : <details>
// <summary
// onClick={() => 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' : '',
<summary
draggable
onDragOver={() => onGroupDragOver(props.group.id)}
onDragEnd={() => onGroupDropEnd(props.group.id)}
className={classNames(
'group items-center px-2 py-2 text-base font-medium rounded-t-md',
selectedGroupId === props.group.id ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white',
(dragOverGroupId === props.group.id) && props.group.id ? 'bg-gray-300 text-gray-700' : '',
)}
>
<a role='button'>{props.group.name}</a>
</summary>
<ul>
{props.group.documents.map((d, index) => (
<DocumentLineItem key={d.id} document={d} index={index} groupId={props.group.id} />
// <li className='p-0 m-0' key={d.id}>
// {!d.areas.length
// ?
// <div
// onClick={() => 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
// ? <input
// type="text"
// name="documentName"
// id="documentName"
// autoFocus
// className="h-8 w-[calc(100%-18px)] text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 inline-block 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
// ? 'bg-gray-900 text-white'
// : 'text-gray-300 hover:bg-gray-700 hover:text-white',
// 'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 '
// )}
// >
// {d.name}
// </a>
// }
// </div>
// : <details>
// <summary
// onClick={() => 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
// ? <input
// type="text"
// name="documentName"
// id="documentName"
// autoFocus
// className="h-8 w-[calc(100%-18px)] text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 inline-block 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
// ? 'bg-gray-900 text-white'
// : 'text-gray-300 hover:bg-gray-700 hover:text-white',
// 'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 '
// )}
// >
// {d.name}
// </a>
// }
// </summary>
// <ul>
// {d.areas.map((a, index) => (
// <li key={a.id}>
// {selectedAreaId === a.id && isEditAreaNameInputShowing
// ? <input
// type="text"
// name="areaName"
// id="areaName"
// 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"
// placeholder={a.name || `Area ${index}`}
// onBlur={onAreaInputBlur}
// onKeyDown={(event) => {
// onEnterHandler(event,
// () => onConfirmAreaNameChangeHandler({ areaId: a.id, areaName: event.currentTarget.value }))
// }}
// ref={editAreaNameTextInput}
// />
// : <div
// draggable
// onDragOver={() => onAreaDragOver(a.id)}
// onDragStart={() => onAreaDragStart(a.id)}
// onDragEnd={() => onAreaDropEnd(a.id)}
// className={classNames('flex justify-between items-center cursor-pointer',
// selectedAreaId === a.id ? 'bg-indigo-500 text-gray-200' : 'text-gray-300 hover:bg-gray-700 hover:text-white',
// dragOverAreaId === a.id ? 'bg-gray-300 text-gray-700' : '',
// selectedAreaId === a.id && dragOverAreaId === a.id ? 'bg-indigo-300' : '',
// )}>
// <a
// role='button'
// onClick={() => onAreaClick(a.id)}
// onDoubleClick={() => onAreaDoubleClick(a.id)}
// className={classNames('group w-full 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',
// )}>
// {a.name || `Area ${a.order}`}
// </a>
// <ArrowPathIcon
// className='w-6 h-5 mr-2 text-white hover:bg-white hover:text-gray-700 rounded-full p-0.5'
// aria-hidden="true"
// onClick={() => console.log('refresh')}
// />
// <XMarkIcon
// className='w-6 h-5 mr-2 text-white hover:bg-red-400 hover:text-gray-100 rounded-full p-0.5'
// onClick={() => handleAreaDeleteButtonClick(a.id)} />
// </div>
// }
// </li>
// ))}
// </ul>
// </details>
// }
// </li>
))}
// )}>
// {selectedDocumentId === d.id && isEditDocumentNameInputShowing
// ? <input
// type="text"
// name="documentName"
// id="documentName"
// autoFocus
// className="h-8 w-[calc(100%-18px)] text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 inline-block 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
// ? 'bg-gray-900 text-white'
// : 'text-gray-300 hover:bg-gray-700 hover:text-white',
// 'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 '
// )}
// >
// {d.name}
// </a>
// }
// </summary>
// <ul>
// {d.areas.map((a, index) => (
// <li key={a.id}>
// {selectedAreaId === a.id && isEditAreaNameInputShowing
// ? <input
// type="text"
// name="areaName"
// id="areaName"
// 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"
// placeholder={a.name || `Area ${index}`}
// onBlur={onAreaInputBlur}
// onKeyDown={(event) => {
// onEnterHandler(event,
// () => onConfirmAreaNameChangeHandler({ areaId: a.id, areaName: event.currentTarget.value }))
// }}
// ref={editAreaNameTextInput}
// />
// : <div
// draggable
// onDragOver={() => onAreaDragOver(a.id)}
// onDragStart={() => onAreaDragStart(a.id)}
// onDragEnd={() => onAreaDropEnd(a.id)}
// className={classNames('flex justify-between items-center cursor-pointer',
// selectedAreaId === a.id ? 'bg-indigo-500 text-gray-200' : 'text-gray-300 hover:bg-gray-700 hover:text-white',
// dragOverAreaId === a.id ? 'bg-gray-300 text-gray-700' : '',
// selectedAreaId === a.id && dragOverAreaId === a.id ? 'bg-indigo-300' : '',
// )}>
// <a
// role='button'
// onClick={() => onAreaClick(a.id)}
// onDoubleClick={() => onAreaDoubleClick(a.id)}
// className={classNames('group w-full 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',
// )}>
// {a.name || `Area ${a.order}`}
// </a>
// <ArrowPathIcon
// className='w-6 h-5 mr-2 text-white hover:bg-white hover:text-gray-700 rounded-full p-0.5'
// aria-hidden="true"
// onClick={() => console.log('refresh')}
// />
// <XMarkIcon
// className='w-6 h-5 mr-2 text-white hover:bg-red-400 hover:text-gray-100 rounded-full p-0.5'
// onClick={() => handleAreaDeleteButtonClick(a.id)} />
// </div>
// }
// </li>
// ))}
// </ul>
// </details>
// }
// </li>
))}
{renderAddNewDocument(props.group.id)}
</ul>
</details>
{renderAddNewDocument(props.group.id)}
</ul>
</details>
)
}

View File

@ -26,6 +26,9 @@ const makeDefaultProject = (): ProjectContextType => ({
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()),
getGroupById: (groupId) => undefined,
requestSelectProjectByName: (projectName) => Promise.resolve(false),
})
export default makeDefaultProject

View File

@ -10,6 +10,9 @@ import {
RequestUpdateDocument,
RequestChangeAreaOrder,
RequestDeleteAreaById,
RequestChangeGroupOrder,
GetProjectByName,
RequestChangeSessionProjectByName,
} from '../../wailsjs/wailsjs/go/ipc/Channel'
import { ipc } from '../../wailsjs/wailsjs/go/models'
import { AddAreaProps, AreaProps, ProjectContextType, ProjectProps, UpdateDocumentRequest, UserProps } from './types'
@ -21,6 +24,8 @@ export function useProject() {
return useContext(ProjectContext)
}
let attempts = 0
type Props = { children: ReactNode, projectProps: ProjectProps }
export function ProjectProvider({ children, projectProps }: Props) {
const [documents, setDocuments] = useState<ipc.Document[]>(projectProps.documents)
@ -110,6 +115,7 @@ export function ProjectProvider({ children, projectProps }: Props) {
const updateSession = async () => {
GetCurrentSession().then(response => {
if (response) setCurrentSession(response)
console.log(response)
Promise.resolve(response)
})
}
@ -143,10 +149,34 @@ export function ProjectProvider({ children, projectProps }: Props) {
return response
}
const getGroupById = (groupId: string): ipc.Group | undefined => (
groups.find(g => g.id === groupId)
)
const requestChangeGroupOrder = async (groupId: string, newOrder: number) => {
const response = await RequestChangeGroupOrder(groupId, newOrder)
await updateDocuments()
return response
}
const requestSelectProjectByName = async (name: string) => {
const successfulResponse = await RequestChangeSessionProjectByName(name)
await updateSession()
return successfulResponse
}
useEffect(() => {
if (!documents.length && !groups.length) updateDocuments()
}, [documents.length, groups.length])
useEffect(() => {
if ((!currentSession?.user?.localId || !currentSession?.user?.id)) {
updateSession()
attempts++
}
}, [currentSession?.user?.localId, currentSession?.user?.id])
const value = {
id: '',
documents,
@ -172,6 +202,9 @@ export function ProjectProvider({ children, projectProps }: Props) {
requestChooseUserAvatar,
requestUpdateDocument,
requestChangeAreaOrder,
requestChangeGroupOrder,
getGroupById,
requestSelectProjectByName,
}
return <ProjectContext.Provider value={value}>

View File

@ -58,4 +58,7 @@ export type ProjectContextType = {
requestChooseUserAvatar: () => Promise<string>
requestUpdateDocument: (request: UpdateDocumentRequest) => Promise<ipc.Document>
requestChangeAreaOrder: (areaId: string, newOrder: number) => Promise<ipc.Document>
requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise<ipc.Group>
getGroupById: (groupId: string) => ipc.Group | undefined
requestSelectProjectByName: (projectName: string) => Promise<boolean>
} & ProjectProps

View File

@ -1,4 +1,5 @@
import { NextPage } from 'next'
import { useEffect, useState } from 'react'
import MainHead from '../components/head'
import MainProject from '../components/project/Main'
import User from '../components/settings/User'
@ -9,7 +10,6 @@ import { mainPages } from '../context/Navigation/types'
import { useProject } from '../context/Project/provider'
const Home: NextPage = () => {
const { currentSession } = useProject()
const { selectedMainPage } = useNavigation()

View File

@ -4,6 +4,8 @@ import {ipc} from '../models';
export function CreateNewProject(arg1:string):Promise<ipc.Session>;
export function GetAllLocalProjects():Promise<Array<ipc.Project>>;
export function GetAreaById(arg1:string):Promise<ipc.Area>;
export function GetCurrentSession():Promise<ipc.Session>;
@ -16,6 +18,8 @@ export function GetDocuments():Promise<ipc.GetDocumentsResponse>;
export function GetProcessedAreasByDocumentId(arg1:string):Promise<Array<ipc.ProcessedArea>>;
export function GetProjectByName(arg1:string):Promise<ipc.Project>;
export function GetSuppportedLanguages():Promise<Array<ipc.Language>>;
export function GetUserMarkdownByDocumentId(arg1:string):Promise<ipc.UserMarkdown>;
@ -30,6 +34,10 @@ export function RequestAddProcessedArea(arg1:ipc.ProcessedArea):Promise<ipc.Proc
export function RequestChangeAreaOrder(arg1:string,arg2:number):Promise<ipc.Document>;
export function RequestChangeGroupOrder(arg1:string,arg2:number):Promise<ipc.Group>;
export function RequestChangeSessionProjectByName(arg1:string):Promise<boolean>;
export function RequestChooseUserAvatar():Promise<string>;
export function RequestDeleteAreaById(arg1:string):Promise<boolean>;

View File

@ -6,6 +6,10 @@ export function CreateNewProject(arg1) {
return window['go']['ipc']['Channel']['CreateNewProject'](arg1);
}
export function GetAllLocalProjects() {
return window['go']['ipc']['Channel']['GetAllLocalProjects']();
}
export function GetAreaById(arg1) {
return window['go']['ipc']['Channel']['GetAreaById'](arg1);
}
@ -30,6 +34,10 @@ export function GetProcessedAreasByDocumentId(arg1) {
return window['go']['ipc']['Channel']['GetProcessedAreasByDocumentId'](arg1);
}
export function GetProjectByName(arg1) {
return window['go']['ipc']['Channel']['GetProjectByName'](arg1);
}
export function GetSuppportedLanguages() {
return window['go']['ipc']['Channel']['GetSuppportedLanguages']();
}
@ -58,6 +66,14 @@ export function RequestChangeAreaOrder(arg1, arg2) {
return window['go']['ipc']['Channel']['RequestChangeAreaOrder'](arg1, arg2);
}
export function RequestChangeGroupOrder(arg1, arg2) {
return window['go']['ipc']['Channel']['RequestChangeGroupOrder'](arg1, arg2);
}
export function RequestChangeSessionProjectByName(arg1) {
return window['go']['ipc']['Channel']['RequestChangeSessionProjectByName'](arg1);
}
export function RequestChooseUserAvatar() {
return window['go']['ipc']['Channel']['RequestChooseUserAvatar']();
}

View File

@ -107,6 +107,7 @@ export namespace ipc {
parentId: string;
projectId: string;
name: string;
order: number;
static createFrom(source: any = {}) {
return new Group(source);
@ -118,6 +119,7 @@ export namespace ipc {
this.parentId = source["parentId"];
this.projectId = source["projectId"];
this.name = source["name"];
this.order = source["order"];
}
}
export class GetDocumentsResponse {
@ -381,7 +383,7 @@ export namespace ipc {
export class ProjectSettings {
defaultProcessLanguage: Language;
defaultTranslateTargetLanguage: Language;
IsHosted: boolean;
isHosted: boolean;
static createFrom(source: any = {}) {
return new ProjectSettings(source);
@ -391,7 +393,7 @@ export namespace ipc {
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"];
this.isHosted = source["isHosted"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {

View File

@ -3,6 +3,7 @@ package ipc
import (
"sort"
app "textualize/core/App"
consts "textualize/core/Consts"
document "textualize/core/Document"
session "textualize/core/Session"
@ -67,10 +68,6 @@ func (c *Channel) GetDocuments() GetDocumentsResponse {
})
}
// sort.Slice(jsonAreas, func(i, j int) bool {
// return jsonAreas[i].Order < jsonAreas[j].Order
// })
sort.Slice(jsonAreas, func(i, j int) bool {
return jsonAreas[i].Order < jsonAreas[j].Order
})
@ -87,16 +84,24 @@ func (c *Channel) GetDocuments() GetDocumentsResponse {
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,
}
response.Groups = append(response.Groups, jsonGroup)
jsonGroups = append(jsonGroups, jsonGroup)
}
sort.Slice(jsonGroups, func(i, j int) bool {
return jsonGroups[i].Order < jsonGroups[j].Order
})
response.Groups = jsonGroups
return response
}
@ -176,24 +181,68 @@ func (c *Channel) GetUserMarkdownByDocumentId(documentId string) UserMarkdown {
}
func (c *Channel) RequestAddDocumentGroup(name string) Group {
groupCollection := document.GetGroupCollection()
newGroup := document.Group{
Id: uuid.NewString(),
Name: name,
ProjectId: session.GetInstance().Project.Id,
Order: len(groupCollection.Groups),
}
document.GetGroupCollection().AddDocumentGroup(newGroup)
groupCollection.AddDocumentGroup(newGroup)
response := Group{
Id: newGroup.Id,
Name: newGroup.Name,
ParentId: newGroup.ParentId,
ProjectId: newGroup.ProjectId,
Order: newGroup.Order,
}
return response
}
func (c *Channel) RequestChangeGroupOrder(groupId string, newOrder int) Group {
groupCollection := document.GetGroupCollection()
// var foundArea document.Area
// for _, a := range documentOfArea.Areas {
// if a.Id == areaId {
// foundArea = a
// break
// }
// }
// if foundArea.Id == "" {
// return Document{}
// }
// processedAreasCollection := document.GetProcessedAreaCollection()
// for index, a := range documentOfArea.Areas {
// if a.Id == areaId {
// documentOfArea.Areas[index].Order = newOrder
// processedAreasCollection.GetAreaById(a.Id).Order = newOrder
// } else if a.Order >= newOrder {
// documentOfArea.Areas[index].Order = a.Order + 1
// processedAreasCollection.GetAreaById(a.Id).Order = a.Order + 1
// }
// }
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))
}
func (c *Channel) GetAreaById(areaId string) Area {
foundDocument := document.GetDocumentCollection().GetDocumentByAreaId(areaId)
@ -243,7 +292,7 @@ func (c *Channel) RequestAddArea(documentId string, area Area) Area {
StartY: area.StartY,
EndY: area.EndY,
Order: order,
Language: app.Language(area.Language),
Language: consts.Language(area.Language),
}
foundDocument.AddArea(newArea)
@ -337,7 +386,7 @@ func (c *Channel) RequestUpdateDocument(updatedDocument Document) Document {
documentToUpdate.Path = updatedDocument.Path
}
if updatedDocument.DefaultLanguage.DisplayName != "" {
documentToUpdate.DefaultLanguage = app.Language(updatedDocument.DefaultLanguage)
documentToUpdate.DefaultLanguage = consts.Language(updatedDocument.DefaultLanguage)
}
return updatedDocument

View File

@ -20,6 +20,7 @@ type Group struct {
ParentId string `json:"parentId"`
ProjectId string `json:"projectId"`
Name string `json:"name"`
Order int `json:"order"`
}
type GroupCollection struct {
@ -110,7 +111,7 @@ type Project struct {
type ProjectSettings struct {
DefaultProcessLanguage Language `json:"defaultProcessLanguage"`
DefaultTranslateTargetLanguage Language `json:"defaultTranslateTargetLanguage"`
IsHosted bool `json:"IsHosted"`
IsHosted bool `json:"isHosted"`
}
type Session struct {

View File

@ -2,7 +2,9 @@ package ipc
import (
app "textualize/core/App"
consts "textualize/core/Consts"
session "textualize/core/Session"
storage "textualize/storage/Local"
"github.com/wailsapp/wails/v2/pkg/runtime"
@ -46,12 +48,24 @@ func (c *Channel) GetCurrentSession() Session {
func (c *Channel) CreateNewProject(name string) Session {
currentSession := session.GetInstance()
currentSession.Project = session.Project{
newProject := session.Project{
Id: uuid.NewString(),
OrganizationId: currentSession.Project.OrganizationId,
Name: name,
}
successfulProjectWrite := storage.WriteLocalProjectData(storage.LocalProject{
Id: uuid.NewString(),
OrganizationId: currentSession.Project.OrganizationId,
Name: name,
})
if !successfulProjectWrite {
return Session{}
}
currentSession.Project = newProject
return c.GetCurrentSession()
}
@ -62,24 +76,30 @@ func (c *Channel) GetCurrentUser() User {
func (c *Channel) RequestUpdateCurrentUser(updatedUserRequest User) User {
sessionInstance := session.GetInstance()
user := session.User(sessionInstance.User)
sessionUser := session.User(sessionInstance.User)
if user.LocalId == "" {
user.LocalId = uuid.NewString()
if sessionUser.LocalId == "" {
sessionUser.LocalId = uuid.NewString()
}
if updatedUserRequest.FirstName != "" {
user.FirstName = updatedUserRequest.FirstName
sessionUser.FirstName = updatedUserRequest.FirstName
}
if updatedUserRequest.LastName != "" {
user.LastName = updatedUserRequest.LastName
sessionUser.LastName = updatedUserRequest.LastName
}
if updatedUserRequest.Email != "" {
user.Email = updatedUserRequest.Email
sessionUser.Email = updatedUserRequest.Email
}
user.AvatarPath = updatedUserRequest.AvatarPath
sessionUser.AvatarPath = updatedUserRequest.AvatarPath
successfulUserWrite := storage.WriteLocalUserData(storage.LocalUser(sessionUser))
if !successfulUserWrite {
return User{}
}
sessionInstance.UpdateCurrentUser(sessionUser)
sessionInstance.UpdateCurrentUser(user)
return User(sessionInstance.User)
}
@ -102,8 +122,68 @@ func (c *Channel) RequestChooseUserAvatar() string {
}
}
func (c *Channel) GetAllLocalProjects() []Project {
readLocalProjects := storage.ReadAllLocalProjects()
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
}
func (c *Channel) GetProjectByName(projectName string) Project {
foundProject := storage.ReadLocalProjectByName(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,
},
}
}
func (c *Channel) RequestChangeSessionProjectByName(projectName string) bool {
foundProject := c.GetProjectByName(projectName)
if foundProject.Id == "" {
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,
},
}
return session.GetInstance().Project.Id == foundProject.Id
}
func (c *Channel) GetSuppportedLanguages() []Language {
supportedLanguages := app.GetSuppportedLanguages()
supportedLanguages := consts.GetSuppportedLanguages()
var response []Language

View File

@ -7,9 +7,8 @@ import (
"net/http"
"os"
ipc "textualize/Ipc"
app "textualize/core/App"
document "textualize/core/Document"
ipc "textualize/ipc"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/logger"
@ -50,8 +49,6 @@ var icon []byte
func main() {
// Create an instance of the app structure
app := app.GetInstance()
document.InitizeModule()
ipcChannel := ipc.GetInstance()
// Create application with options

150
storage/Local/Interface.go Normal file
View File

@ -0,0 +1,150 @@
package storage
import (
"encoding/json"
"fmt"
"os"
"strings"
)
func GetLocalStoragePath() string {
homeDir, err := os.UserHomeDir()
applicationName := "/Textualize"
if err != nil {
return ""
}
return homeDir + applicationName
}
func createLocalStorageDirIfNeeded() bool {
localStoragePath := GetLocalStoragePath()
if localStoragePath == "" {
return false
}
_, directoryError := os.Stat(GetLocalStoragePath())
directoryDoesNotExist := os.IsNotExist(directoryError)
if !directoryDoesNotExist {
return true
}
errorCreatingDir := os.Mkdir(localStoragePath, os.ModePerm)
return errorCreatingDir != nil
}
func createLocalStorageSubDirIfNeeded(relativeSubdirectoryPath string) bool {
localStoragePath := GetLocalStoragePath()
if localStoragePath == "" {
return false
}
fullLocalStoragePath := GetLocalStoragePath() + relativeSubdirectoryPath
_, directoryError := os.Stat(fullLocalStoragePath)
directoryDoesNotExist := os.IsNotExist(directoryError)
if !directoryDoesNotExist {
return true
}
errorCreatingDir := os.MkdirAll(fullLocalStoragePath, os.ModePerm)
return errorCreatingDir != nil
}
func WriteLocalUserData(user LocalUser) bool {
file, _ := json.MarshalIndent(user, "", " ")
path := GetLocalStoragePath()
if path == "" {
return false
}
isLocalStorageDirectoryCreated := createLocalStorageDirIfNeeded()
if !isLocalStorageDirectoryCreated {
return false
}
err := os.WriteFile(GetLocalStoragePath()+"/User.json", file, 0644)
return err == nil
}
func ReadLocalUserData() LocalUser {
file, err := os.ReadFile(GetLocalStoragePath() + "/User.json")
if err != nil {
return LocalUser{}
}
response := LocalUser{}
errorUnmarshaling := json.Unmarshal([]byte(file), &response)
if errorUnmarshaling != nil {
return LocalUser{}
}
return response
}
func ReadLocalProjectByName(name string) LocalProject {
file, err := os.ReadFile(GetLocalStoragePath() + "/projects/" + name + ".json")
if err != nil {
return LocalProject{}
}
response := LocalProject{}
errorUnmarshaling := json.Unmarshal([]byte(file), &response)
if errorUnmarshaling != nil {
return LocalProject{}
}
return response
}
func WriteLocalProjectData(project LocalProject) bool {
file, _ := json.MarshalIndent(project, "", " ")
path := GetLocalStoragePath()
if path == "" {
return false
}
subdirectory := "/projects/"
isLocalStorageDirectoryCreated := createLocalStorageSubDirIfNeeded(subdirectory)
if !isLocalStorageDirectoryCreated {
return false
}
err := os.WriteFile(GetLocalStoragePath()+subdirectory+project.Name+".json", file, 0644)
return err == nil
}
func ReadAllLocalProjects() []LocalProject {
localProjects := make([]LocalProject, 0)
subdirectory := "/projects/"
isLocalStorageDirectoryCreated := createLocalStorageSubDirIfNeeded(subdirectory)
if !isLocalStorageDirectoryCreated {
return localProjects
}
localProjectFileEntries, readDirError := os.ReadDir(GetLocalStoragePath() + subdirectory)
if readDirError != nil {
fmt.Println(localProjectFileEntries)
return localProjects
}
localProjectNames := make([]string, 0)
for _, fileName := range localProjectFileEntries {
localProjectNames = append(localProjectNames, strings.ReplaceAll(fileName.Name(), ".json", ""))
}
for _, projectName := range localProjectNames {
localProjects = append(localProjects, ReadLocalProjectByName(projectName))
}
return localProjects
}

View File

@ -0,0 +1,30 @@
package storage
type LocalProject struct {
Id string `json:"id"`
OrganizationId string `json:"organizationId"`
Name string `json:"name"`
Settings LocalProjectSettings `json:"settings"`
}
type LocalProjectSettings struct {
DefaultProcessLanguage Language `json:"defaultProcessLanguage"`
DefaultTranslateTargetLanguage Language `json:"defaultTranslateTargetLanguage"`
IsHosted bool `json:"isHosted"`
}
type Language struct {
DisplayName string `json:"displayName"`
ProcessCode string `json:"processCode"`
TranslateCode string `json:"translateCode"`
}
type LocalUser 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"`
}