Skip to content

Commit

Permalink
feat: add sidebar timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
haydenull committed Jun 1, 2022
1 parent 81453e1 commit 0ce840d
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 19 deletions.
77 changes: 77 additions & 0 deletions src/TaskListApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, { useEffect, useState } from 'react'
import { MemoryRouter, Routes, Route } from 'react-router-dom'
import Sider from '@/components/Sider'
import { MENUS } from '@/constants/elements'
import { useAtom } from 'jotai'
import { projectSchedulesAtom, subscriptionSchedulesAtom, todayTasksAtom } from '@/model/schedule'
import { getSchedules, categorizeTasks } from '@/util/schedule'
import { getInitalSettings } from '@/util/baseInfo'
import { getSubCalendarSchedules } from '@/util/subscription'
import { DEFAULT_SETTINGS } from '@/util/constants'
import ProjectDetail from '@/pages/ProjectDetail'
import SidebarTask from './components/SidebarTask'

const App: React.FC<{
defaultRoute?: string
}> = ({ defaultRoute }) => {

// TODO: 使用 only-write 减少重新渲染
const [, setProjectSchedules] = useAtom(projectSchedulesAtom)
// const [, setSubscriptionSchedules] = useAtom(subscriptionSchedulesAtom)

const [todayTasks] = useAtom(todayTasksAtom)
console.log('[faiz:] === todayTasks', todayTasks)
const { overdueTasks, allDayTasks, timeTasks } = categorizeTasks(todayTasks)

useEffect(() => {
async function fetchSchedules() {
setProjectSchedules(await getSchedules())
const { subscriptionList } = getInitalSettings()
// setSubscriptionSchedules(await getSubCalendarSchedules(subscriptionList))
}
fetchSchedules()
}, [])

return (
<main>
{
overdueTasks.length > 0 && (
<div style={{ margin: '8px 0' }}>
{/* <span>Overdue</span> */}
{
overdueTasks.map(task => (
<SidebarTask key={task.id} task={task} type="overdue" />
))
}
</div>
)
}
{
allDayTasks.length > 0 && (
<div>
{/* <span>All Day</span> */}
{
allDayTasks.map(task => (
<SidebarTask key={task.id} task={task} type="allDay" />
))
}
</div>
)
}
{
timeTasks.length > 0 && (
<div>
{/* <span>Time</span> */}
{
timeTasks.map(task => (
<SidebarTask key={task.id} task={task} type="time" showTimeDot />
))
}
</div>
)
}
</main>
)
}

export default App
117 changes: 117 additions & 0 deletions src/components/SidebarTask.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import classNames from 'classnames'
import React, { useState } from 'react'
import { RiExternalLinkLine } from 'react-icons/ri'
import { GrAddCircle } from 'react-icons/gr'
import type { ISchedule } from 'tui-calendar'
import dayjs from 'dayjs'
import { getPageData, moveBlockToNewPage, moveBlockToSpecificBlock } from '@/util/logseq'
import { format } from 'date-fns'
import { ISettingsForm } from '@/util/type'

function getTime(task: ISchedule) {
const startDay = dayjs(task.start as string)
const endDay = dayjs(task.end as string)
const isSameDay = startDay.isSame(endDay, 'day')
if (!isSameDay) return ({ start: startDay.format('MM-DD'), end: endDay.format('MM-DD') })
if (task.isAllDay) return ({ start: 'all day', end: 'all day' })
return ({ start: startDay.format('HH:mm'), end: endDay.format('HH:mm') })
}

const Task: React.FC<{
task: ISchedule
showTimeDot?: boolean
type?: 'overdue' | 'allDay' | 'time'
}> = ({ task, showTimeDot = false, type = 'allDay' }) => {
const startDay = dayjs(task.start as string)
const endDay = dayjs(task.end as string)
const timeFormatter = startDay.isSame(endDay, 'day') ? 'HH:mm' : 'HH:mm (ddd)'
const isActive = type === 'time' && dayjs().isBetween(startDay, endDay)
const isDone = task?.raw?.marker === 'DONE'

const { start, end } = getTime(task)

const navToBlock = async () => {
const rawData: any = task.raw
const { id: pageId, originalName } = rawData?.page || {}
let pageName = originalName
if (!pageName) {
const page = await getPageData({ id: pageId })
pageName = page?.originalName
}
logseq.Editor.scrollToBlockInPage(pageName, task.id!)
}
const embedToToday: React.MouseEventHandler = async (e) => {
console.log('[faiz:] === embedToToday')
e.stopPropagation()
const { preferredDateFormat } = await logseq.App.getUserConfigs()
const todayPage = format(dayjs().valueOf(), preferredDateFormat)
const newBlock = await logseq.Editor.insertBlock(task.id!, `((${task?.id})) #${task.calendarId}`, { before: false, sibling: true })
const logKey: ISettingsForm['logKey'] = logseq.settings?.logKey
if (logKey?.enabled) {
await moveBlockToSpecificBlock(newBlock?.uuid!, todayPage, `[[${logKey?.id}]]`)
} else {
await moveBlockToNewPage(newBlock?.uuid!, todayPage)
}
}

return (
<div className="agenda-sidebar-task flex cursor-pointer" style={{ margin: '10px 0', opacity: isDone ? 0.8 : 1 }} onClick={navToBlock}>
<div
className="flex flex-col justify-between text-right"
style={{
color: isActive ? 'var(--ls-link-text-color)' : 'var(--ls-icon-color)', fontSize: '0.8em',
width: '50px',
}}
>
{
type === 'overdue'
? <div className="w-full">overdue</div>
: (
<>
<div className="w-full">{start}</div>
{ end !== 'all day' && (<div className="w-full">{end}</div>) }
</>
)
}
</div>
<div style={{ width: '4px', backgroundColor: task.bgColor, borderRadius: '2px', margin: '0 6px' }}></div>
<div>
<div style={{ color: 'var(--ls-icon-color)', fontSize: '0.8em' }}>{task.calendarId}</div>
<div style={{ marginBottom: '-0.2em' }}>{task.title}</div>
</div>
{/* <div className="flex items-center"> */}
{
(task.calendarId?.toLocaleLowerCase() !== 'journal' || type === 'overdue') && (
<div onClick={embedToToday} className="agenda-sidebar-task__add flex items-center">
<GrAddCircle style={{ marginLeft: '4px', color: 'var(--ls-icon-color)', fontSize: '0.8em', opacity: '0.7' }} />
</div>
)
}
{/* </div> */}


{/* <div className="w-7 h-7 rounded-full flex items-center justify-center" style={{ backgroundColor: task.bgColor, color: task.color }} title={task.calendarId}>{task?.calendarId?.[0]?.toUpperCase()}</div>
<div className="flex flex-col flex-1 ellipsis mx-4">
<span className="ellipsis text">{task.title}</span>
<div className={classNames(s.subscription, 'text-xs flex justify-between')}>
<span className="description-text">{getTime(task)}</span>
<span className="ml-2 ellipsis #6b531a" title={task.calendarId}>{task.calendarId}</span>
</div>
</div>
<div className="w-5 h-5 cursor-pointer text" onClick={async () => {
const rawData: any = task.raw
const { id: pageId, originalName } = rawData?.page || {}
let pageName = originalName
if (!pageName) {
const page = await getPageData({ id: pageId })
pageName = page?.originalName
}
const { uuid: blockUuid } = await logseq.Editor.getBlock(rawData.id) || { uuid: '' }
logseq.Editor.scrollToBlockInPage(pageName, blockUuid)
logseq.hideMainUI()
}}><RiExternalLinkLine /></div> */}
</div>
)
}

export default Task
36 changes: 36 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'tui-calendar/dist/tui-calendar.css'
import './style/index.less'
import { listenEsc, managePluginTheme, setPluginTheme, toggleAppTransparent } from './util/util'
import ModalApp, { IModalAppProps } from './ModalApp'
import TaskListApp from './TaskListApp'
import { IScheduleValue } from '@/components/ModifySchedule'
import { getBlockData, getBlockUuidFromEventPath, isEnabledAgendaPage, pureTaskBlockContent } from './util/logseq'
import { convertBlockToSchedule, deleteProjectTaskTime, getProjectTaskTime, getSchedules } from './util/schedule'
Expand Down Expand Up @@ -141,12 +142,47 @@ if (isDevelopment) {
logseq.showMainUI()
return Promise.resolve()
})
logseq.Editor.registerSlashCommand('Agenda: Insert Task List Renderer', async () => {
logseq.Editor.insertAtEditingCursor(`{{renderer agenda, task-list}}`)
})
logseq.App.onMacroRendererSlotted(async ({ slot, payload: { arguments: args, uuid } }) => {
console.log('[faiz:] === onMacroRendererSlotted', slot, args, uuid)
if (args?.[0] !== 'agenda' || args?.[1] !== 'task-list') return
const renderered = parent.document.getElementById(slot)?.childElementCount
console.log('[faiz:] === is renderered', renderered)
if (renderered) return

const id = `agenda-task-list-${slot}`
logseq.provideUI({
key: `agenda-${slot}`,
slot,
reset: true,
template: `<div id="${id}"></div>`,
// style: {},
})

requestAnimationFrame(() => {
ReactDOM.render(
<React.StrictMode>
{/* @ts-ignore */}
<TaskListApp />
</React.StrictMode>,
parent.document.getElementById(id)
)
})
})

logseq.provideStyle(`
.external-link[href^="#agenda://"]::before {
content: '📅';
margin: 0 4px;
}
.agenda-sidebar-task__add {
display: none;
}
.agenda-sidebar-task:hover .agenda-sidebar-task__add {
display: flex;
}
`)

if (top) {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Dashboard/components/Project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const Project: React.FC<{
<span className="text-xs description-text ml-1">{dayjs(milestone.start).format('MMM')}</span>
</div>
<span className="text-xs description-text">days left: {dayjs(milestone.start).diff(dayjs(), 'day')}d</span>
<span className="text-xs description-text ellipsis w-full" title={milestone.title}>{milestone.title}</span>
<span className="text-xs description-text ellipsis w-full text-center" title={milestone.title}>{milestone.title}</span>
</div>
)
}
Expand Down
18 changes: 1 addition & 17 deletions src/pages/Dashboard/components/TaskLines.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dayjs from 'dayjs'
import Task from './Task'
import { useAtom } from 'jotai'
import { todayTasksAtom } from '@/model/schedule'
import { categorizeTasks } from '@/util/schedule'

const MOCK_TASKS: ISchedule[] = [
{ id: '111', title: 'foo', start: '2022-05-01T16:00', end: '2022-05-01T17:00', isAllDay: true, calendarId: 'overdue-journal', bgColor: '#aaa' },
Expand All @@ -18,23 +19,6 @@ const MOCK_TASKS: ISchedule[] = [
{ id: '6', title: 'dgfsd', start: '2022-05-01T22:00', end: '2022-05-01T23:00', isAllDay: false, calendarId: 'journal', bgColor: '#aaa' },
]

function categorizeTasks (tasks: ISchedule[]) {
let overdueTasks: ISchedule[] = []
let allDayTasks: ISchedule[] = []
let timeTasks: ISchedule[] = []
tasks.forEach(task => {
if (task.category === 'task') {
overdueTasks.push(task)
} else if (task.isAllDay) {
allDayTasks.push(task)
} else {
timeTasks.push(task)
}
})

return { overdueTasks, allDayTasks, timeTasks }
}

const TaskLines: React.FC<{}> = () => {
const [todayTasks] = useAtom(todayTasksAtom)
console.log('[faiz:] === todayTasks', todayTasks)
Expand Down
19 changes: 18 additions & 1 deletion src/util/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,5 +471,22 @@ export const updateProjectTaskTime = (blockContent: string, timeInfo: { start: D
if (MARKDOWN_PROJECT_TIME_REG.test(blockContent)) {
return blockContent.replace(MARKDOWN_PROJECT_TIME_REG, time)
}
return blockContent + time
return blockContent?.split('\n').map((txt, index) => index === 0 ? txt + ' ' + time : txt).join('\n')
}

export function categorizeTasks (tasks: ISchedule[]) {
let overdueTasks: ISchedule[] = []
let allDayTasks: ISchedule[] = []
let timeTasks: ISchedule[] = []
tasks.forEach(task => {
if (task.category === 'task') {
overdueTasks.push(task)
} else if (task.isAllDay) {
allDayTasks.push(task)
} else {
timeTasks.push(task)
}
})

return { overdueTasks, allDayTasks, timeTasks }
}

0 comments on commit 0ce840d

Please sign in to comment.