feat: store user markdown

This commit is contained in:
Joshua Shoemaker 2023-02-10 22:58:00 -06:00
parent 47cda8d0c8
commit 0ca3f06c8b
15 changed files with 236 additions and 23 deletions

Binary file not shown.

View 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
}

View File

@ -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>
}

View File

@ -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"

View File

@ -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' />,

View File

@ -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) => {}
})

View File

@ -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}>

View File

@ -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

View File

@ -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: () => { },

View 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

View File

@ -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>;

View File

@ -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);
}

View File

@ -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"];
}
}
}

View File

@ -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(),

View File

@ -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"`
}