diff --git a/build/bin/textualize.app/Contents/MacOS/Textualize b/build/bin/textualize.app/Contents/MacOS/Textualize new file mode 100755 index 0000000..c155b39 Binary files /dev/null and b/build/bin/textualize.app/Contents/MacOS/Textualize differ diff --git a/core/App/app.go b/core/App/app.go new file mode 100644 index 0000000..93f3d12 --- /dev/null +++ b/core/App/app.go @@ -0,0 +1,23 @@ +package app + +import ( + "context" +) + +type App struct { + Context context.Context +} + +var instance *App + +func GetInstance() *App { + if instance == nil { + instance = &App{} + } + + return instance +} + +func (a *App) Startup(ctx context.Context) { + a.Context = ctx +} diff --git a/core/Document/Initialize.go b/core/Document/Initialize.go index a583382..f5dbc60 100644 --- a/core/Document/Initialize.go +++ b/core/Document/Initialize.go @@ -3,9 +3,6 @@ package document func InitizeModule() { GetDocumentCollection() GetDocumentGroupCollection() - - // TODO: remove when UI allows adding docs - createTestData() } func createTestData() { diff --git a/frontend/components/workspace/Navigation.tsx b/frontend/components/workspace/Navigation.tsx index 6b94479..853e2d3 100644 --- a/frontend/components/workspace/Navigation.tsx +++ b/frontend/components/workspace/Navigation.tsx @@ -1,11 +1,10 @@ 'use client' -import React, { Fragment, useEffect, useState } from 'react' -import { v4 as uuidv4 } from 'uuid' +import React, { Fragment, useRef, useState } from 'react' import { Menu, Transition } from '@headlessui/react' import { BellIcon } from '@heroicons/react/24/outline' -import { MagnifyingGlassIcon } from '@heroicons/react/20/solid' -import { GetDocuments } from '../../wailsjs/wailsjs/go/ipc/Channel' +import { MagnifyingGlassIcon, PlusIcon, XMarkIcon } from '@heroicons/react/20/solid' +import { GetDocuments, RequestAddDocument, RequestAddDocumentGroup } from '../../wailsjs/wailsjs/go/ipc/Channel' import { LogPrint } from '../../wailsjs/wailsjs/runtime/runtime' import { ipc } from '../../wailsjs/wailsjs/go/models' @@ -15,74 +14,71 @@ type NavigationItem = { children: { id: string, name: string }[] } -// const navigation: NavigationItem[] = [ -// { -// id: uuidv4(), -// name: 'Chapter One', -// children: [ -// { name: 'Overview', id: uuidv4() }, -// { name: 'Members', id: uuidv4() }, -// { name: 'Calendar', id: uuidv4() }, -// { name: 'Settings', id: uuidv4() }, -// ], -// }, -// ] - const userNavigation = [ { name: 'Your Profile' }, { name: 'Settings' }, { name: 'Sign out' }, ] -const getNavigationProps = ( - documents: ipc.Document[], - documentGroups: ipc.DocumentGroup[]): NavigationItem[] => { - const groupsWithDocuments = documentGroups.map(g => { - const childrenDocuments = documents - .filter(d => d.groupId === g.id) - .map(d => ({ id: d.id, name: d.name })) +const getNavigationProps = (documentsAndGroups: ipc.GetDocumentsResponse): NavigationItem[] => { + const { documents, documentGroups } = documentsAndGroups - return { - id: g.id, - name: g.name, - children: childrenDocuments - } - }) - - const documentsWithoutGroup = documents - .filter(d => !d.groupId) + const groupsWithDocuments = documentGroups.map(g => { + const childrenDocuments = documents + .filter(d => d.groupId === g.id) .map(d => ({ id: d.id, name: d.name })) - return [ - ...groupsWithDocuments, - { - id: 'Uncategorized', - name: 'Uncategorized', - children: documentsWithoutGroup - } - ] + return { + id: g.id, + name: g.name, + children: childrenDocuments + } + }) + + const documentsWithoutGroup = documents + .filter(d => !d.groupId || d.groupId === 'Uncategorized') + .map(d => ({ id: d.id, name: d.name })) + + return [ + ...groupsWithDocuments, + { + id: 'Uncategorized', + name: 'Uncategorized', + children: documentsWithoutGroup + } + ] } +const initDocumentsAndGroups = new ipc.GetDocumentsResponse({ documents: [], documentGroups: [] }) + function classNames(...classes: any[]) { return classes.filter(Boolean).join(' ') } -function WorkspaceNavigation () { +function WorkspaceNavigation() { const [selectedItemId, setSelectedItemId] = useState('') const [selectedGroupId, setSelectedGroupId] = useState('') - const [documents, setDocuments] = useState([] as ipc.Document[]) - const [documentGroups, setDocumentGroups] = useState([] as ipc.DocumentGroup[]) - const [navigation, setNavigation] = useState([] as NavigationItem[]) + const [isAddNewDocumentInputShowing, setIsAddNewDocumentInputShowing] = useState(false) + const [isAddNewGroupInputShowing, setIsAddNewGroupInputShowing] = useState(false) + const [documentsAndGroups, setDocumentsAndGroups] = useState(initDocumentsAndGroups) + const addDocumentTextInput = useRef(null) + const addGroupTextInput = useRef(null) - useEffect(() => { - setNavigation(getNavigationProps(documents, documentGroups)) - }, [documents, documentGroups]) - GetDocuments().then(response => { - LogPrint(JSON.stringify(response, null, 2)) - if (!documents.length) setDocuments(response.documents) - if (!documentGroups.length) setDocumentGroups(response.documentGroups) - }) + const navigation = getNavigationProps(documentsAndGroups) + + const updateDocuments = async () => { + GetDocuments().then(response => { + LogPrint(JSON.stringify(response, null, 2)) + setDocumentsAndGroups(response) + Promise.resolve(response) + }) + } + + if (!documentsAndGroups.documents.length + || !documentsAndGroups.documentGroups.length) { + updateDocuments() + } const getParentGroupIdFromItemId = (itemId: string) => { @@ -94,69 +90,180 @@ function WorkspaceNavigation () { return parentGroupId } + const onAddNewDocumentLineItemClickHandler = (groupId: string) => { + setSelectedGroupId(groupId) + setIsAddNewDocumentInputShowing(true) + setIsAddNewGroupInputShowing(false) + } + + const onAddNewGroupLineItemClickHandler = () => { + setIsAddNewGroupInputShowing(true) + setIsAddNewDocumentInputShowing(false) + } + const onItemClickHandler = (itemId: string) => { setSelectedItemId(itemId) setSelectedGroupId(getParentGroupIdFromItemId(itemId)) + setIsAddNewDocumentInputShowing(false) + setIsAddNewGroupInputShowing(false) + } + + const onCancelAddGroupClickHandler = () => { + setIsAddNewGroupInputShowing(false) + } + + const onCancelAddItemClickHandler = () => { + setIsAddNewDocumentInputShowing(false) + } + + const onConfirmAddDocumentClickHandler = async (groupId: string) => { + const documentName = addDocumentTextInput.current?.value + if (!documentName) return + + const response = await RequestAddDocument(groupId, documentName) + if (!response.id) return + + let newDocumentsAndGroups = new ipc.GetDocumentsResponse(documentsAndGroups) + newDocumentsAndGroups.documents.push(response) + setDocumentsAndGroups(newDocumentsAndGroups) + setSelectedItemId(response.id) + setSelectedGroupId(groupId) + setIsAddNewDocumentInputShowing(false) + } + + const onConfirmAddGroupClickHandler = async(e: React.MouseEvent) => { + const groupName = addGroupTextInput.current?.value + if (!groupName) return + + const response = await RequestAddDocumentGroup(groupName) + if (!response.id) return + + let newDocumentsAndGroups = new ipc.GetDocumentsResponse(documentsAndGroups) + newDocumentsAndGroups.documentGroups.push(response) + setDocumentsAndGroups(newDocumentsAndGroups) + setSelectedGroupId(response.id) + setIsAddNewGroupInputShowing(false) + } + + const renderAddGroupInput = () => { + return isAddNewGroupInputShowing + ?
+
+ +
+ + + +
+ : + + } + + const renderAddNewDocument = (groupId: string) => { + return isAddNewDocumentInputShowing && selectedGroupId === groupId + ?
+
+ +
+ + +
+ : onAddNewDocumentLineItemClickHandler(groupId)} + > + } const renderNavigationItems = () => (