Skip to content

Commit d30404a

Browse files
author
Sleek Zheng
authored
feat: store data in local storage (#5)
* feat: store data in local storage * remove url listen
1 parent 4e1e8c2 commit d30404a

5 files changed

Lines changed: 137 additions & 36 deletions

File tree

src/app/page.tsx

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
format,
1616
isAfter,
1717
isBefore, isEqual,
18-
min
18+
min, parseISO
1919
} from "date-fns";
2020
import {
2121
createTheme,
@@ -46,6 +46,7 @@ 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";
4950

5051
export default function Home() {
5152
const [maxYear, setMaxYear] = useState(80)
@@ -56,6 +57,8 @@ export default function Home() {
5657
const searchParams = useSearchParams()
5758
const pathname = usePathname()
5859

60+
const {save, load} = useStorage()
61+
5962
const array = useMemo(() => {
6063
return Array.from({length: unit * maxYear}, (v, k) => k)
6164
}, [unit, maxYear])
@@ -80,6 +83,11 @@ export default function Home() {
8083
format(value, 'yyyy-MM-dd')
8184
setValidDate(true)
8285
confirmDefaultMilestone(value)
86+
save({
87+
user: {
88+
birthday: value
89+
}
90+
})
8391
} catch (e) {
8492
setValidDate(false)
8593
}
@@ -250,13 +258,14 @@ export default function Home() {
250258

251259
const timelineItems = useMemo(() => {
252260
if (validDate) {
253-
return milestones.map(it => {
254-
return {
255-
startDate: it.startDate,
256-
label: it.label,
257-
color: it.color
258-
}
259-
})
261+
return milestones.filter(it => it.startDate !== undefined)
262+
.map(it => {
263+
return {
264+
startDate: it.startDate,
265+
label: it.label,
266+
color: it.color
267+
}
268+
})
260269
} else {
261270
return []
262271
}
@@ -273,17 +282,16 @@ export default function Home() {
273282
)
274283

275284
useEffect(() => {
276-
const params = new URLSearchParams(searchParams)
277-
const maxYear = params.get('maxYear')
278-
if (maxYear) {
279-
setMaxYear(parseInt(maxYear))
280-
}
281-
const unit = params.get('unit')
282-
if (unit) {
283-
setUnit(parseInt(unit))
285+
const data = load()
286+
if (data?.user) {
287+
if (data.user.birthday) {
288+
setBirthday(parseISO(data.user.birthday))
289+
setValidDate(true)
290+
}
291+
data.user.maxYear && setMaxYear(data.user.maxYear)
292+
data.user.unit && setUnit(data.user.unit)
284293
}
285-
286-
}, []);
294+
}, [])
287295

288296
const customMilestoneRef = useRef<CustomMilestoneDialogRef>(null)
289297
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
@@ -310,13 +318,21 @@ export default function Home() {
310318
type='number'
311319
onChange={(e) => {
312320
setMaxYear(parseInt(e.target.value))
313-
router.push(pathname + '?' + createQueryString('maxYear', e.target.value))
321+
save({
322+
user: {
323+
maxYear: parseInt(e.target.value)
324+
}
325+
})
314326
}}/>
315327
<FormControl>
316328
<FormLabel>显示粒度</FormLabel>
317329
<RadioGroup row value={unit} onChange={(e) => {
318330
setUnit(parseInt(e.target.value))
319-
router.push(pathname + '?' + createQueryString('unit', e.target.value))
331+
save({
332+
user: {
333+
unit: parseInt(e.target.value)
334+
}
335+
})
320336
}}>
321337
<FormControlLabel value={365} control={<Radio/>} label="日"/>
322338
<FormControlLabel value={52} control={<Radio/>} label="周"/>

src/components/Rectangle.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export default function Rectangle(props: RectangleProps) {
6565
<div className='flex flex-col gap-x-2'>
6666
{
6767
props.milestones.map(it => (
68-
<div className='flex gap-1 items-center'>
68+
<div key={it.label} className='flex gap-1 items-center'>
6969
<Rectangle backgroundColor={it.color}/>
7070
<p>{it.label}</p>
7171
<p>今天是{props.date}</p>

src/hooks/useMilestones.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import {useCallback, useState} from "react";
1+
import {useCallback, useEffect, useState} from "react";
22
import {Milestone} from "@/components/CustomMilestoneDialog";
3-
import {addYears, isAfter, isBefore, isEqual} from "date-fns";
3+
import {addYears, parseISO} from "date-fns";
4+
import useStorage from "@/hooks/useStorage";
5+
import {sortMilestones} from "@/utils/milestoneUtil";
46

57
const defaultMilestoneDurationYears = [3, 3, 6, 3, 3, 4]
68
export default function useMilestones() {
79
const [milestones, setMilestones] = useState<Milestone[]>([
810
{
9-
label: '出生',
11+
label: '童年',
1012
color: 'bg-zinc-400',
1113
},
1214
{
@@ -39,26 +41,22 @@ export default function useMilestones() {
3941
},
4042
])
4143

44+
const {save, load} = useStorage()
45+
4246
const addMilestone = useCallback((milestone: Milestone) => {
4347
milestones.splice(milestones.length - 2, 0, milestone)
44-
milestones.sort((a: Milestone, b: Milestone) => {
45-
if (isBefore(a.startDate!, b.startDate!)) {
46-
return -1
47-
}
48-
if (isAfter(a.startDate!, b.startDate!)) {
49-
return 1
50-
}
51-
if (isEqual(a.startDate!, b.startDate!)) {
52-
return isBefore(a.endDate!, b.endDate!) ? -1 : 1
53-
}
54-
return 0
48+
setMilestones([...sortMilestones(milestones)])
49+
save({
50+
milestones
5551
})
56-
setMilestones([...milestones])
5752
}, [milestones])
5853

5954
const removeMilestone = useCallback((index: number) => {
6055
milestones.splice(index, 1)
6156
setMilestones([...milestones])
57+
save({
58+
milestones
59+
})
6260
}, [milestones])
6361

6462
const confirmDefaultMilestone = useCallback((birthday: Date) => {
@@ -75,11 +73,29 @@ export default function useMilestones() {
7573
return object
7674
}))
7775
setMilestones(newMilestones)
76+
save({
77+
milestones: newMilestones
78+
})
7879
}, [])
7980

8081
const isMilestoneExist = (label: string) => {
8182
return milestones.find(it => it.label === label) !== undefined
8283
}
84+
85+
useEffect(() => {
86+
const data = load()
87+
if (data?.milestones) {
88+
const newMilestones = data.milestones
89+
.map(it => {
90+
return {
91+
...it,
92+
startDate: it.startDate ? parseISO(it.startDate + '') : undefined,
93+
endDate: it.endDate ? parseISO(it.endDate + '') : undefined
94+
}
95+
})
96+
setMilestones(sortMilestones(newMilestones))
97+
}
98+
}, []);
8399
return {
84100
milestones,
85101
addMilestone,

src/hooks/useStorage.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {Milestone} from "@/components/CustomMilestoneDialog";
2+
3+
export type StorageSchema = {
4+
user?: {
5+
birthday?: string
6+
maxYear?: number
7+
unit?: number
8+
},
9+
milestones?: Milestone[]
10+
}
11+
const key = 'lifetime__db'
12+
export default function useStorage() {
13+
const save = (schema: StorageSchema) => {
14+
const existData = load()
15+
if (existData) {
16+
const merged = Object.assign(existData, schema)
17+
window.localStorage.setItem(key, JSON.stringify(merged))
18+
} else {
19+
window.localStorage.setItem(key, JSON.stringify(schema))
20+
}
21+
}
22+
23+
const load = (): StorageSchema | undefined => {
24+
const data = window.localStorage.getItem(key)
25+
if (data) {
26+
return JSON.parse(data) as StorageSchema
27+
} else {
28+
return undefined
29+
}
30+
}
31+
32+
return {
33+
save,
34+
load
35+
}
36+
}

src/utils/milestoneUtil.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {Milestone} from "@/components/CustomMilestoneDialog";
2+
3+
export const sortMilestones = (milestones: Milestone[]): Milestone[] => {
4+
return milestones.sort((a, b) => {
5+
if (a.startDate === undefined && b.startDate === undefined) {
6+
// 如果startDate都为undefined,则比较label
7+
return a.label.localeCompare(b.label);
8+
}
9+
10+
if (a.startDate !== undefined && b.startDate !== undefined) {
11+
// 如果startDate都有值,则比较startDate
12+
if (a.startDate.getTime() !== b.startDate.getTime()) {
13+
return a.startDate.getTime() - b.startDate.getTime();
14+
}
15+
16+
// 如果startDate相同,则比较endDate
17+
if (a.endDate !== undefined && b.endDate !== undefined) {
18+
if (a.endDate.getTime() !== b.endDate.getTime()) {
19+
return a.endDate.getTime() - b.endDate.getTime();
20+
}
21+
}
22+
}
23+
24+
// 如果startDate和endDate都相同,或者有一个是undefined,则比较label
25+
if (a.startDate === undefined) {
26+
return 1; // 将未定义的startDate的元素排在末尾
27+
} else if (b.startDate === undefined) {
28+
return -1; // 将未定义的startDate的元素排在末尾
29+
}
30+
31+
return a.label.localeCompare(b.label);
32+
});
33+
};

0 commit comments

Comments
 (0)