Custom Tab: Task Manager

In this example, we make a simple task manager, and enable task to become meta-object, task drag & drop, etc.

Register Task Object, Meta-Object and Tab

First, we need to define the task object type and meta-object type. Of course tab type.

declare module 'rendevoz' {
    interface CustomTypes {
        Object: {
            specType: 'task'
            taskBeginDate: number
            taskFinishDate?: number
            taskProgress: number
            taskName: string
            taskTargetMetaId: number
        },
        MetaObject: 'task',
        Tab: {
            type: 'taskManager'
            // you can define custom instance, right now we just skip it
            instance: never
            objectId: never
        }
    }
}

Then, we register in plugin.

import { PluginApi } from 'rendevoz'
import { t } from 'i18next'

export default (plugin: PluginApi) => {
    // first register your tab
    plugin.register.registerTab({
        type: 'taskManager',
        content: () => (
            <>
                <plugin.Rendevoz.InnerComponents.TabTitle 
                    title="I am task manager!"
                />
                <TaskManager plugin={plugin} />
            </>
        )
    })
    
    // second register object and meta-object
    plugin.register.registerObject('task', {
        normalizeDraggingEleToMetaObject: async draggingTask => {
            const taskId = draggingTask.id
            const metaStore = plugin.api.getMetaStore()
            let alreadyExist = false
            const taskMeta = metaStore.getMetaObjectByObjectId(taskId)
            let normalizedMeta = {}
            if (taskMeta){
                 alreadyExist = true
                 normalizedMeta = taskMeta
            }
            // normalize your task object to task meta-object
            else {
                normalizedMeta = {
                    name: draggingTask.taskName,
                    description: `This task starts at ${draggingTask.taskBeginDate}`,
                    type: 'task'
                }
            }
            return {
                normalizedMetaObject: normalizedMeta,
                alreadyHasMeta: alreadyExist,
                // if true, will open edit meta modal
                shouldOpenEditMetaModal: false,
                // no prelinks, you can add links if you want
                preLinks: []
            }
        }
    })
    
    plugin.register.registerMetaObject('task', {
        i18nKey: 'task',
        defaultName: 'Unnamed Task',
        onAdd: m => console.log(m),
        onUpdate: m => console.log(m),
        // if navigate, open taskManager
        onNavigate: m => {
            plugin.Rendevoz.Actions.Layout.openNewTab({data: {type: 'taskManager'}})
        }
    })
    
    // register in sidebar menu
    plugin.register.registerInSidebarMenu('task-manager', {
        icon: 'park-home',
        name: () => t('general.taskManager'),
        onClick: () => {
            plugin.Rendevoz.Actions.Layout.openNewTab({data: {type: 'taskManager'}})
        }
    })
    
    // at last, register i18n
    plugin.register.registerI18N('en-US', {
        general: {
            task: 'Task',
            taskManager: 'Task Manager'
        }
    })
}

Design React Component

Then, we make the React component TaskManager for tab.

const TaskManager = ({plugin}: {plugin: PluginApi}) => {
    const taskStore = plugin.api.getObjectStore('task')
    const allTasks = taskStore.useAll()
    const globalEventEmitter = plugin.Rendevoz.Events.Emit.GlobalEventEmitter
    const globalEventHandler = plugin.Rendevoz.Events.Emit.useEventHandler('global')
    const containerRef = useRef<HTMLDivElement>(null)
    globalEventHandler.on('globalDragPointerUp', ({event, draggingElement}) => {
        // drop on container, and dragging element is meta-object
        if(containerRef.current?.contains(event.target)){
            if(draggingElement.type === 'metaElement'){
            
                taskStore.addOne({
                    taskName: 'New Task', 
                    taskBeginDate: Date.now(),
                    taskTargetMetaId: draggingElement.metaId
                })
                
            }
        }    
    })
    
    useEffect(() => {
        taskStore.getAll()
        globalEventEmitter.addListener('global', globalEventHandler)
        return () => {
            globalEventEmitter.removeListener('global', globalEventHandler)
        }
    }, [])

    return (
        <div ref={containerRef}>
            {Array.from(allTasks.value()).map(task => 
                <Task key={task.id} task={task} plugin={plugin}/>
            )}
        </div>
    )
}

At last, we make task element.

const Task = ({task, plugin}) => {
    const metaStore = plugin.api.getMetaStore()
    const objectStore = plugin.api.getObjectStore('task')
    const taskMeta = metaStore.useValue(task.taskTargetMetaId)
    
    return (
        <div>
            <h5>Task Meta Name: {taskMeta.name}</h5>
            <p>Task Name: {task.taskName}</p>
            <p>Task Begin Date: {task.taskBeginDate}</p>
        </div>
    )
}

Last updated