Skip to content

Commit 6c28ae8

Browse files
author
Sleek Zheng
authored
opt: milestone cover (#6)
* use lodash fix storage * temp * findCoverMilestones
1 parent d30404a commit 6c28ae8

7 files changed

Lines changed: 109 additions & 56 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
"@mui/x-date-pickers": "^6.18.4",
2121
"@types/qs": "^6.9.10",
2222
"@uiw/react-color": "^2.0.3",
23-
"DateRangePicker": "link:@mui/x-date-pickers-pro/DateRangePicker",
2423
"date-fns": "^2.30.0",
24+
"lodash": "^4.17.21",
2525
"next": "14.0.4",
2626
"qs": "^6.11.2",
2727
"react": "^18",
@@ -30,6 +30,7 @@
3030
"use-debounce": "^10.0.0"
3131
},
3232
"devDependencies": {
33+
"@types/lodash": "^4.14.202",
3334
"@types/node": "^20",
3435
"@types/react": "^18",
3536
"@types/react-dom": "^18",

pnpm-lock.yaml

Lines changed: 14 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/page.tsx

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ import {
1111
differenceInDays,
1212
differenceInMonths,
1313
differenceInWeeks,
14-
differenceInYears,
14+
differenceInYears, endOfWeek,
1515
format,
1616
isAfter,
17-
isBefore, isEqual,
18-
min, parseISO
17+
isBefore, isEqual, isSameWeek,
18+
min, startOfWeek,
19+
toDate
1920
} from "date-fns";
2021
import {
2122
createTheme,
@@ -42,20 +43,16 @@ import {
4243
TimelineOppositeContent,
4344
TimelineSeparator
4445
} from "@mui/lab";
45-
import {usePathname, useRouter, useSearchParams} from "next/navigation";
4646
import CustomMilestoneDialog, {CustomMilestoneDialogRef, Milestone} from "@/components/CustomMilestoneDialog";
4747
import useMilestones from "@/hooks/useMilestones";
4848
import {twColorToHex} from "@/utils/colorUtil";
49-
import useStorage from "@/hooks/useStorage";
49+
import useStorage, {StorageSchema} from "@/hooks/useStorage";
5050

5151
export default function Home() {
5252
const [maxYear, setMaxYear] = useState(80)
5353
const [birthday, setBirthday] = useState<Date | undefined>(undefined)
5454
const [unit, setUnit] = useState(12)
5555
const [validDate, setValidDate] = useState(false)
56-
const router = useRouter()
57-
const searchParams = useSearchParams()
58-
const pathname = usePathname()
5956

6057
const {save, load} = useStorage()
6158

@@ -64,7 +61,7 @@ export default function Home() {
6461
}, [unit, maxYear])
6562

6663
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
67-
const {milestones, addMilestone, removeMilestone, confirmDefaultMilestone} = useMilestones()
64+
const {milestones, addMilestone, removeMilestone, confirmDefaultMilestone, getCoveredMilestone} = useMilestones()
6865

6966
const theme = useMemo(
7067
() =>
@@ -76,7 +73,7 @@ export default function Home() {
7673
[prefersDarkMode],
7774
);
7875

79-
const handleChangeBirthday = useDebouncedCallback((value) => {
76+
const handleChangeBirthday = useDebouncedCallback((value: Date | null) => {
8077
if (value) {
8178
setBirthday(value)
8279
try {
@@ -85,7 +82,7 @@ export default function Home() {
8582
confirmDefaultMilestone(value)
8683
save({
8784
user: {
88-
birthday: value
85+
birthday: value.getTime()
8986
}
9087
})
9188
} catch (e) {
@@ -120,7 +117,7 @@ export default function Home() {
120117
return days;
121118
}, [unit, validDate, birthday, maxYear])
122119

123-
const getBackgroundColor = useCallback((day: number) => {
120+
const getBackgroundColors = useCallback((day: number) => {
124121
// 今天
125122
if (day === liveDays) {
126123
return twColorToHex('bg-sky-600')
@@ -150,23 +147,19 @@ export default function Home() {
150147
break
151148
}
152149
}
153-
const colors = milestones.filter(it => {
154-
return isEqual(it.startDate!, date) ||
155-
(isBefore(date, it.endDate!) && isAfter(date, it.startDate!));
156-
})
150+
const colors = getCoveredMilestone(date, unit)
157151
.map(it => twColorToHex(it.color))
158152
if (colors.length) {
159153
return colors
160154
}
161155
}
162156

163157
return twColorToHex('bg-green-200')
164-
}, [liveDays, milestones, birthday, unit])
158+
}, [liveDays, birthday, unit, getCoveredMilestone])
165159

166160

167161
const rectangles = useMemo(() => {
168162
return array.map((it) => {
169-
const backgroundColor = getBackgroundColor(it)
170163
let date: Date = new Date()
171164
if (validDate && birthday) {
172165
switch (unit) {
@@ -188,14 +181,13 @@ export default function Home() {
188181
}
189182
}
190183
}
184+
const backgroundColor = getBackgroundColors(it)
191185

192-
const validMilestones = milestones.filter(item =>
193-
isEqual(item.startDate!, date) ||
194-
(isBefore(date, item.endDate || date) && isAfter(date, item.startDate || date))
195-
)
186+
const validMilestones = getCoveredMilestone(date, unit)
196187
return <Rectangle
197188
key={it}
198-
date={validDate ? format(date, 'yyyy-MM-dd') : undefined}
189+
date={validDate ? date : undefined}
190+
unit={unit}
199191
onClick={() => {
200192
customMilestoneRef.current?.open({
201193
startDate: date,
@@ -206,7 +198,7 @@ export default function Home() {
206198
milestones={validMilestones}
207199
/>
208200
})
209-
}, [getBackgroundColor, array, unit, validDate, birthday, milestones])
201+
}, [getBackgroundColors, array, unit, validDate, birthday, getCoveredMilestone])
210202

211203
const aliveDisplay = useMemo(() => {
212204
if (validDate && birthday) {
@@ -258,7 +250,7 @@ export default function Home() {
258250

259251
const timelineItems = useMemo(() => {
260252
if (validDate) {
261-
return milestones.filter(it => it.startDate !== undefined)
253+
return milestones.filter(it => it.startDate !== undefined && isBefore(it.startDate, new Date()))
262254
.map(it => {
263255
return {
264256
startDate: it.startDate,
@@ -271,21 +263,11 @@ export default function Home() {
271263
}
272264
}, [validDate, milestones])
273265

274-
const createQueryString = useCallback(
275-
(name: string, value: string) => {
276-
const params = new URLSearchParams(searchParams)
277-
params.set(name, value)
278-
279-
return params.toString()
280-
},
281-
[searchParams]
282-
)
283-
284266
useEffect(() => {
285267
const data = load()
286268
if (data?.user) {
287269
if (data.user.birthday) {
288-
setBirthday(parseISO(data.user.birthday))
270+
setBirthday(toDate(data.user.birthday))
289271
setValidDate(true)
290272
}
291273
data.user.maxYear && setMaxYear(data.user.maxYear)
@@ -317,10 +299,11 @@ export default function Home() {
317299
value={maxYear}
318300
type='number'
319301
onChange={(e) => {
320-
setMaxYear(parseInt(e.target.value))
302+
const maxYear = Math.min(parseInt(e.target.value), 120)
303+
setMaxYear(maxYear)
321304
save({
322305
user: {
323-
maxYear: parseInt(e.target.value)
306+
maxYear
324307
}
325308
})
326309
}}/>

src/components/CustomMilestoneDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type Milestone = {
2020
startDate?: Date
2121
endDate?: Date
2222
color: string
23+
order?: number
2324
}
2425
export type CustomMilestoneDialogRef = {
2526
open: (milestone: Partial<Milestone>) => void

src/components/Rectangle.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import {useMemo, useState} from "react";
22
import {buildLinearGradient, twColorToHex} from "@/utils/colorUtil";
33
import {Milestone} from "@/components/CustomMilestoneDialog";
4+
import {format} from "date-fns";
45

56
export interface RectangleProps {
67
backgroundColor?: string[] | string
7-
date?: string
8+
date?: Date
9+
unit?: number
810
milestones?: Milestone[]
911
onClick?: () => void
1012
className?: string
@@ -59,18 +61,35 @@ export default function Rectangle(props: RectangleProps) {
5961
style={style}
6062
>
6163
{
62-
props.milestones && (
64+
(props.milestones?.length || 0) > 0 && (
6365
<div
6466
className={`absolute border border-gray-300 -top-16 flex-col ${hover ? 'flex' : 'hidden'} w-[300px] p-4 rounded z-10 bg-white`}>
6567
<div className='flex flex-col gap-x-2'>
6668
{
67-
props.milestones.map(it => (
68-
<div key={it.label} className='flex gap-1 items-center'>
69-
<Rectangle backgroundColor={it.color}/>
70-
<p>{it.label}</p>
71-
<p>今天是{props.date}</p>
72-
</div>
73-
))
69+
props.milestones!.map(it => {
70+
let formatPattern = 'yyyy年MM月dd号'
71+
switch (props.unit) {
72+
case 1: {
73+
formatPattern = 'yyyy年'
74+
break
75+
}
76+
case 12: {
77+
formatPattern = 'yyyy年MM月'
78+
break
79+
}
80+
case 52: {
81+
formatPattern = 'yyyy年MM月ww周'
82+
break
83+
}
84+
}
85+
return (
86+
<div key={it.label} className='flex gap-2 items-center'>
87+
<Rectangle backgroundColor={it.color}/>
88+
<p>{it.label}</p>
89+
<p>{format(props.date!, formatPattern)}</p>
90+
</div>
91+
)
92+
})
7493
}
7594
</div>
7695
</div>

src/hooks/useMilestones.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {useCallback, useEffect, useState} from "react";
22
import {Milestone} from "@/components/CustomMilestoneDialog";
3-
import {addYears, parseISO} from "date-fns";
3+
import {addYears, endOfWeek, parseISO, startOfWeek} from "date-fns";
44
import useStorage from "@/hooks/useStorage";
55
import {sortMilestones} from "@/utils/milestoneUtil";
66

@@ -10,34 +10,42 @@ export default function useMilestones() {
1010
{
1111
label: '童年',
1212
color: 'bg-zinc-400',
13+
order: 0
1314
},
1415
{
1516
label: '幼儿园',
1617
color: 'bg-red-600',
18+
order: 1
1719
},
1820
{
1921
label: '小学',
2022
color: 'bg-orange-400',
23+
order: 2
2124
},
2225
{
2326
label: '初中',
2427
color: 'bg-yellow-400',
28+
order: 3
2529
},
2630
{
2731
label: '高中',
2832
color: 'bg-rose-400',
33+
order: 4
2934
},
3035
{
3136
label: '大学本科',
3237
color: 'bg-cyan-400',
38+
order: 5
3339
},
3440
{
3541
label: '日常',
3642
color: 'bg-green-200',
43+
order: 6
3744
},
3845
{
3946
label: '今天',
4047
color: 'bg-sky-600',
48+
order: 7
4149
},
4250
])
4351

@@ -96,11 +104,40 @@ export default function useMilestones() {
96104
setMilestones(sortMilestones(newMilestones))
97105
}
98106
}, []);
107+
108+
const getCoveredMilestone = (date: Date, unit: number) => {
109+
return milestones
110+
.filter(it => it.startDate !== undefined && it.endDate !== undefined)
111+
.filter(it => {
112+
switch (unit) {
113+
case 1: {
114+
const startYear = it.startDate!.getFullYear()
115+
const endYear = it.endDate!.getFullYear()
116+
return date.getFullYear() >= startYear && date.getFullYear() <= endYear
117+
}
118+
case 12: {
119+
const startDate = new Date(it.startDate!.getFullYear(), it.startDate!.getMonth())
120+
const endDate = new Date(it.endDate!.getFullYear(), it.endDate!.getMonth())
121+
return date.getTime() >= startDate.getTime() && date.getTime() <= endDate.getTime()
122+
}
123+
case 52: {
124+
const startDate = endOfWeek(it.startDate!)
125+
const endDate = startOfWeek(it.endDate!)
126+
return date.getTime() >= startDate.getTime() && date.getTime() <= endDate.getTime()
127+
}
128+
case 365: {
129+
return date.getTime() >= it.startDate!.getTime() && date.getTime() <= it.endDate!.getTime()
130+
}
131+
}
132+
})
133+
}
134+
99135
return {
100136
milestones,
101137
addMilestone,
102138
removeMilestone,
103139
confirmDefaultMilestone,
104-
isMilestoneExist
140+
isMilestoneExist,
141+
getCoveredMilestone
105142
}
106143
}

0 commit comments

Comments
 (0)