feat: store user markdown
This commit is contained in:
parent
47cda8d0c8
commit
0ca3f06c8b
BIN
build/bin/textualize.app/Contents/MacOS/Textualize
Executable file
BIN
build/bin/textualize.app/Contents/MacOS/Textualize
Executable file
Binary file not shown.
51
core/Document/UserMarkdown.go
Normal file
51
core/Document/UserMarkdown.go
Normal file
@ -0,0 +1,51 @@
|
||||
package document
|
||||
|
||||
type UserMarkdown struct {
|
||||
Id string
|
||||
DocumentId string
|
||||
Value string
|
||||
}
|
||||
|
||||
type UserMarkdownCollection struct {
|
||||
Values []UserMarkdown
|
||||
}
|
||||
|
||||
var userMarkdownCollection *UserMarkdownCollection
|
||||
|
||||
func GetUserMarkdownCollection() *UserMarkdownCollection {
|
||||
if userMarkdownCollection == nil {
|
||||
userMarkdownCollection = &UserMarkdownCollection{}
|
||||
}
|
||||
|
||||
return userMarkdownCollection
|
||||
}
|
||||
|
||||
func (collection *UserMarkdownCollection) GetUserMarkdownByDocumentId(documentId string) *UserMarkdown {
|
||||
var foundUserMarkdown *UserMarkdown
|
||||
|
||||
for index, m := range collection.Values {
|
||||
if m.DocumentId == documentId {
|
||||
foundUserMarkdown = &collection.Values[index]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return foundUserMarkdown
|
||||
}
|
||||
|
||||
func (collection *UserMarkdownCollection) AddUserMarkdown(userMarkdown UserMarkdown) UserMarkdown {
|
||||
collection.Values = append(collection.Values, userMarkdown)
|
||||
return userMarkdown
|
||||
}
|
||||
|
||||
func (collection *UserMarkdownCollection) UpdateUserMarkdown(userMarkdown UserMarkdown) UserMarkdown {
|
||||
currentUserMarkdown := collection.GetUserMarkdownByDocumentId(userMarkdown.DocumentId)
|
||||
|
||||
if currentUserMarkdown != nil {
|
||||
currentUserMarkdown.Value = userMarkdown.Value
|
||||
} else {
|
||||
collection.AddUserMarkdown(userMarkdown)
|
||||
}
|
||||
|
||||
return userMarkdown
|
||||
}
|
@ -5,24 +5,42 @@ import type { DiffOnMount } from '@monaco-editor/react/'
|
||||
import TextEditorButtons from './TextEditorButtons'
|
||||
import createDiffEditorInteractions from '../../useCases/createDiffEditorInteractions'
|
||||
import TextPreview from './TextPreview'
|
||||
import createDebounce from '../../utils/createDebounce'
|
||||
|
||||
let editorInteractions: ReturnType<typeof createDiffEditorInteractions>
|
||||
|
||||
const TextEditor = () => {
|
||||
const { selectedDocumentId, getProcessedAreasByDocumentId } = useProject()
|
||||
const { selectedDocumentId, getProcessedAreasByDocumentId, requestUpdateDocumentUserMarkdown, getUserMarkdownByDocumentId } = useProject()
|
||||
const [editorHeight, setEditorHeight] = useState(window.innerHeight - 200)
|
||||
const [editorValue, setEditorValue] = useState('')
|
||||
const [isEditorReady, setIsEditorReady] = useState(false)
|
||||
const [isPreviewOpen, setIsPreviewOpen] = useState(false)
|
||||
const [modifiedEditorValue, setIsModifiedEditorValue] = useState('')
|
||||
const [modifiedEditorValue, setModifiedEditorValue] = useState('')
|
||||
|
||||
const handleEditorDidMount: DiffOnMount = (editor, _) => {
|
||||
const handleEditorDidMount: DiffOnMount = async (editor, _) => {
|
||||
const currentDocumentId = selectedDocumentId
|
||||
|
||||
editorInteractions = createDiffEditorInteractions(editor)
|
||||
const modifiedEditor = editor.getModifiedEditor()
|
||||
setIsModifiedEditorValue(modifiedEditor.getValue())
|
||||
modifiedEditor.onDidChangeModelContent(() => {
|
||||
setIsModifiedEditorValue(modifiedEditor.getValue())
|
||||
})
|
||||
const originalEditor = editor.getOriginalEditor()
|
||||
|
||||
setModifiedEditorValue(originalEditor.getValue())
|
||||
|
||||
try {
|
||||
const initialStoredUserMarkdownResponse = await getUserMarkdownByDocumentId(selectedDocumentId)
|
||||
if (initialStoredUserMarkdownResponse.value) {
|
||||
setModifiedEditorValue(initialStoredUserMarkdownResponse.value)
|
||||
modifiedEditor.getModel()?.setValue(initialStoredUserMarkdownResponse.value)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
modifiedEditor.onDidChangeModelContent(createDebounce(async () => {
|
||||
const modifiedMarkdown = modifiedEditor.getValue()
|
||||
requestUpdateDocumentUserMarkdown(currentDocumentId, modifiedMarkdown)
|
||||
setModifiedEditorValue(modifiedMarkdown)
|
||||
}))
|
||||
|
||||
setIsEditorReady(true)
|
||||
}
|
||||
@ -62,13 +80,13 @@ const TextEditor = () => {
|
||||
}
|
||||
<DiffEditor
|
||||
original={editorValue}
|
||||
modified={editorValue}
|
||||
modified={modifiedEditorValue}
|
||||
language='markdown'
|
||||
height={`${editorHeight}px`}
|
||||
onMount={handleEditorDidMount}
|
||||
/>
|
||||
|
||||
{ isPreviewOpen ? <TextPreview markdown={modifiedEditorValue} height={editorHeight} /> : '' }
|
||||
{isPreviewOpen ? <TextPreview markdown={modifiedEditorValue} height={editorHeight} /> : ''}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,30 @@ const TextEditorButtons = (props: Props) => {
|
||||
<span className="sr-only">Header 4</span>
|
||||
H4
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.H5)}
|
||||
className={classNames(
|
||||
'text-xs 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 font-bold',
|
||||
)}>
|
||||
<span className="sr-only">Header 5</span>
|
||||
H5
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.H6)}
|
||||
className={classNames(
|
||||
'text-xs 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 font-bold',
|
||||
)}>
|
||||
<span className="sr-only">Header 6</span>
|
||||
H6
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
|
@ -8,12 +8,12 @@ const TextPreview = (props: Props) => (
|
||||
style={{ 'height': `${props.height}px` }}>
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
h1: ({ node, ...props }) => <h1 {...props} className='font-extrabold text-2xl' />,
|
||||
h1: ({ node, ...props }) => <h1 {...props} className='font-black text-2xl' />,
|
||||
h2: ({ node, ...props }) => <h2 {...props} className='font-extrabold text-xl' />,
|
||||
h3: ({ node, ...props }) => <h3 {...props} className='font-extrabold text-lg' />,
|
||||
h4: ({ node, ...props }) => <h4 {...props} className='font-extrabold text-base' />,
|
||||
h5: ({ node, ...props }) => <h5 {...props} className='font-bold text-base' />,
|
||||
h6: ({ node, ...props }) => <h6 {...props} className='font-semibold text-base' />,
|
||||
h3: ({ node, ...props }) => <h3 {...props} className='font-bold text-lg' />,
|
||||
h4: ({ node, ...props }) => <h4 {...props} className='font-semibold text-base' />,
|
||||
h5: ({ node, ...props }) => <h5 {...props} className='font-medium text-base' />,
|
||||
h6: ({ node, ...props }) => <h6 {...props} className='font-light text-base underline' />,
|
||||
ul: ({ node, ...props }) => <ul {...props} className='list-disc list-inside ml-2' />,
|
||||
ol: ({ node, ...props }) => <ol {...props} className='list-decimal list-inside ml-2' />,
|
||||
em: ({ node, ...props }) => <em {...props} className='italic font-light' />,
|
||||
|
@ -15,6 +15,8 @@ const makeDefaultProject = (): ProjectContextType => ({
|
||||
requestUpdateArea: (updatedArea) => Promise.resolve(new ipc.Area()),
|
||||
requestAddDocument: (groupId, documentName) => Promise.resolve(new ipc.Document()),
|
||||
requestAddDocumentGroup: (groupName: string) => Promise.resolve(new ipc.Group()),
|
||||
requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise.resolve(new ipc.UserMarkdown()),
|
||||
getUserMarkdownByDocumentId: (documentId) => Promise.resolve(new ipc.UserMarkdown),
|
||||
setSelectedAreaId: (id) => {},
|
||||
setSelectedDocumentId: (id) => {}
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, ReactNode, useContext, useEffect, useState } from 'react'
|
||||
import { GetDocuments, GetProcessedAreasByDocumentId, RequestAddArea, RequestAddDocument, RequestAddDocumentGroup, RequestAddProcessedArea, RequestUpdateArea } from '../../wailsjs/wailsjs/go/ipc/Channel'
|
||||
import { 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'
|
||||
@ -68,6 +68,28 @@ export function ProjectProvider({ children, projectProps }: Props) {
|
||||
return response
|
||||
}
|
||||
|
||||
const requestUpdateDocumentUserMarkdown = async (documentId: string, markdown: string) => {
|
||||
let response: ipc.UserMarkdown = new ipc.UserMarkdown()
|
||||
try {
|
||||
response = await RequestUpdateDocumentUserMarkdown(documentId, markdown)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
const getUserMarkdownByDocumentId = async (documentId: string): Promise<ipc.UserMarkdown> => {
|
||||
let response: ipc.UserMarkdown = new ipc.UserMarkdown({})
|
||||
console.log(documentId)
|
||||
try {
|
||||
response = await GetUserMarkdownByDocumentId(documentId)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
const requestAddProcessedArea = async (processedArea: ipc.ProcessedArea) => await RequestAddProcessedArea(processedArea)
|
||||
|
||||
useEffect(() => {
|
||||
@ -90,6 +112,8 @@ export function ProjectProvider({ children, projectProps }: Props) {
|
||||
setSelectedDocumentId,
|
||||
getProcessedAreasByDocumentId,
|
||||
requestAddProcessedArea,
|
||||
requestUpdateDocumentUserMarkdown,
|
||||
getUserMarkdownByDocumentId,
|
||||
}
|
||||
|
||||
return <ProjectContext.Provider value={value}>
|
||||
|
@ -25,6 +25,8 @@ export type ProjectContextType = {
|
||||
requestUpdateArea: (area: AreaProps) => Promise<ipc.Area>
|
||||
requestAddDocument: (groupId: string, documentName: string) => Promise<ipc.Document>
|
||||
requestAddDocumentGroup: (groupName: string) => Promise<ipc.Group>
|
||||
requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise<ipc.UserMarkdown>
|
||||
getUserMarkdownByDocumentId: (documentId: string) => Promise<ipc.UserMarkdown>
|
||||
selectedAreaId: string,
|
||||
setSelectedAreaId: (id: string) => void,
|
||||
selectedDocumentId: string
|
||||
|
@ -5,6 +5,8 @@ export enum MarkdownOperator {
|
||||
H2 = '## ',
|
||||
H3 = '### ',
|
||||
H4 = '#### ',
|
||||
H5 = '##### ',
|
||||
H6 = '###### ',
|
||||
ITALLICS = '_',
|
||||
BOLD = '**',
|
||||
BULLET = '* ',
|
||||
@ -18,7 +20,6 @@ const wrapperOperators = [
|
||||
|
||||
const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEditor) => {
|
||||
const modifiedEditor = editor.getModifiedEditor()
|
||||
const originalEditor = editor.getOriginalEditor()
|
||||
|
||||
return {
|
||||
undo: () => { },
|
||||
|
11
frontend/utils/createDebounce.ts
Normal file
11
frontend/utils/createDebounce.ts
Normal file
@ -0,0 +1,11 @@
|
||||
const createDebounce = (fn: Function, ms = 300) => {
|
||||
let timeoutId: ReturnType<typeof setTimeout>
|
||||
return function (this: any, ...args: any[]) {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => {
|
||||
return fn.apply(this, args)
|
||||
}, ms)
|
||||
}
|
||||
}
|
||||
|
||||
export default createDebounce
|
4
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
4
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
@ -8,6 +8,8 @@ export function GetDocuments():Promise<ipc.GetDocumentsResponse>;
|
||||
|
||||
export function GetProcessedAreasByDocumentId(arg1:string):Promise<Array<ipc.ProcessedArea>>;
|
||||
|
||||
export function GetUserMarkdownByDocumentId(arg1:string):Promise<ipc.UserMarkdown>;
|
||||
|
||||
export function RequestAddArea(arg1:string,arg2:ipc.Area):Promise<ipc.Area>;
|
||||
|
||||
export function RequestAddDocument(arg1:string,arg2:string):Promise<ipc.Document>;
|
||||
@ -17,3 +19,5 @@ export function RequestAddDocumentGroup(arg1:string):Promise<ipc.Group>;
|
||||
export function RequestAddProcessedArea(arg1:ipc.ProcessedArea):Promise<ipc.ProcessedArea>;
|
||||
|
||||
export function RequestUpdateArea(arg1:ipc.Area):Promise<ipc.Area>;
|
||||
|
||||
export function RequestUpdateDocumentUserMarkdown(arg1:string,arg2:string):Promise<ipc.UserMarkdown>;
|
||||
|
@ -14,6 +14,10 @@ export function GetProcessedAreasByDocumentId(arg1) {
|
||||
return window['go']['ipc']['Channel']['GetProcessedAreasByDocumentId'](arg1);
|
||||
}
|
||||
|
||||
export function GetUserMarkdownByDocumentId(arg1) {
|
||||
return window['go']['ipc']['Channel']['GetUserMarkdownByDocumentId'](arg1);
|
||||
}
|
||||
|
||||
export function RequestAddArea(arg1, arg2) {
|
||||
return window['go']['ipc']['Channel']['RequestAddArea'](arg1, arg2);
|
||||
}
|
||||
@ -33,3 +37,7 @@ export function RequestAddProcessedArea(arg1) {
|
||||
export function RequestUpdateArea(arg1) {
|
||||
return window['go']['ipc']['Channel']['RequestUpdateArea'](arg1);
|
||||
}
|
||||
|
||||
export function RequestUpdateDocumentUserMarkdown(arg1, arg2) {
|
||||
return window['go']['ipc']['Channel']['RequestUpdateDocumentUserMarkdown'](arg1, arg2);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ export namespace ipc {
|
||||
path: string;
|
||||
projectId: string;
|
||||
areas: Area[];
|
||||
modifiedMarkdown: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Document(source);
|
||||
@ -42,6 +43,7 @@ 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 {
|
||||
@ -274,6 +276,23 @@ export namespace ipc {
|
||||
|
||||
|
||||
|
||||
|
||||
export class UserMarkdown {
|
||||
id: string;
|
||||
documentId: string;
|
||||
value: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new UserMarkdown(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.id = source["id"];
|
||||
this.documentId = source["documentId"];
|
||||
this.value = source["value"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ func (c *Channel) RequestAddDocument(groupId string, documentName string) Docume
|
||||
Name: documentName,
|
||||
Path: filePath,
|
||||
GroupId: groupId,
|
||||
ProjectId: "something else",
|
||||
ProjectId: "something else", // TODO: Change me
|
||||
}
|
||||
|
||||
document.GetDocumentCollection().AddDocument(newDocument)
|
||||
@ -121,6 +121,44 @@ func (c *Channel) RequestAddDocument(groupId string, documentName string) Docume
|
||||
return documentResponse
|
||||
}
|
||||
|
||||
func (c *Channel) RequestUpdateDocumentUserMarkdown(documentId string, markdown string) UserMarkdown {
|
||||
markdownCollection := document.GetUserMarkdownCollection()
|
||||
markdownToUpdate := markdownCollection.GetUserMarkdownByDocumentId(documentId)
|
||||
|
||||
newMarkdown := document.UserMarkdown{
|
||||
DocumentId: documentId,
|
||||
Value: markdown,
|
||||
}
|
||||
if markdownToUpdate == nil {
|
||||
newMarkdown.Id = uuid.NewString()
|
||||
} else {
|
||||
newMarkdown.Id = markdownToUpdate.Id
|
||||
}
|
||||
|
||||
updatedMarkdown := markdownCollection.UpdateUserMarkdown(newMarkdown)
|
||||
return UserMarkdown{
|
||||
Id: updatedMarkdown.Id,
|
||||
DocumentId: updatedMarkdown.DocumentId,
|
||||
Value: updatedMarkdown.Value,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Channel) GetUserMarkdownByDocumentId(documentId string) UserMarkdown {
|
||||
foundUserMarkdown := document.GetUserMarkdownCollection().GetUserMarkdownByDocumentId((documentId))
|
||||
|
||||
response := UserMarkdown{}
|
||||
|
||||
if foundUserMarkdown != nil {
|
||||
response = UserMarkdown{
|
||||
Id: foundUserMarkdown.Id,
|
||||
DocumentId: foundUserMarkdown.DocumentId,
|
||||
Value: foundUserMarkdown.Value,
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
func (c *Channel) RequestAddDocumentGroup(name string) Group {
|
||||
newGroup := document.Group{
|
||||
Id: uuid.NewString(),
|
||||
|
@ -1,12 +1,13 @@
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type DocumentCollection struct {
|
||||
@ -68,3 +69,13 @@ type ProcessedArea struct {
|
||||
FullText string `json:"fullText"`
|
||||
Lines []ProcessedLine `json:"lines"`
|
||||
}
|
||||
|
||||
type UserMarkdown struct {
|
||||
Id string `json:"id"`
|
||||
DocumentId string `json:"documentId"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type UserMarkdownCollection struct {
|
||||
Values []UserMarkdown `json:"values"`
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user