Skip to content

Commit

Permalink
feat: show subscriptions in sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
haydenull committed Jun 20, 2022
1 parent 36886ed commit d786bc7
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 5 deletions.
24 changes: 19 additions & 5 deletions src/TaskListApp.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import React, { useEffect, useState } from 'react'
import { useAtom } from 'jotai'
import { projectSchedulesAtom } from '@/model/schedule'
import { categorizeTasks } from '@/util/schedule'
import { projectSchedulesAtom, subscriptionSchedulesAtom, todaySubscriptionSchedulesAtom } from '@/model/schedule'
import { categorizeSubscriptions, categorizeTasks } from '@/util/schedule'
import SidebarTask from './components/SidebarTask'
import { fullEventsAtom, journalEventsAtom, projectEventsAtom, todayTasksAtom } from '@/model/events'
import { getInternalEvents } from './util/events'
import SidebarSubscription from './components/SidebarSubscription'

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

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

const [todaySubscriptions] = useAtom(todaySubscriptionSchedulesAtom)
const { allDaySubscriptions, timeSubscriptions } = categorizeSubscriptions(todaySubscriptions)
const [todayTasks] = useAtom(todayTasksAtom)
const { overdueTasks, allDayTasks, timeTasks } = categorizeTasks(todayTasks)

const [, setFullEvents] = useAtom(fullEventsAtom)
const [, setJournalEvents] = useAtom(journalEventsAtom)
const [, setProjectEvents] = useAtom(projectEventsAtom)

console.log('[faiz:] === subscriptions tasklist', todaySubscriptions)

useEffect(() => {
async function fetchSchedules() {
const res = await getInternalEvents()
Expand Down Expand Up @@ -59,9 +63,14 @@ const App: React.FC<{
)
}
{
allDayTasks.length > 0 && (
(allDayTasks.length > 0 || allDaySubscriptions.length > 0) && (
<div>
{/* <span>All Day</span> */}
{
allDaySubscriptions.map(subscription => (
<SidebarSubscription key={subscription.id} subscription={subscription} type="allDay" />
))
}
{
allDayTasks.map(task => (
<SidebarTask key={task.id} task={task} type="allDay" />
Expand All @@ -71,9 +80,14 @@ const App: React.FC<{
)
}
{
timeTasks.length > 0 && (
(timeTasks.length > 0 || timeSubscriptions.length > 0) && (
<div>
{/* <span>Time</span> */}
{
timeSubscriptions.map(subscription => (
<SidebarSubscription key={subscription.id} subscription={subscription} type="time" />
))
}
{
timeTasks.map(task => (
<SidebarTask key={task.id} task={task} type="time" />
Expand Down
124 changes: 124 additions & 0 deletions src/components/SidebarSubscription.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { RiAddCircleLine } from 'react-icons/ri'
import dayjs from 'dayjs'
import { getPageData, moveBlockToNewPage, moveBlockToSpecificBlock } from '@/util/logseq'
import { format } from 'date-fns'
import { ISettingsForm } from '@/util/type'
import { IEvent } from '@/util/events'
import { ISchedule } from 'tui-calendar'

function getTime(task: IEvent, overdue = false) {
const startStr = task?.addOns.start
const endStr = task?.addOns.end

const startDay = dayjs(startStr)
const endDay = dayjs(endStr)
const isSameDay = startDay.isSame(endDay, 'day')

if (overdue) {
if (task.addOns.allDay) {
if (isSameDay) return ({ start: startDay.format('MM-DD') })
return ({ start: startDay.format('MM-DD'), end: endDay.format('MM-DD') })
} else {
if (isSameDay && startDay.isSame(dayjs(), 'day')) return ({ start: startDay.format('HH:mm'), end: endDay.format('HH:mm') })
if (isSameDay) return ({ start: startDay.format('MM-DD') })
return ({ start: startDay.format('MM-DD'), end: endDay.format('MM-DD') })
}
}

if (!isSameDay) return ({ start: startDay.format('MM-DD'), end: endDay.format('MM-DD') })
if (task.addOns.allDay) return ({ start: 'all-day' })
return ({ start: startDay.format('HH:mm'), end: endDay.format('HH:mm') })
}

const Task: React.FC<{
subscription: ISchedule
type?: 'allDay' | 'time'
}> = ({ task, type = 'allDay' }) => {
const startDay = dayjs(task.addOns.start)
const endDay = dayjs(task.addOns.end)
const isActive = type === 'time' && dayjs().isBetween(startDay, endDay)
const isDone = task.addOns.status === 'done'
const calendarConfig = task.addOns.calendarConfig

const { start, end } = getTime(task, type === 'overdue')

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

return (
<div className="agenda-sidebar-task flex cursor-pointer" style={{ margin: '10px 0', opacity: isDone ? 0.4 : 0.9 }} 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',
color: type === 'overdue' ? '#ed4245' : 'var(--ls-icon-color)', fontSize: '0.8em',
width: '50px',
}}
>
<div className="w-full">{start}</div>
{ end && (<div className="w-full" style={{ opacity: 0.6 }}>{end}</div>) }
</div>
<div style={{ width: '4px', backgroundColor: calendarConfig?.bgColor, borderRadius: '2px', margin: '0 6px' }}></div>
<div style={{ width: 'calc(100% - 90px)', paddingBottom: '24px', position: 'relative' }}>
<div style={{ color: 'var(--ls-icon-color)', fontSize: '0.8em', opacity: 0.6 }}>{calendarConfig?.id}</div>
<div className="agenda-sidebar-task__title" style={{ marginBottom: '-0.2em', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', position: 'absolute', bottom: 0, width: 'calc(100% - 30px)' }} title={task.addOns.showTitle}>{task.addOns.showTitle}</div>
{ isActive && <span
className="ui__button bg-indigo-600"
style={{ fontSize: '0.5em', position: 'absolute', right: 0, bottom: 0, padding: '0 3px', borderRadius: '3px' }}
>NOW</span> }
</div>

<div onClick={embedToToday} className="agenda-sidebar-task__add flex" style={{ alignItems: 'center', paddingLeft: '8px' }}>
<RiAddCircleLine style={{ color: 'var(--ls-icon-color)', fontSize: '0.9em', opacity: '0.7', marginTop: '0.2em' }} />
</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
9 changes: 9 additions & 0 deletions src/model/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ import { getInitalSettings } from '@/util/baseInfo'
export const projectSchedulesAtom = atom<ISchedule[]>([]) // include overdue schedules
export const subscriptionSchedulesAtom = atom<ISchedule[]>([])

export const todaySubscriptionSchedulesAtom = atom<ISchedule[]>(get => {
const subscriptions = get(subscriptionSchedulesAtom)
return subscriptions.filter(schedule => {
const start = dayjs(schedule.start as string)
const end = dayjs(schedule.end as string)
return dayjs().isBetween(start, end, 'day', '[]')
}).sort((a, b) => dayjs(a.start as string).diff(dayjs(b.start as string)))
})

// export const schedulesAtom = atom((get) => get(projectSchedulesAtom).concat(get(subscriptionSchedulesAtom)))

// export const projectRawSchedulesAtom = atom<ISchedule[]>((get) => {
Expand Down
14 changes: 14 additions & 0 deletions src/util/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,20 @@ export function categorizeTasks (tasks: IEvent[]) {
return { overdueTasks, allDayTasks, timeTasks }
}

export function categorizeSubscriptions (subscriptions: ISchedule[]) {
let allDaySubscriptions: ISchedule[] = []
let timeSubscriptions: ISchedule[] = []
subscriptions.forEach(subscription => {
if (subscription.isAllDay) {
allDaySubscriptions.push(subscription)
} else {
timeSubscriptions.push(subscription)
}
})

return { allDaySubscriptions, timeSubscriptions }
}

export const judgeIsMilestone = (block: BlockEntity) => {
return / #milestone/.test(block.content) || / #\[\[milestone\]\]/.test(block.content)
}

0 comments on commit d786bc7

Please sign in to comment.