From 68a7c103b72910ac904cbf72afaffcfe56af1e4f Mon Sep 17 00:00:00 2001 From: Marcin Zelent Date: Mon, 5 Dec 2022 13:00:06 +0100 Subject: Added sorting of groups --- src/App.tsx | 45 +++++++++++++++++++++++++++++++-------------- src/lib/util.ts | 10 +++++++++- src/models/Group.ts | 4 +++- src/models/Trip.ts | 14 +++++++++++++- src/models/index.guard.ts | 7 +++++-- 5 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 8dece4e..3f15a7a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,12 +4,13 @@ import PhotoSwipeLightbox from 'photoswipe/lightbox'; import Sidebar from './components/Sidebar/Sidebar'; import Map from './components/Map/Map'; import { createLightbox } from './components/Lightbox/Lightbox'; +import LoadingScreen from './components/LoadingScreen/LoadingScreen'; import { MediaType, Trip } from './models'; import { isTrip } from './models/index.guard'; +import { getObjectProperty } from './lib/util'; import styles from './App.module.css'; import 'photoswipe/style.css'; -import LoadingScreen from './components/LoadingScreen/LoadingScreen'; function App(): JSX.Element { const [allTrips, setAllTrips] = useState([]); @@ -49,39 +50,55 @@ function App(): JSX.Element { } } - async function getTrip(url: string): Promise { + async function getTrip(trip: Trip): Promise { try { - if (url === undefined) { + if (trip.url === undefined) { return; } - const res = await fetch(url); + const res = await fetch(trip.url); const data: Trip = await res.json(); if (!isTrip(data)) { throw new Error('The requested data file has incorrect structure or types.'); } - const trip = { + const fullTrip = { ...data, - url, + ...trip, downloaded: true, }; if (data.groups !== undefined) { - - trip.groups = [ + const sortProperty = fullTrip.sortProperty ?? 'name'; + const sortOrder = fullTrip.sortOrder ?? false; + + data.groups.sort((a, b) => { + let aVal = getObjectProperty(a, sortProperty); + let bVal = getObjectProperty(b, sortProperty); + + aVal = aVal === undefined ? (typeof bVal === "string" ? "" : 0) : aVal; + bVal = bVal === undefined ? (typeof aVal === "string" ? "" : 0) : bVal; + + if (sortOrder) { + return aVal > bVal ? -1 : (aVal === bVal ? 0 : 1); + } + + return aVal > bVal ? 1 : (aVal === bVal ? 0 : -1); + }); + + fullTrip.groups = [ { id: 'all', name: 'Show all', media: data.groups?.flatMap((g) => g.media), - geoData: data.groups?.flatMap((g) => g.geoData !== undefined ? g.geoData : []) + geoData: data.groups?.flatMap((g) => (g.geoData !== undefined ? g.geoData : [])), }, ...data.groups, ]; } - return trip; + return fullTrip; } catch (err) { setError(`An error occurred while retrieving data: "${err as string}"`); console.error(err); @@ -89,7 +106,7 @@ function App(): JSX.Element { } function updateAllTrips(trips: Trip[], trip: Trip): void { - const updatedTrips = (trips).map((t) => { + const updatedTrips = trips.map((t) => { if (t.id === trip.id) { t = trip; } @@ -106,7 +123,7 @@ function App(): JSX.Element { } async function getFirstTrip(trips: Trip[]): Promise { - const trip = await getTrip(trips[0].url); + const trip = await getTrip(trips[0]); if (trip !== undefined) { updateAllTrips(trips, trip); @@ -131,7 +148,7 @@ function App(): JSX.Element { return await getFirstTrip(trips); } - const trip = await getTrip(trips[tripIndex].url); + const trip = await getTrip(trips[tripIndex]); if (trip === undefined) { return await getFirstTrip(trips); @@ -232,7 +249,7 @@ function App(): JSX.Element { setIsLoading(true); if (!allTrips[tripIndex].downloaded) { - void getTrip(allTrips[tripIndex].url).then((trip) => { + void getTrip(allTrips[tripIndex]).then((trip) => { if (trip === undefined) { return; } diff --git a/src/lib/util.ts b/src/lib/util.ts index 1b000c0..ed216e7 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -27,4 +27,12 @@ export function distanceBetween(latlng1: number[], latlng2: number[]): number { return R * c; } -export default { secondsToTimeString, distanceBetween }; +export function getObjectProperty (object: any, path: string): any { + if (object == null) { + return object; + } + const parts = path.split('.'); + return parts.reduce((object, key) => object?.[key], object); +}; + +export default { secondsToTimeString, distanceBetween, getObjectProperty }; diff --git a/src/models/Group.ts b/src/models/Group.ts index 9b3aa6d..c0d281d 100644 --- a/src/models/Group.ts +++ b/src/models/Group.ts @@ -19,7 +19,7 @@ export default interface Group { description?: string; /** - * Medias in the group. + * Media in the group. */ media: MediaItem[]; @@ -32,4 +32,6 @@ export default interface Group { * Metadata that can be displayed in the description. */ metadata?: { [key: string]: string | number }; + + [key: string]: any; } diff --git a/src/models/Trip.ts b/src/models/Trip.ts index 7f92669..46c462d 100644 --- a/src/models/Trip.ts +++ b/src/models/Trip.ts @@ -19,10 +19,22 @@ export default interface Trip { /** * URL to a JSON file containing data for the trip. */ - url: string; + url?: string; /** * Property indicating if the trip data has been already downloaded. */ downloaded: boolean; + + /** + * A group property to use while sorting groups. + * Nested properties should be delimited with a dot. + * Default: name. + */ + sortProperty?: string; + + /** + * Sort order. true = descending, false = ascending. + */ + sortOrder?: boolean; } diff --git a/src/models/index.guard.ts b/src/models/index.guard.ts index d564ff9..6e9265f 100644 --- a/src/models/index.guard.ts +++ b/src/models/index.guard.ts @@ -47,8 +47,11 @@ export function isTrip(obj: unknown): obj is Trip { typeof typedObj.id === 'string' && typeof typedObj.name === 'string' && (typeof typedObj.groups === 'undefined' || - (Array.isArray(typedObj.groups) && typedObj.groups.every((e: any) => isGroup(e))) - ); + (Array.isArray(typedObj.groups) && typedObj.groups.every((e: any) => isGroup(e)))) && + (typeof typedObj.url === 'undefined' || typeof typedObj.url === 'string') && + (typeof typedObj.downloaded === 'undefined' || typeof typedObj.downloaded === 'boolean') && + (typeof typedObj.sortProperty === 'undefined' || typeof typedObj.sortProperty === 'string') && + (typeof typedObj.sortOrder === 'undefined' || typeof typedObj.sortOrder === 'boolean'); if (!isValid) { throw new Error(`Invalid object: ${JSON.stringify(obj)}`); -- cgit v1.2.3