Skip to content

Commit 527f9c5

Browse files
authored
Merge pull request #10 from Mojtaba-NA/refactor-storage-ts
refactor: storage types
2 parents 0141f05 + 30df834 commit 527f9c5

File tree

14 files changed

+670
-704
lines changed

14 files changed

+670
-704
lines changed

biome.json

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,46 @@
11
{
2-
"$schema": "https://biomejs.dev/schemas/1.9.2/schema.json",
3-
"vcs": {
4-
"enabled": false,
5-
"clientKind": "git",
6-
"useIgnoreFile": false
7-
},
8-
"files": {
9-
"ignoreUnknown": false,
10-
"ignore": []
11-
},
12-
"formatter": {
13-
"enabled": true,
14-
"indentStyle": "tab",
15-
"lineWidth": 90
16-
},
17-
"organizeImports": {
18-
"enabled": true
19-
},
20-
"linter": {
21-
"enabled": true,
22-
"rules": {
23-
"recommended": true,
24-
"a11y": {
25-
"useAltText": "off",
26-
"useButtonType": "off",
27-
"useKeyWithClickEvents": "off",
28-
"noSvgWithoutTitle": "off",
29-
"noLabelWithoutControl": "off"
30-
},
31-
"style": {
32-
"useSelfClosingElements": "off"
33-
},
34-
"suspicious": {
35-
"noExplicitAny": "off",
36-
"noArrayIndexKey": "off"
37-
}
38-
}
39-
},
40-
"javascript": {
41-
"formatter": {
42-
"quoteStyle": "single",
43-
"semicolons": "asNeeded"
44-
}
45-
}
2+
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3+
"vcs": {
4+
"enabled": false,
5+
"clientKind": "git",
6+
"useIgnoreFile": false
7+
},
8+
"files": {
9+
"ignoreUnknown": false,
10+
"ignore": []
11+
},
12+
"formatter": {
13+
"enabled": true,
14+
"indentStyle": "tab",
15+
"lineWidth": 90
16+
},
17+
"organizeImports": {
18+
"enabled": true
19+
},
20+
"linter": {
21+
"enabled": true,
22+
"rules": {
23+
"recommended": true,
24+
"a11y": {
25+
"useAltText": "off",
26+
"useButtonType": "off",
27+
"useKeyWithClickEvents": "off",
28+
"noSvgWithoutTitle": "off",
29+
"noLabelWithoutControl": "off"
30+
},
31+
"style": {
32+
"useSelfClosingElements": "off"
33+
},
34+
"suspicious": {
35+
"noExplicitAny": "off",
36+
"noArrayIndexKey": "off"
37+
}
38+
}
39+
},
40+
"javascript": {
41+
"formatter": {
42+
"quoteStyle": "single",
43+
"semicolons": "asNeeded"
44+
}
45+
}
4646
}

package.json

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,57 @@
11
{
2-
"name": "widgetify-webapp",
3-
"private": true,
4-
"version": "1.0.0",
5-
"type": "module",
6-
"scripts": {
7-
"dev": "vite",
8-
"build": "tsc -b && vite build",
9-
"tsbuild": "tsc -b && vite build",
10-
"lint": "eslint .",
11-
"preview": "vite preview",
12-
"test": "npx tailwindcss init"
13-
},
14-
"dependencies": {
15-
"@hello-pangea/dnd": "^18.0.1",
16-
"@tanstack/react-query": "5.66.0",
17-
"add": "2.0.6",
18-
"axios": "1.7.9",
19-
"chart.js": "^4.4.7",
20-
"jalali-moment": "3.3.11",
21-
"moment": "^2.30.1",
22-
"moment-hijri": "^3.0.0",
23-
"motion": "12.3.1",
24-
"ms": "2.1.3",
25-
"react": "18.3.1",
26-
"react-chartjs-2": "^5.3.0",
27-
"react-daisyui": "5.0.5",
28-
"react-dom": "18.3.1",
29-
"react-hot-toast": "2.5.1",
30-
"react-icons": "5.4.0",
31-
"react-router-dom": "7.1.5",
32-
"react-select": "^5.10.0",
33-
"vite-plugin-pwa": "0.21.1"
34-
},
35-
"devDependencies": {
36-
"@biomejs/biome": "1.9.4",
37-
"@eslint/js": "9.17.0",
38-
"@tailwindcss/vite": "4.0.0",
39-
"@types/moment-hijri": "^2.1.4",
40-
"@types/ms": "2.1.0",
41-
"@types/node": "22.13.1",
42-
"@types/react": "18.2.19",
43-
"@types/react-dom": "18.3.5",
44-
"@vitejs/plugin-react": "4.3.4",
45-
"autoprefixer": "10.4.20",
46-
"eslint": "9.17.0",
47-
"eslint-plugin-react-hooks": "5.0.0",
48-
"eslint-plugin-react-refresh": "0.4.16",
49-
"globals": "15.14.0",
50-
"postcss": "8.5.1",
51-
"tailwindcss": "4.0.0",
52-
"typescript": "~5.6.3",
53-
"typescript-eslint": "8.18.2",
54-
"vite": "6.0.5"
55-
}
2+
"name": "widgetify-webapp",
3+
"private": true,
4+
"version": "1.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc -b && vite build",
9+
"tsbuild": "tsc -b && vite build",
10+
"lint": "eslint .",
11+
"preview": "vite preview",
12+
"test": "npx tailwindcss init",
13+
"tcw": "tsc --project tsconfig.app.json -w"
14+
},
15+
"dependencies": {
16+
"@hello-pangea/dnd": "^18.0.1",
17+
"@tanstack/react-query": "5.66.0",
18+
"add": "2.0.6",
19+
"axios": "1.7.9",
20+
"chart.js": "^4.4.7",
21+
"jalali-moment": "3.3.11",
22+
"moment": "^2.30.1",
23+
"moment-hijri": "^3.0.0",
24+
"motion": "12.3.1",
25+
"ms": "2.1.3",
26+
"react": "18.3.1",
27+
"react-chartjs-2": "^5.3.0",
28+
"react-daisyui": "5.0.5",
29+
"react-dom": "18.3.1",
30+
"react-hot-toast": "2.5.1",
31+
"react-icons": "5.4.0",
32+
"react-router-dom": "7.1.5",
33+
"react-select": "^5.10.0",
34+
"vite-plugin-pwa": "0.21.1"
35+
},
36+
"devDependencies": {
37+
"@biomejs/biome": "1.9.4",
38+
"@eslint/js": "9.17.0",
39+
"@tailwindcss/vite": "4.0.0",
40+
"@types/moment-hijri": "^2.1.4",
41+
"@types/ms": "2.1.0",
42+
"@types/node": "22.13.1",
43+
"@types/react": "18.2.19",
44+
"@types/react-dom": "18.3.5",
45+
"@vitejs/plugin-react": "4.3.4",
46+
"autoprefixer": "10.4.20",
47+
"eslint": "9.17.0",
48+
"eslint-plugin-react-hooks": "5.0.0",
49+
"eslint-plugin-react-refresh": "0.4.16",
50+
"globals": "15.14.0",
51+
"postcss": "8.5.1",
52+
"tailwindcss": "4.0.0",
53+
"typescript": "~5.6.3",
54+
"typescript-eslint": "8.18.2",
55+
"vite": "6.0.5"
56+
}
5657
}

src/common/constant/store.key.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
export enum StoreKey {
2-
CURRENCIES = 'CURRENCIES',
3-
hasShownPwaModal = 'hasShownPwaModal',
4-
CURRENCY_UPDATED_AT = 'CURRENCY_UPDATED_AT',
5-
SELECTED_CITY = 'SELECTED_CITY',
6-
CURRENT_WEATHER = 'CURRENT_WEATHER',
7-
LAYOUT_ORDER = 'LAYOUT_ORDER',
8-
Todos = 'Todos',
1+
import type { SelectedCity } from '../../context/setting.context'
2+
import type { Todo } from '../../layouts/calendar/interface/todo.interface'
3+
import type { FetchedCurrency } from '../../services/getMethodHooks/getCurrencyByCode.hook'
4+
import type { FetchedWeather } from '../../services/getMethodHooks/weather/weather.interface'
5+
6+
export interface StorageKV {
7+
CURRENCIES: string[]
8+
hasShownPwaModal: boolean
9+
CURRENCY_UPDATED_AT: string
10+
SELECTED_CITY: SelectedCity
11+
CURRENT_WEATHER: FetchedWeather
12+
LAYOUT_ORDER: string[]
13+
Todos: Todo[]
14+
[key: `currency:${string}`]: FetchedCurrency
915
}
10-
export type StoreKeyType = StoreKey | `currency:${string}`

src/common/storage.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import type { StoreKeyType } from './constant/store.key'
1+
import type { StorageKV } from './constant/store.key'
22

3-
export function setToStorage<T>(key: StoreKeyType, value: T) {
4-
localStorage.setItem(key, JSON.stringify(value))
3+
export function setToStorage<K extends keyof StorageKV>(key: K, value: StorageKV[K]) {
4+
localStorage.setItem(key, JSON.stringify(value))
55
}
66

7-
export function getFromStorage<T>(key: StoreKeyType): T | null {
8-
const value = localStorage.getItem(key)
9-
if (!value) return null
10-
try {
11-
return JSON.parse(value) as T
12-
} catch {
13-
return value as T
14-
}
7+
export function getFromStorage<K extends keyof StorageKV>(key: K): StorageKV[K] | null {
8+
const value = localStorage.getItem(key)
9+
if (!value) return null
10+
try {
11+
return JSON.parse(value)
12+
} catch {
13+
return null
14+
}
1515
}

src/context/todo.context.tsx

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,56 @@
11
import { type ReactNode, createContext, useContext, useEffect, useState } from 'react'
2-
import { StoreKey } from '../common/constant/store.key'
32
import { getFromStorage, setToStorage } from '../common/storage'
43
import type { Todo } from '../layouts/calendar/interface/todo.interface'
54

65
interface TodoContextType {
7-
todos: Todo[]
8-
addTodo: (text: string, date: string) => void
9-
removeTodo: (id: string) => void
10-
toggleTodo: (id: string) => void
11-
setTodos: (todos: Todo[]) => void
6+
todos: Todo[]
7+
addTodo: (text: string, date: string) => void
8+
removeTodo: (id: string) => void
9+
toggleTodo: (id: string) => void
10+
setTodos: (todos: Todo[]) => void
1211
}
1312

1413
const TodoContext = createContext<TodoContextType | undefined>(undefined)
1514
export function TodoProvider({ children }: { children: ReactNode }) {
16-
const [todos, setTodos] = useState<Todo[]>([])
17-
18-
useEffect(() => {
19-
const todosFromStorage = getFromStorage(StoreKey.Todos)
20-
if (todosFromStorage) {
21-
setTodos(todosFromStorage as Todo[])
22-
}
23-
}, [])
24-
25-
const addTodo = (text: string, date: string) => {
26-
const todoList = [
27-
...todos,
28-
{ id: Math.random().toString(36).slice(2), text, completed: false, date },
29-
]
30-
31-
setTodos(todoList)
32-
33-
setToStorage(StoreKey.Todos, todoList)
34-
}
35-
36-
const removeTodo = (id: string) => {
37-
setTodos(todos.filter((todo) => todo.id !== id))
38-
}
39-
40-
const toggleTodo = (id: string) => {
41-
setTodos(
42-
todos.map((todo) =>
43-
todo.id === id ? { ...todo, completed: !todo.completed } : todo,
44-
),
45-
)
46-
}
47-
48-
return (
49-
<TodoContext.Provider value={{ todos, addTodo, removeTodo, toggleTodo, setTodos }}>
50-
{children}
51-
</TodoContext.Provider>
52-
)
15+
const [todos, setTodos] = useState<Todo[]>([])
16+
17+
useEffect(() => {
18+
const todosFromStorage = getFromStorage('Todos')
19+
if (todosFromStorage) {
20+
setTodos(todosFromStorage)
21+
}
22+
}, [])
23+
24+
const addTodo = (text: string, date: string) => {
25+
const todoList = [
26+
...todos,
27+
{ id: Math.random().toString(36).slice(2), text, completed: false, date }
28+
]
29+
30+
setTodos(todoList)
31+
32+
setToStorage('Todos', todoList)
33+
}
34+
35+
const removeTodo = (id: string) => {
36+
setTodos(todos.filter(todo => todo.id !== id))
37+
}
38+
39+
const toggleTodo = (id: string) => {
40+
setTodos(todos.map(todo => (todo.id === id ? { ...todo, completed: !todo.completed } : todo)))
41+
}
42+
43+
return (
44+
<TodoContext.Provider value={{ todos, addTodo, removeTodo, toggleTodo, setTodos }}>
45+
{children}
46+
</TodoContext.Provider>
47+
)
5348
}
5449

5550
export function useTodo() {
56-
const context = useContext(TodoContext)
57-
if (context === undefined) {
58-
throw new Error('useTodo must be used within a TodoProvider')
59-
}
60-
return context
51+
const context = useContext(TodoContext)
52+
if (context === undefined) {
53+
throw new Error('useTodo must be used within a TodoProvider')
54+
}
55+
return context
6156
}

0 commit comments

Comments
 (0)