Skip to content

Commit

Permalink
Works again - Rm date btn - infinite scroll works better - performanc…
Browse files Browse the repository at this point in the history
…e is ehhh
  • Loading branch information
ugtthis committed Jul 17, 2024
1 parent 37d488d commit 0b86a9a
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 83 deletions.
17 changes: 11 additions & 6 deletions src/components/RouteSorter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import { SortKey, SortOption, SortOrder } from '~/utils/sorting'
const GRADIENT = 'from-cyan-700 via-blue-800 to-purple-900'

interface RouteSorterProps {
onSortChange: (key: SortKey, order: SortOrder) => void
onSortChange: (key: SortKey, order: SortOrder | null) => void
currentSort: SortOption
}

export const RouteSorter: Component<RouteSorterProps> = (props) => {
const [sortOptions] = createStore<SortOption[]>([
{ label: 'Date', key: 'date', order: 'desc' },
{ label: 'Duration', key: 'duration', order: 'desc' },
{ label: 'Miles', key: 'miles', order: 'desc' },
{ label: 'Engaged', key: 'engaged', order: 'desc' },
Expand All @@ -26,10 +25,16 @@ export const RouteSorter: Component<RouteSorterProps> = (props) => {
}

const handleClick = (clickedOption: SortOption) => {
let newOrder: SortOrder
let newOrder: SortOrder | null
if (props.currentSort.key === clickedOption.key) {
// If the same button is clicked, toggle the order
newOrder = props.currentSort.order === 'desc' ? 'asc' : 'desc'
// If the same button is clicked, toggle the order or deactivate the filter
if (props.currentSort.order === 'desc') {
newOrder = 'asc'
} else if (props.currentSort.order === 'asc') {
newOrder = null
} else {
newOrder = 'desc'
}
} else {
newOrder = 'desc'
}
Expand Down Expand Up @@ -71,7 +76,7 @@ export const RouteSorter: Component<RouteSorterProps> = (props) => {
</span>
{props.currentSort.key === option.key && (
<span class="relative z-10 ml-3 inline-block w-4 text-white transition-transform duration-300">
{props.currentSort.order === 'asc' ? '↑' : '↓'}
{props.currentSort.order === 'asc' ? '↑' : props.currentSort.order === 'desc' ? '↓' : ''}
</span>
)}
{/* Added div for hover effect since gradient effect is using absolute positioning */}
Expand Down
167 changes: 90 additions & 77 deletions src/pages/dashboard/components/RouteList.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,72 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import {
createEffect,
createResource,
createSignal,
For,
Suspense,
onCleanup,
} from 'solid-js'
import type { VoidComponent } from 'solid-js'
import clsx from 'clsx'
import dayjs from 'dayjs'

import type { RouteSegments } from '~/types'

import RouteCard from '~/components/RouteCard'
import { fetcher } from '~/api'
import Button from '~/components/material/Button'
import RouteSorter from '~/components/RouteSorter'
import { SortOption, SortKey, sortRoutes } from '~/utils/sorting'

const PAGE_SIZE = 3
const PAGE_SIZE = 7
const DEFAULT_DAYS = 7

type RouteListProps = {
class?: string
dongleId: string
}

const pages: Promise<RouteSegments[]>[] = []
const fetchRoutesWithinDays = async (dongleId: string, days: number): Promise<RouteSegments[]> => {
const now = dayjs().valueOf()
const pastDate = dayjs().subtract(days, 'day').valueOf()
const endpoint = (end: number) => `/v1/devices/${dongleId}/routes_segments?limit=${PAGE_SIZE}&end=${end}`

let allRoutes: RouteSegments[] = []
let end = now

while (true) {
const key = `${endpoint(end)}`
try {
const routes = await fetcher<RouteSegments[]>(key)
if (routes.length === 0) break
allRoutes = [...allRoutes, ...routes]
end = routes.at(-1)!.end_time_utc_millis - 1
if (end < pastDate) break
} catch (error) {
console.error('Error fetching routes:', error)
break
}
}
return allRoutes.filter(route => route.end_time_utc_millis >= pastDate)
}

const RouteList: VoidComponent<RouteListProps> = (props) => {
const [sortOption, setSortOption] = createSignal<SortOption>({ label: 'Date', key: 'date', order: 'desc' })
const [allRoutes, setAllRoutes] = createSignal<RouteSegments[]>([])
const [sortedRoutes, setSortedRoutes] = createSignal<RouteSegments[]>([])
const [size, setSize] = createSignal(1)

const endpoint = () => `/v1/devices/${props.dongleId}/routes_segments?limit=${PAGE_SIZE}`
const getKey = (previousPageData?: RouteSegments[]): string | undefined => {
if (!previousPageData) return endpoint()
if (previousPageData.length === 0) return undefined
const lastSegmentEndTime = previousPageData.at(-1)!.end_time_utc_millis
return `${endpoint()}&end=${lastSegmentEndTime - 1}`
}
const getPage = (page: number): Promise<RouteSegments[]> => {
if (!pages[page]) {
// eslint-disable-next-line no-async-promise-executor
pages[page] = new Promise(async (resolve) => {
const previousPageData = page > 0 ? await getPage(page - 1) : undefined
const key = getKey(previousPageData)
resolve(key ? fetcher<RouteSegments[]>(key) : [])
})
}
return pages[page]
}
const [loading, setLoading] = createSignal(false)
const [days, setDays] = createSignal(DEFAULT_DAYS)

createEffect(() => {
if (props.dongleId) {
pages.length = 0
setSize(1)
setAllRoutes([])
}
})

const onLoadMore = () => setSize(size() + 1)
const pageNumbers = () => Array.from(Array(size()).keys())

// Effect to update allRoutes when new data is fetched
createEffect(() => {
const fetchData = async () => {
const newRoutes: RouteSegments[] = []
for (const i of pageNumbers()) {
const routes = await getPage(i)
newRoutes.push(...routes)
}
if (newRoutes.length > 0) {
setAllRoutes(newRoutes)
}
setLoading(true)
fetchRoutesWithinDays(props.dongleId, days()).then(routes => {
setAllRoutes(routes)
setLoading(false)
}).catch(error => {
console.error('Error fetching routes:', error)
setLoading(false)
})
}
void fetchData()
})

// Effect to sort routes when allRoutes or sortOption changes
Expand All @@ -83,22 +75,50 @@ const RouteList: VoidComponent<RouteListProps> = (props) => {
const routes = allRoutes()
const currentSortOption = sortOption()
if (routes.length > 0) {
const sorted = await sortRoutes(routes, currentSortOption)
setSortedRoutes(sorted)
try {
const sorted = await sortRoutes(routes, currentSortOption)
setSortedRoutes(sorted)
} catch (error) {
console.error('Error sorting routes:', error)
}
}
}
void sortAndSetRoutes()
})

// Handler for sort option changes
const handleSortChange = (key: SortKey, order: 'asc' | 'desc') => {
const label = key.charAt(0).toUpperCase() + key.slice(1)
setSortOption({ label, key, order })
// Reset allRoutes and refetch sorted routes
setAllRoutes([])
setSize(1)
const handleSortChange = (key: SortKey, order: 'asc' | 'desc' | null) => {
if (order === null) {
setSortOption({ label: 'Date', key: 'date', order: 'desc' })
} else {
const label = key.charAt(0).toUpperCase() + key.slice(1)
setSortOption({ label, key, order })
}
}

// Infinite scrolling observer
let bottomRef: HTMLDivElement | undefined
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting && !loading()) {
setLoading(true)
setDays(days => days + DEFAULT_DAYS)
}
},
{ rootMargin: '200px' },
)

createEffect(() => {
if (bottomRef) {
observer.observe(bottomRef)
}
return () => {
if (bottomRef) observer.unobserve(bottomRef)
}
})

onCleanup(() => observer.disconnect())

return (
<div
class={clsx(
Expand All @@ -107,32 +127,25 @@ const RouteList: VoidComponent<RouteListProps> = (props) => {
)}
>
<RouteSorter onSortChange={handleSortChange} currentSort={sortOption()} />
<For each={pageNumbers()}>
{(i) => {
const [routes] = createResource(() => i, getPage)
return (
<Suspense
fallback={
<>
<div class="skeleton-loader elevation-1 flex h-[336px] max-w-md flex-col rounded-lg bg-surface-container-low" />
<div class="skeleton-loader elevation-1 flex h-[336px] max-w-md flex-col rounded-lg bg-surface-container-low" />
<div class="skeleton-loader elevation-1 flex h-[336px] max-w-md flex-col rounded-lg bg-surface-container-low" />
</>
}
>
<For each={routes()}>
{(route) => <RouteCard route={route} />}
</For>
</Suspense>
)
}}
</For>
<div class="flex justify-center">
<Button onClick={onLoadMore}>Load more</Button>
<Suspense
fallback={
<>
<div class="skeleton-loader elevation-1 flex h-[336px] max-w-md flex-col rounded-lg bg-surface-container-low" />
<div class="skeleton-loader elevation-1 flex h-[336px] max-w-md flex-col rounded-lg bg-surface-container-low" />
<div class="skeleton-loader elevation-1 flex h-[336px] max-w-md flex-col rounded-lg bg-surface-container-low" />
</>
}
>
<For each={sortedRoutes()}>
{(route) => <RouteCard route={route} />}
</For>
</Suspense>
<div ref={bottomRef} class="flex justify-center">
{loading() && <div>Loading more...</div>}
</div>
<div>
{sortedRoutes().length === 0 && <div>No routes found</div>}
{sortedRoutes().length > 0 && <div>All routes loaded</div>}
{sortedRoutes().length === 0 && !loading() && <div>No routes found</div>}
{sortedRoutes().length > 0 && !loading() && <div>All routes loaded</div>}
</div>
</div>
)
Expand Down

0 comments on commit 0b86a9a

Please sign in to comment.