diff --git a/build/bin/textualize.app/Contents/MacOS/Textualize b/build/bin/textualize.app/Contents/MacOS/Textualize deleted file mode 100755 index fda2dde..0000000 Binary files a/build/bin/textualize.app/Contents/MacOS/Textualize and /dev/null differ diff --git a/core/Session/Organization.go b/core/Session/Organization.go index b906155..dc0191f 100644 --- a/core/Session/Organization.go +++ b/core/Session/Organization.go @@ -1,4 +1,8 @@ package session type Organization struct { + Id string + Name string + LogoPath string + Users []User } diff --git a/core/Session/Session.go b/core/Session/Session.go index f504029..e4f952a 100644 --- a/core/Session/Session.go +++ b/core/Session/Session.go @@ -1,21 +1,23 @@ package session -type session struct { +type Session struct { Project Project Organization Organization User User } -var sessionInstance *session +var sessionInstance *Session -func GetInstance() *session { +func GetInstance() *Session { if sessionInstance == nil { - sessionInstance = &session{} + sessionInstance = &Session{} } return sessionInstance } -func InitializeModule(newSession session) *session { - sessionInstance = &newSession +func InitializeModule(newSession Session) *Session { + if sessionInstance == nil { + sessionInstance = &newSession + } return sessionInstance } diff --git a/core/Session/User.go b/core/Session/User.go index c5410e4..524ab89 100644 --- a/core/Session/User.go +++ b/core/Session/User.go @@ -1,8 +1,9 @@ package session type User struct { - Id string - FirstName string - LastName string - Avatar string + Id string + FirstName string + LastName string + AvatarPath string + AuthToken string } diff --git a/frontend/components/project/Main.tsx b/frontend/components/project/Main.tsx new file mode 100644 index 0000000..84d71f3 --- /dev/null +++ b/frontend/components/project/Main.tsx @@ -0,0 +1,153 @@ +'use client' + +import { Popover, Transition } from '@headlessui/react' +import { ChevronDownIcon, FolderArrowDownIcon, FolderOpenIcon, FolderPlusIcon } from '@heroicons/react/20/solid' +import { Fragment, useState } from 'react' +import { useProject } from '../../context/Project/provider' +import NewProjectModal from './NewProjectModal' + + +const MainProject = () => { + const [isNewProjectModalOpen, setIsNewProjectModalOpen] = useState(false) + const [canPopoverBeOpen, setCanPopoverBeOpen] = useState(true) + const { createNewProject } = useProject() + + const buttonOptions = [ + { + name: 'New', + description: 'Create a new Project', + icon: , + onClick: () => { + setIsNewProjectModalOpen(true) + setCanPopoverBeOpen(false) + }, + }, + { + name: 'Open', + description: 'Open a local Project', + icon: , + onClick: () => { + setCanPopoverBeOpen(false) + }, + }, + { + name: 'Connect', + description: 'Connected to a hosted Project', + icon: , + onClick: () => { + setCanPopoverBeOpen(false) + }, + }, + ] + + const onCreateNewProjectHandler = (projectName: string) => { + setIsNewProjectModalOpen(false) + setCanPopoverBeOpen(true) + createNewProject(projectName) + } + + return
+ + {isNewProjectModalOpen ? : ''} + +
+
+ +
+ T +

extualize

+
+

+ Digitize, Translate, and Manage +
+ Your Physical Documents +

+

+ Incididunt sint fugiat pariatur cupidatat consectetur sit cillum anim id veniam aliqua proident excepteur + commodo do ea. +

+
+ + {({ open }) => <> + + Select Project + + + {canPopoverBeOpen ? ( + + + + ) : + } + + + } + + + Learn More + +
+
+
+
+} + +export default MainProject diff --git a/frontend/components/project/NewProjectModal.tsx b/frontend/components/project/NewProjectModal.tsx new file mode 100644 index 0000000..a072084 --- /dev/null +++ b/frontend/components/project/NewProjectModal.tsx @@ -0,0 +1,48 @@ +import { useRef } from 'react' + +type Props = { + onCreateNewProjectHandler: (projectName: string) => void +} + +const NewProjectModal = (props: Props) => { + const projectNameRef = useRef(null) + + return ( +
+
+

Name Your New Project

+
+

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

+
+
+
+ + +
+ +
+
+
+ ) +} + +export default NewProjectModal diff --git a/frontend/components/workspace/Sidebar.tsx b/frontend/components/workspace/Sidebar.tsx index a921f90..e3a40c1 100644 --- a/frontend/components/workspace/Sidebar.tsx +++ b/frontend/components/workspace/Sidebar.tsx @@ -74,6 +74,7 @@ function Sidebar() { setSelectedAreaId, selectedDocumentId, setSelectedDocumentId, + currentSession, } = useProject() const navigation = getNavigationProps(documents, groups) @@ -388,11 +389,8 @@ function Sidebar() {
- Textualize + Textualize +

{currentSession.project.name}

{renderNavigationItems()} diff --git a/frontend/components/workspace/TextEditor.tsx b/frontend/components/workspace/TextEditor.tsx index 4df3c24..270daff 100644 --- a/frontend/components/workspace/TextEditor.tsx +++ b/frontend/components/workspace/TextEditor.tsx @@ -17,9 +17,9 @@ const TextEditor = () => { const [isPreviewOpen, setIsPreviewOpen] = useState(false) const [modifiedEditorValue, setModifiedEditorValue] = useState('') - const handleEditorDidMount: DiffOnMount = async (editor, _) => { + const handleEditorDidMount: DiffOnMount = async (editor, monaco) => { const currentDocumentId = selectedDocumentId - + editorInteractions = createDiffEditorInteractions(editor) const modifiedEditor = editor.getModifiedEditor() const originalEditor = editor.getOriginalEditor() @@ -84,6 +84,10 @@ const TextEditor = () => { language='markdown' height={`${editorHeight}px`} onMount={handleEditorDidMount} + options={{ + renderMarginRevertIcon: true, + enableSplitViewResizing: false, + }} /> {isPreviewOpen ? : ''} diff --git a/frontend/components/workspace/TextPreview.tsx b/frontend/components/workspace/TextPreview.tsx index 05e09dd..5e444d3 100644 --- a/frontend/components/workspace/TextPreview.tsx +++ b/frontend/components/workspace/TextPreview.tsx @@ -4,7 +4,7 @@ type Props = { markdown: string, height: number } const TextPreview = (props: Props) => (
({ requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise.resolve(new ipc.UserMarkdown()), getUserMarkdownByDocumentId: (documentId) => Promise.resolve(new ipc.UserMarkdown), setSelectedAreaId: (id) => {}, - setSelectedDocumentId: (id) => {} + setSelectedDocumentId: (id) => {}, + currentSession: new ipc.Session(), + createNewProject: (name: string) => Promise.resolve(new ipc.Session()), }) export default makeDefaultProject diff --git a/frontend/context/Project/provider.tsx b/frontend/context/Project/provider.tsx index ec86223..0b11928 100644 --- a/frontend/context/Project/provider.tsx +++ b/frontend/context/Project/provider.tsx @@ -1,7 +1,7 @@ 'use client' import { createContext, ReactNode, useContext, useEffect, useState } from 'react' -import { GetDocuments, GetProcessedAreasByDocumentId, GetUserMarkdownByDocumentId, RequestAddArea, RequestAddDocument, RequestAddDocumentGroup, RequestAddProcessedArea, RequestUpdateArea, RequestUpdateDocumentUserMarkdown } from '../../wailsjs/wailsjs/go/ipc/Channel' +import { CreateNewProject, GetCurrentSession, GetDocuments, GetProcessedAreasByDocumentId, GetUserMarkdownByDocumentId, RequestAddArea, RequestAddDocument, RequestAddDocumentGroup, RequestAddProcessedArea, RequestUpdateArea, RequestUpdateDocumentUserMarkdown } from '../../wailsjs/wailsjs/go/ipc/Channel' import { ipc } from '../../wailsjs/wailsjs/go/models' import { AddAreaProps, AreaProps, ProjectContextType, ProjectProps } from './types' import makeDefaultProject from './makeDefaultProject' @@ -18,6 +18,7 @@ export function ProjectProvider({ children, projectProps }: Props) { const [ groups, setGroups ] = useState(projectProps.groups) const [ selectedAreaId, setSelectedAreaId ] = useState('') const [selectedDocumentId, setSelectedDocumentId] = useState('') + const [currentSession, setCurrentSession] = useState(new ipc.Session()) const updateDocuments = async () => { GetDocuments().then(response => { @@ -92,6 +93,19 @@ export function ProjectProvider({ children, projectProps }: Props) { const requestAddProcessedArea = async (processedArea: ipc.ProcessedArea) => await RequestAddProcessedArea(processedArea) + const updateSession = async () => { + GetCurrentSession().then(response => { + if (response) setCurrentSession(response) + Promise.resolve(response) + }) + } + + const createNewProject = async (name: string) => { + const sessionResponse = await CreateNewProject(name) + await updateSession() + return sessionResponse + } + useEffect(() => { if (!documents.length && !groups.length) updateDocuments() }, [documents.length, groups.length]) @@ -114,6 +128,8 @@ export function ProjectProvider({ children, projectProps }: Props) { requestAddProcessedArea, requestUpdateDocumentUserMarkdown, getUserMarkdownByDocumentId, + currentSession, + createNewProject, } return diff --git a/frontend/context/Project/types.ts b/frontend/context/Project/types.ts index 9a1cd8f..73c564f 100644 --- a/frontend/context/Project/types.ts +++ b/frontend/context/Project/types.ts @@ -27,8 +27,10 @@ export type ProjectContextType = { requestAddDocumentGroup: (groupName: string) => Promise requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise getUserMarkdownByDocumentId: (documentId: string) => Promise - selectedAreaId: string, - setSelectedAreaId: (id: string) => void, + selectedAreaId: string + setSelectedAreaId: (id: string) => void selectedDocumentId: string setSelectedDocumentId: (id: string) => void + currentSession: ipc.Session + createNewProject: (name: string) => Promise } & ProjectProps \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b5e40db..70e106c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,7 +12,6 @@ "@heroicons/react": "^2.0.13", "@monaco-editor/react": "^4.4.6", "@tailwindcss/forms": "^0.5.3", - "monaco-editor": "^0.34.1", "next": "^13.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -3589,7 +3588,8 @@ "node_modules/monaco-editor": { "version": "0.34.1", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", - "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==", + "peer": true }, "node_modules/mri": { "version": "1.2.0", @@ -7576,7 +7576,8 @@ "monaco-editor": { "version": "0.34.1", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", - "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==", + "peer": true }, "mri": { "version": "1.2.0", diff --git a/frontend/package.json b/frontend/package.json index 887eeef..622df84 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,7 +17,6 @@ "@heroicons/react": "^2.0.13", "@monaco-editor/react": "^4.4.6", "@tailwindcss/forms": "^0.5.3", - "monaco-editor": "^0.34.1", "next": "^13.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index ff03f5b..4ec9d15 100755 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -963e9c454713999e77f8d595ec4cc5ea \ No newline at end of file +db04e8c6f18963bc94c89200059f2f38 \ No newline at end of file diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index 4446638..e158015 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -1,22 +1,24 @@ import { NextPage } from 'next' -import { useState } from 'react' import MainHead from '../components/head' +import MainProject from '../components/project/Main' import MainWorkspace from '../components/workspace/Main' import Navigation from '../components/workspace/Navigation' - -enum workspaces { - PROCESSOR = 'PROCESSOR', - TEXTEDITOR = 'TEXTEDITOR' -} +import { useProject } from '../context/Project/provider' const Home: NextPage = () => { - const [ selectedWorkSpace, setSelectedWorkSpace ] = useState(workspaces.PROCESSOR) + + const { currentSession } = useProject() return ( <> - - + {!currentSession?.project?.id + ? + : <> + + + + } ) } diff --git a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts index 99cb5ee..1271a0f 100755 --- a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts +++ b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts @@ -2,6 +2,10 @@ // This file is automatically generated. DO NOT EDIT import {ipc} from '../models'; +export function CreateNewProject(arg1:string):Promise; + +export function GetCurrentSession():Promise; + export function GetDocumentById(arg1:string):Promise; export function GetDocuments():Promise; diff --git a/frontend/wailsjs/wailsjs/go/ipc/Channel.js b/frontend/wailsjs/wailsjs/go/ipc/Channel.js index d422828..533a3a6 100755 --- a/frontend/wailsjs/wailsjs/go/ipc/Channel.js +++ b/frontend/wailsjs/wailsjs/go/ipc/Channel.js @@ -2,6 +2,14 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +export function CreateNewProject(arg1) { + return window['go']['ipc']['Channel']['CreateNewProject'](arg1); +} + +export function GetCurrentSession() { + return window['go']['ipc']['Channel']['GetCurrentSession'](); +} + export function GetDocumentById(arg1) { return window['go']['ipc']['Channel']['GetDocumentById'](arg1); } diff --git a/frontend/wailsjs/wailsjs/go/models.ts b/frontend/wailsjs/wailsjs/go/models.ts index c877042..81a9394 100755 --- a/frontend/wailsjs/wailsjs/go/models.ts +++ b/frontend/wailsjs/wailsjs/go/models.ts @@ -29,7 +29,6 @@ export namespace ipc { path: string; projectId: string; areas: Area[]; - modifiedMarkdown: string; static createFrom(source: any = {}) { return new Document(source); @@ -43,7 +42,6 @@ export namespace ipc { this.path = source["path"]; this.projectId = source["projectId"]; this.areas = this.convertValues(source["areas"], Area); - this.modifiedMarkdown = source["modifiedMarkdown"]; } convertValues(a: any, classs: any, asMap: boolean = false): any { @@ -115,6 +113,62 @@ export namespace ipc { } } + export class User { + id: string; + firstName: string; + lastName: string; + avatarPath: string; + authToken: string; + + static createFrom(source: any = {}) { + return new User(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.id = source["id"]; + this.firstName = source["firstName"]; + this.lastName = source["lastName"]; + this.avatarPath = source["avatarPath"]; + this.authToken = source["authToken"]; + } + } + export class Organization { + id: string; + name: string; + logoPath: string; + users: User[]; + + static createFrom(source: any = {}) { + return new Organization(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.id = source["id"]; + this.name = source["name"]; + this.logoPath = source["logoPath"]; + this.users = this.convertValues(source["users"], User); + } + + 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 ProcessedBoundingBox { x0: number; y0: number; @@ -277,6 +331,55 @@ export namespace ipc { + export class Project { + id: string; + name: string; + + static createFrom(source: any = {}) { + return new Project(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.id = source["id"]; + this.name = source["name"]; + } + } + export class Session { + project: Project; + organization: Organization; + user: User; + + static createFrom(source: any = {}) { + return new Session(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.project = this.convertValues(source["project"], Project); + this.organization = this.convertValues(source["organization"], Organization); + this.user = this.convertValues(source["user"], User); + } + + 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 UserMarkdown { id: string; documentId: string; diff --git a/ipc/Documents.go b/ipc/Documents.go index 748f8c2..cf14b96 100644 --- a/ipc/Documents.go +++ b/ipc/Documents.go @@ -3,6 +3,7 @@ package ipc import ( app "textualize/core/App" document "textualize/core/Document" + session "textualize/core/Session" "github.com/google/uuid" "github.com/wailsapp/wails/v2/pkg/runtime" @@ -18,14 +19,7 @@ func (c *Channel) GetDocumentById(id string) Document { var jsonAreas []Area for _, a := range foundDocument.Areas { - jsonAreas = append(jsonAreas, Area{ - Id: a.Id, - Name: a.Name, - StartX: a.StartX, - StartY: a.StartY, - EndX: a.EndX, - EndY: a.EndY, - }) + jsonAreas = append(jsonAreas, Area(a)) } response := Document{ Id: foundDocument.Id, @@ -50,14 +44,7 @@ func (c *Channel) GetDocuments() GetDocumentsResponse { for _, d := range documents { jsonAreas := make([]Area, 0) for _, a := range d.Areas { - jsonAreas = append(jsonAreas, Area{ - Id: a.Id, - Name: a.Name, - StartX: a.StartX, - StartY: a.StartY, - EndX: a.EndX, - EndY: a.EndY, - }) + jsonAreas = append(jsonAreas, Area(a)) } jsonDocument := Document{ @@ -105,7 +92,7 @@ func (c *Channel) RequestAddDocument(groupId string, documentName string) Docume Name: documentName, Path: filePath, GroupId: groupId, - ProjectId: "something else", // TODO: Change me + ProjectId: session.GetInstance().Project.Id, } document.GetDocumentCollection().AddDocument(newDocument) @@ -163,7 +150,7 @@ func (c *Channel) RequestAddDocumentGroup(name string) Group { newGroup := document.Group{ Id: uuid.NewString(), Name: name, - ProjectId: "something else", // TODO: change me + ProjectId: session.GetInstance().Project.Id, } document.GetGroupCollection().AddDocumentGroup(newGroup) diff --git a/ipc/JsonEntities.go b/ipc/JsonEntities.go index f39d34b..5b7327d 100644 --- a/ipc/JsonEntities.go +++ b/ipc/JsonEntities.go @@ -1,13 +1,12 @@ package ipc type Document struct { - Id string `json:"id"` - GroupId string `json:"groupId"` - Name string `json:"name"` - Path string `json:"path"` - ProjectId string `json:"projectId"` - Areas []Area `json:"areas"` - ModifiedMarkdown string `json:"modifiedMarkdown"` + Id string `json:"id"` + GroupId string `json:"groupId"` + Name string `json:"name"` + Path string `json:"path"` + ProjectId string `json:"projectId"` + Areas []Area `json:"areas"` } type DocumentCollection struct { @@ -79,3 +78,29 @@ type UserMarkdown struct { type UserMarkdownCollection struct { Values []UserMarkdown `json:"values"` } + +type User struct { + Id string `json:"id"` + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + AvatarPath string `json:"avatarPath"` + AuthToken string `json:"authToken"` +} + +type Organization struct { + Id string `json:"id"` + Name string `json:"name"` + LogoPath string `json:"logoPath"` + Users []User `json:"users"` +} + +type Project struct { + Id string `json:"id"` + Name string `json:"name"` +} + +type Session struct { + Project Project `json:"project"` + Organization Organization `json:"organization"` + User User `json:"user"` +} diff --git a/ipc/Session.go b/ipc/Session.go new file mode 100644 index 0000000..5e0edea --- /dev/null +++ b/ipc/Session.go @@ -0,0 +1,38 @@ +package ipc + +import ( + session "textualize/core/Session" + + "github.com/google/uuid" +) + +func (c *Channel) GetCurrentSession() Session { + currentSession := session.GetInstance() + + var sessionUsers []User + for _, u := range currentSession.Organization.Users { + sessionUsers = append(sessionUsers, User(u)) + } + + return Session{ + Project: Project(currentSession.Project), + User: User(currentSession.User), + Organization: Organization{ + Id: currentSession.Organization.Id, + Name: currentSession.Project.Name, + LogoPath: currentSession.Organization.LogoPath, + Users: sessionUsers, + }, + } +} + +func (c *Channel) CreateNewProject(name string) Session { + currentSession := session.GetInstance() + + currentSession.Project = session.Project{ + Id: uuid.NewString(), + Name: name, + } + + return c.GetCurrentSession() +}