Skip to content

Commit

Permalink
feat: add feature to load load inifnite words
Browse files Browse the repository at this point in the history
  • Loading branch information
luan0ap committed Nov 10, 2022
1 parent caf1af3 commit 97e5483
Show file tree
Hide file tree
Showing 30 changed files with 301 additions and 128 deletions.
4 changes: 3 additions & 1 deletion server/db.json

Large diffs are not rendered by default.

48 changes: 46 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,59 @@
import React from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import {
ChakraProvider,
theme,
Tabs,
TabList,
TabPanels,
Tab,
TabPanel,
Flex,
Box,
} from '@chakra-ui/react'

import { WordsGrid } from 'features/wordslist/components/WordsGrid'
import { WordsList } from 'features/wordslist/components/WordsList'
import { FavoritesList } from 'features/wordslist/components/FavoritesList'
import { WordsProvider } from 'contexts/Words'

// Create a client
const queryClient = new QueryClient()

export function App() {
return (
<QueryClientProvider client={queryClient}>
<WordsGrid />
<ChakraProvider theme={theme}>
<WordsProvider>
<Flex
width="100vw"
flexDirection={['column', 'row']}
padding="20px 10px">
<Box flex="1"></Box>

<Flex
flex="1"
flexDirection="row"
maxWidth={'50vw'}>
<Tabs>
<TabList>
<Tab>Word list</Tab>
<Tab>Favorites</Tab>
</TabList>

<TabPanels>
<TabPanel>
<WordsList />
</TabPanel>

<TabPanel>
<FavoritesList />
</TabPanel>
</TabPanels>
</Tabs>
</Flex>
</Flex>
</WordsProvider>
</ChakraProvider>
</QueryClientProvider>
)
}
Expand Down
37 changes: 37 additions & 0 deletions src/components/InfiniteScrollGrid/InfiniteScrollGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { useEffect } from 'react'

import { Grid, GridProps } from '@chakra-ui/react'
import { useInView } from 'react-intersection-observer'

export interface InfiniteScrollGridProps extends GridProps {
onViewChange?: () => void
}

export function InfiniteScrollGrid({
onViewChange = () => void 0,
children,
maxHeight = 'md',
templateColumns = ['repeat(3, 1fr)', 'repeat(4, 1fr)', 'repeat(5, 1fr)'],
gap = 0,
...props
}: InfiniteScrollGridProps) {
const { ref, inView } = useInView()

useEffect(() => {
onViewChange()
}, [onViewChange, inView])

return (
<Grid
templateColumns={templateColumns}
gap={gap}
overflowY="scroll"
maxHeight={maxHeight}
{...props}>
{children}
<div ref={ref} />
</Grid>
)
}

export default InfiniteScrollGrid
4 changes: 4 additions & 0 deletions src/components/InfiniteScrollGrid/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { InfiniteScrollGrid } from './InfiniteScrollGrid'

export { InfiniteScrollGrid }
export default InfiniteScrollGrid
50 changes: 30 additions & 20 deletions src/contexts/Words.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
import React, { createContext, ReactNode, useEffect, useState } from 'react'
import { QueryStatus } from '@tanstack/react-query'
import { AxiosResponse } from 'axios'

import useLocalStorage from 'hooks/useLocalStorage.js'
import { Dictionary as DictionaryService } from 'services/Dictionary.js'
import { WordDefinitionResponse } from 'models/WordDefinition'
import useLocalStorage from 'hooks/useLocalStorage'
import { Dictionary as DictionaryService } from 'services/Dictionary'
import { IWordDefinition } from 'models/WordDefinition'
import { useFetch } from 'hooks/useQuery'
import { IWord } from 'models/Word'

const useFavoriteWord = () => {
const [storage, setStorage] = useLocalStorage('@word-meaning/favorites')

return {
add({ word = '' } = { word: '' }): void {
setStorage([...storage, word])
add(item: IWord): void {
setStorage([...storage, item])
},

remove({ word = '' } = { word: '' }): void {
setStorage(storage.filter((w: string) => w !== word))
remove({ id }: IWord): void {
setStorage(storage.filter((saved: IWord) => saved.id !== id))
},

has({ word = '' } = { word: '' }): boolean {
return storage.includes(word)
has({ id }: IWord): boolean {
return storage.some((saved: IWord) => saved.id === id)
},

getAll(): string[] {
getAll(): IWord[] {
return storage
},
}
}

interface WordsContextInterface {
favoriteWord: object
selectedWord: string
setSelectedWord: React.Dispatch<React.SetStateAction<string>>
fetchWordDefinitionStatus: 'error' | 'success' | 'loading'
wordDefinition: WordDefinitionResponse | undefined
favoriteWord: {
add(item: IWord): void
remove({ id }: IWord): void
has({ id }: IWord): boolean
getAll(): IWord[]
}
selectedWord: IWord
setSelectedWord: React.Dispatch<React.SetStateAction<IWord>>
fetchWordDefinitionStatus: QueryStatus
wordDefinition: AxiosResponse<IWordDefinition[]> | undefined
}

const WordsContext = createContext({} as WordsContextInterface)
Expand All @@ -43,20 +51,22 @@ interface Props {

const WordsProvider = ({ children }: Props) => {
const favoriteWord = useFavoriteWord()
const [selectedWord, setSelectedWord] = useState('')
const [selectedWord, setSelectedWord] = useState({} as IWord)

const {
refetch,
data: wordDefinition,
status: fetchWordDefinitionStatus,
} = useFetch<WordDefinitionResponse>({
queryKey: ['word-definition', selectedWord],
queryFn: () => DictionaryService.findOne({ word: selectedWord }),
} = useFetch<IWordDefinition[]>({
queryKey: ['word-definition'],
queryFn: () => DictionaryService.findOne(selectedWord),
enabled: false,
})

useEffect(() => {
refetch()
if (selectedWord) {
refetch()
}
}, [refetch, selectedWord])

return (
Expand Down
7 changes: 0 additions & 7 deletions src/features/wordslist/components/FavoritesGrid/Favorites.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions src/features/wordslist/components/FavoritesGrid/index.ts

This file was deleted.

25 changes: 25 additions & 0 deletions src/features/wordslist/components/FavoritesList/FavoritesList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useState } from 'react'

import { Item } from 'features/wordslist/components/Item'
import { useWordContext } from 'hooks/useWordContext'
import { InfiniteScrollGrid } from 'components/InfiniteScrollGrid'

interface Props {}

export function FavoritesList({}: Props) {
const { favoriteWord } = useWordContext()
const [favorites] = useState(favoriteWord.getAll())

return (
<InfiniteScrollGrid>
{favorites.map(favorite => (
<Item
key={favorite.id}
data={favorite}
/>
))}
</InfiniteScrollGrid>
)
}

export default FavoritesList
3 changes: 3 additions & 0 deletions src/features/wordslist/components/FavoritesList/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { FavoritesList } from './FavoritesList'

export { FavoritesList }
7 changes: 0 additions & 7 deletions src/features/wordslist/components/History/History.jsx

This file was deleted.

3 changes: 0 additions & 3 deletions src/features/wordslist/components/History/index.ts

This file was deleted.

37 changes: 37 additions & 0 deletions src/features/wordslist/components/Item/Item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'
import { GridItem, GridItemProps } from '@chakra-ui/react'

import { IWord } from 'models/Word'
import useWordContext from 'hooks/useWordContext'

export interface ItemProps extends GridItemProps {
isFavorite?: boolean
data: IWord
}

export function Item({
children,
isFavorite,
data,
borderColor = 'blackAlpha.300',
...props
}: ItemProps) {
const { setSelectedWord } = useWordContext()

return (
<GridItem
borderStyle="solid"
borderWidth={1}
borderColor={borderColor}
textAlign="center"
cursor="pointer"
padding={[2, 5]}
onClick={() => setSelectedWord(data)}
wordBreak="break-all"
{...props}>
{data.word}
</GridItem>
)
}

export default Item
4 changes: 4 additions & 0 deletions src/features/wordslist/components/Item/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Item } from './Item'

export { Item }
export default Item
56 changes: 0 additions & 56 deletions src/features/wordslist/components/WordsGrid/WordsGrid.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions src/features/wordslist/components/WordsGrid/index.ts

This file was deleted.

32 changes: 32 additions & 0 deletions src/features/wordslist/components/WordsList/WordsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react'

import useWordsLoadMore from 'features/wordslist/hooks/useWordsLoadMore'
import { Item } from 'features/wordslist/components/Item'
import { useWordContext } from 'hooks/useWordContext'
import { InfiniteScrollGrid } from 'components/InfiniteScrollGrid'

interface Props {}

export function WordsList({}: Props) {
const { data, fetchNextPage } = useWordsLoadMore()
const { favoriteWord } = useWordContext()

return (
<InfiniteScrollGrid onViewChange={fetchNextPage}>
{data?.pages.map((page, i) => {
return (
<React.Fragment key={i}>
{page.data.map(word => (
<Item
key={word.id}
data={word}
/>
))}
</React.Fragment>
)
})}
</InfiniteScrollGrid>
)
}

export default WordsList
3 changes: 3 additions & 0 deletions src/features/wordslist/components/WordsList/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { WordsList } from './WordsList'

export { WordsList }
Loading

0 comments on commit 97e5483

Please sign in to comment.