Skip to content

Commit

Permalink
begin to store provider url state (#39)
Browse files Browse the repository at this point in the history
* begin to store provider url state

* get all queries to persist in url

* check all providers by default
  • Loading branch information
clairenied authored Sep 26, 2024
1 parent 04399e8 commit 47df5d3
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 89 deletions.
3 changes: 1 addition & 2 deletions src/components/Cards/OverlayCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
LowCostSensorMarker,
} from '../LocationMarker';
import { useStore } from '~/stores';
import { createSignal } from 'solid-js';

import '~/assets/scss/components/overlay-card.scss';

Expand Down Expand Up @@ -39,7 +38,7 @@ export function OverlayCard() {
name="parameter-select"
id="parameter-select"
onChange={(e) => setSelectedMapParameter(e.target.value)}
value={'all'}
value={store.mapParameter.toString()}
>
<option value="all">Any pollutant</option>
<option value="pm25">PM&#8322;&#8325;</option>
Expand Down
86 changes: 34 additions & 52 deletions src/components/Cards/ProvidersCard.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { useStore } from '~/stores';
import { useStore } from "~/stores";

import {
For,
Show,
createSignal,
onMount,
} from 'solid-js';
import { getProviders } from '~/client';
import MiniSearch from 'minisearch';
import bbox from '@turf/bbox';
import { createStore, produce } from 'solid-js/store';

import '~/assets/scss/components/providers-card.scss';
import { For, Show, createSignal, onMount, createEffect } from "solid-js";
import { getProviders } from "~/client";
import MiniSearch from "minisearch";
import bbox from "@turf/bbox";
import { createStore, produce } from "solid-js/store";

import "~/assets/scss/components/providers-card.scss";

interface ProvidersStoreDefinition {
name: string;
Expand All @@ -26,15 +20,11 @@ interface ProvidersStoreDefinition {
export function ProvidersCard() {
const [
store,
{
toggleShowProvidersCard,
setViewport,
setProviders,
setTotalProviders,
},
{ toggleShowProvidersCard, setViewport, setProviders, setTotalProviders },
] = useStore();

const [count, setCount] = createSignal();

const [selectedProviders, setSelectedProviders] = createStore<
ProvidersStoreDefinition[]
>([]);
Expand All @@ -45,22 +35,22 @@ export function ProvidersCard() {
};

const miniSearch = new MiniSearch({
fields: ['name'],
storeFields: ['name'],
fields: ["name"],
storeFields: ["name"],
});

let timeout: ReturnType<typeof setTimeout>;

const onSearchInput = (e: InputEvent) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
const target = e.target as HTMLInputElement
const target = e.target as HTMLInputElement;
const value = target.value;
const res = miniSearch.search(value, { prefix: true });
setSelectedProviders(
() => true,
produce((provider) =>
value != ''
value != ""
? (provider.matchesQuery =
res.map((o) => o.id).indexOf(provider.id) != -1)
: (provider.matchesQuery = true)
Expand All @@ -80,7 +70,10 @@ export function ProvidersCard() {
return {
name: o.name,
id: o.id,
checked: true,
checked:
store.providers.length === 0
? "true"
: store.providers.includes(o.id),
matchesQuery: true,
bbox: o.bbox,
};
Expand All @@ -90,6 +83,10 @@ export function ProvidersCard() {
miniSearch.addAll(selectedProviders);
});

createEffect(() => {
setActiveProviders(selectedProviders.filter((p) => p.checked));
});

function zoomToExtent() {
const providerBounds = selectedProviders
.filter((o) => o.checked)
Expand All @@ -110,12 +107,10 @@ export function ProvidersCard() {
setBounds([minLeft, minBottom, maxRight, maxTop]);
}

function onClickUpdate(selectedProviders: any) {
setActiveProviders(selectedProviders);
function onClickUpdate(selectedProviders: ProvidersStoreDefinition[]) {
const selectedIds = selectedProviders.map((p) => p.id);
setProviders(
selectedProviders.length === store.totalProviders
? []
: selectedProviders.map((o) => o.id)
selectedIds.length === store.totalProviders ? [] : selectedIds
);
}

Expand All @@ -134,25 +129,22 @@ export function ProvidersCard() {
<div class="select-helpers">
<button
class="button-reset type-link-1 providers-list-select-all"
onClick={() =>
setSelectedProviders(() => true, 'checked', true)
}
onClick={() => setSelectedProviders(() => true, "checked", true)}
>
Select All
</button>
<span>|</span>
<button
class="button-reset type-link-1 providers-list-select-none"
onClick={() => {
setSelectedProviders(() => true, 'checked', false);
setSelectedProviders(() => true, "checked", false);
}}
>
Select None
</button>
</div>
<span>
{selectedProviders.filter((o) => o.checked).length}{' '}
providers selected
{activeProviders().length} of {`${count()}`} providers selected
</span>
<Show
when={
Expand All @@ -176,20 +168,16 @@ export function ProvidersCard() {
onInput={(e) => onSearchInput(e)}
/>
<span>
{selectedProviders.filter((o) => o.matchesQuery).length ==
count()
{selectedProviders.filter((o) => o.matchesQuery).length == count()
? `Listing all ${count()} providers`
: `Listing ${
selectedProviders.filter((o) => o.matchesQuery)
.length
selectedProviders.filter((o) => o.matchesQuery).length
} of ${count()} providers`}
</span>
</div>
<div class="list-container">
<ul class="providers-list">
<For
each={selectedProviders.filter((o) => o.matchesQuery)}
>
<For each={selectedProviders.filter((o) => o.matchesQuery)}>
{(provider, i) => {
if (provider.matchesQuery) {
return (
Expand All @@ -210,7 +198,7 @@ export function ProvidersCard() {
onChange={(e) => {
setSelectedProviders(
(p) => p.id == provider.id,
'checked',
"checked",
e.target.checked
);
}}
Expand All @@ -226,16 +214,10 @@ export function ProvidersCard() {
<footer class="providers-card__footer">
<button
class={`btn btn-primary ${
selectedProviders.filter((o) => o.checked).length > 0
? ''
: 'btn-primary--disabled'
activeProviders().length > 0 ? "" : "btn-primary--disabled"
}`}
disabled={
selectedProviders.filter((o) => o.checked).length == 0
}
onClick={() =>
onClickUpdate(selectedProviders.filter((o) => o.checked))
}
disabled={activeProviders().length === 0}
onClick={() => onClickUpdate(activeProviders())}
>
Update
</button>
Expand Down
61 changes: 47 additions & 14 deletions src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { Map } from '~/components/Map';
import { getUserId } from '~/db';
import { LocationDetailCard } from '~/components/Cards/LocationDetailCard';
import { FlipCard } from '~/components/Cards/FlipCard';
import { Header } from '~/components/Header';
import { Map } from "~/components/Map";
import { getUserId } from "~/db";
import { LocationDetailCard } from "~/components/Cards/LocationDetailCard";
import { FlipCard } from "~/components/Cards/FlipCard";
import { Header } from "~/components/Header";

import "~/assets/scss/routes/index.scss";
import { useLocation, useNavigate } from "@solidjs/router";
import { useStore } from "~/stores";
import { createEffect, createMemo, onMount } from "solid-js";

import '~/assets/scss/routes/index.scss';
import { useLocation, useNavigate } from '@solidjs/router';
import { useStore } from '~/stores';
import { createEffect, onMount } from 'solid-js';

export const route = {
load: () => getUserId(),
};

export default function Home() {
const [store, { setSelectedLocationsId }] = useStore();
const [store, actions] = useStore();

const setSelectedLocationsId = actions.setSelectedLocationsId;
const setSelectedMapParameter = actions.setSelectedMapParameter;
const setProviders = actions.setProviders;

const location = useLocation();
const navigate = useNavigate();
Expand All @@ -23,14 +28,42 @@ export default function Home() {
if (location.query.location) {
setSelectedLocationsId(Number(location.query.location));
}
})

if (location.query?.parameter) {
setSelectedMapParameter(location.query.parameter);
}

if (location.query?.provider) {
const params = new URLSearchParams(location.search);
const providersArray = params.get("provider")?.split(",").map(providerId => Number(providerId));
providersArray && setProviders(providersArray);
}
});

createEffect(() => {
const getProviders = createMemo(() => store.providers);
const providers = getProviders();

const searchParams = new URLSearchParams();

if (store.locationsId !== undefined) {
navigate(`${location.pathname}?location=${store.locationsId}${location.hash}`);
} else {
navigate(`${location.pathname}${location.hash}`);
searchParams.append("location", store.locationsId.toString());
}

if (store.mapParameter !== "all") {
searchParams.append("parameter", store.mapParameter.toString());
}

if (providers.length > 0) {
searchParams.append("provider", store.providers.join(","));
}

const queryString = searchParams.toString();
const path = queryString
? `${location.pathname}?${queryString}${location.hash}`
: `${location.pathname}${location.hash}`;

navigate(path);
});

return (
Expand Down
43 changes: 22 additions & 21 deletions src/stores/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContext, useContext, Component } from 'solid-js';
import { createStore } from 'solid-js/store';
import { Viewport } from 'solid-map-gl';
import { createContext, useContext, Component } from "solid-js";
import { createStore, produce } from "solid-js/store";
import { Viewport } from "solid-map-gl";

type Store = [
{
Expand Down Expand Up @@ -31,26 +31,25 @@ type Store = [
{
setSelectedLocationsId: () => void;
clearLocationsId?: () => void;
setSelectedMapParameter?: () => void;
setSelectedMapParameter: (mapParameter: string) => void;
setDeleteListsId?: () => void;
setDeleteListLocationsId? : () => void;
setDeleteListLocationsId?: () => void;


setListParametersId? : () => void;
setListParameter? : () => void;
setListParametersId?: () => void;
setListParameter?: () => void;

clearDeleteListsId?: () => void;
toggleDeleteListModalOpen?: () => void;
toggleNewListModalOpen?: () => void;
toggleEditListModalOpen?: () => void;
toggleShowProvidersCard?: () => void;
toggleShowProvidersCard: () => void;
toggleRegenerateKeyModalOpen?: () => void;
toggleDeleteListLocationModalOpen? : () => void;
toggleDeleteListLocationModalOpen?: () => void;
setViewport?: () => void;
toggleMonitor?: () => void;
toggleAirSensor?: () => void;
toggleMapIsActive?: () => void;
setProviders?: () => void;
setProviders: (providers: any[]) => void;
setRecentMeasurements?: () => void;
addRecentMeasurements?: () => void;
updateRecentMeasurements?: () => void;
Expand Down Expand Up @@ -166,17 +165,19 @@ export const StoreProvider: Component<{}> = (props) => {
});
},
toggleDeleteListLocationModalOpen() {
setState({deleteListLocationModalOpen: !state.deleteListLocationModalOpen})
setState({
deleteListLocationModalOpen: !state.deleteListLocationModalOpen,
});
},
setDeleteListLocationsId(listLocationsId: number) {
setState({listLocationsId: listLocationsId})
setState({ listLocationsId: listLocationsId });
},
setListParametersId(parametersId: number) {
setState({listParametersId: parametersId})
setState({ listParametersId: parametersId });
},
setListParameter(parameter: string) {
setState({ listParameter: parameter });
},
setListParameter(parameter:string ) {
setState({listParameter: parameter})
}
},
];

Expand All @@ -188,13 +189,13 @@ export const StoreProvider: Component<{}> = (props) => {
};

function useStoreContext() {
const context = useContext(StoreContext)
const context = useContext(StoreContext);
if (!context) {
throw new Error("useStoreContext: cannot find a StoreContext")
throw new Error("useStoreContext: cannot find a StoreContext");
}
return context
return context;
}

export function useStore() : Store {
export function useStore(): Store {
return useStoreContext();
}

0 comments on commit 47df5d3

Please sign in to comment.