Skip to content

Commit

Permalink
fix: add types to garden filters
Browse files Browse the repository at this point in the history
The facilities filter was initialized with `[]`, but it actually needed to be an object with specific keys, not an array. Now its keys/values are also initialized for them to be more type-safe.

Discovered through adding types: the garden toilet facility has always had the key 'toilet', and not 'toilets' - dangerous mistake that seems to have had no problematic effects so far, since it a type-only inaccuracy save for the local testing seeding script.
  • Loading branch information
th0rgall committed May 8, 2024
1 parent 6256e86 commit 7cf35d2
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 41 deletions.
2 changes: 1 addition & 1 deletion api/seeders/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ exports.createGarden = async ({ latitude, longitude }, user, extraProps) => {
},
facilities: {
capacity: 2,
toilets: true,
toilet: true,
shower: false,
electricity: true,
water: false,
Expand Down
22 changes: 12 additions & 10 deletions src/lib/components/Garden/FacilitiesFilter.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<script>
<script lang="ts">
import { _ } from 'svelte-i18n';
import { beforeUpdate } from 'svelte';
import { LabeledCheckbox, Button, Modal } from '$lib/components/UI';
import { allGardens } from '$lib/stores/garden';
import type { Garden } from '$lib/types/Garden';
import { facilities, type CapacityFilterType, type FacilitiesFilterType } from './Filter.svelte';
export let facilities;
export let filteredGardens;
// Will practically never be `undefined`, because it is initialized in beforeUpdate
export let filteredGardens: Garden[] | undefined;
export let filter;
export let show;
export let show: boolean;
// because filter has a 2 way binding, if filter is modified somewhere the fx is called
beforeUpdate(() => {
Expand All @@ -21,14 +23,14 @@
if (filter.capacity.min < 20) filter.capacity.min += 1;
};
function gardenFilterFacilities(garden) {
function gardenFilterFacilities(this: FacilitiesFilterType, garden: Garden) {
for (const [key, value] of Object.entries(this)) {
if (value && garden.facilities[key] !== value) return false;
if (value && garden.facilities[key as keyof FacilitiesFilterType] !== value) return false;
}
return true;
}
function gardenFilterCapacity(garden) {
function gardenFilterCapacity(this: CapacityFilterType, garden: Garden) {
const value = garden.facilities.capacity;
return value >= this.min && value <= this.max;
}
Expand All @@ -42,7 +44,7 @@
let stickToBottom = false;
let maxWidth = 700;
let vw;
let vw: number;
// Stick the modal to the bottom on mobile
$: {
Expand Down Expand Up @@ -112,8 +114,8 @@
<p class="controls-gardens-available">
{@html $_('garden.filter.available', {
values: {
amount: filteredGardens.length,
styledAmount: `<strong>${filteredGardens.length}</strong>`
amount: filteredGardens?.length,
styledAmount: `<strong>${filteredGardens?.length}</strong>`
}
})}
</p>
Expand Down
81 changes: 55 additions & 26 deletions src/lib/components/Garden/Filter.svelte
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
<script context="module" lang="ts">
export const facilities = [
{ name: 'toilet', icon: toiletIcon, transKey: 'garden.facilities.labels.toilet' },
{ name: 'shower', icon: showerIcon, transKey: 'garden.facilities.labels.shower' },
{
name: 'electricity',
icon: electricityIcon,
transKey: 'garden.facilities.labels.electricity'
},
{ name: 'tent', icon: tentIcon, transKey: 'garden.facilities.labels.tent' },
{ name: 'bonfire', icon: bonfireIcon, transKey: 'garden.facilities.labels.bonfire' },
{ name: 'water', icon: waterIcon, transKey: 'garden.facilities.labels.water' },
{
name: 'drinkableWater',
icon: waterIcon,
transKey: 'garden.facilities.labels.drinkable-water'
}
] as const;
export type CapacityFilterType = {
min: number;
max: number;
};
/**
* If a value is not included, it will not be shown as a filter.
* A false value may still be shown.
*/
export type FacilitiesFilterType = {
[key in (typeof facilities)[number]['name']]: boolean;
};
export type FilterType = {
facilities: FacilitiesFilterType;
capacity: CapacityFilterType;
};
/**
* Generates a default (unfiltered) facility filter configuration.
* Needs to be a function that generates new objects, because of the bind: semantics
* applied to `filter.facilities`: changes would mutate the default initializer object, making a reset
* behave unexpectedly (it wouldn't reset filters).
*/
const unfilteredFacilities = () =>
Object.fromEntries(facilities.map((f) => [f.name, false])) as FacilitiesFilterType;
</script>

<script lang="ts">
import { _ } from 'svelte-i18n';
import { Button, Tag } from '$lib/components/UI';
Expand All @@ -14,49 +60,31 @@
} from '$lib/images/icons';
import trackEvent from '$lib/util/track-plausible';
import { PlausibleEvent } from '$lib/types/Plausible';
import type { Garden } from '$lib/types/Garden';
export let filteredGardens;
export let filteredGardens: Garden[] | undefined;
/**
* Prioritize location filter results close to this location.
*/
export let closeToLocation;
let showFilterModal = false;
let filter = {
facilities: [],
let filter: FilterType = {
facilities: unfilteredFacilities(),
capacity: {
min: 1,
max: 20
}
};
const facilities = [
{ name: 'toilet', icon: toiletIcon, transKey: 'garden.facilities.labels.toilet' },
{ name: 'shower', icon: showerIcon, transKey: 'garden.facilities.labels.shower' },
{
name: 'electricity',
icon: electricityIcon,
transKey: 'garden.facilities.labels.electricity'
},
{ name: 'tent', icon: tentIcon, transKey: 'garden.facilities.labels.tent' },
{ name: 'bonfire', icon: bonfireIcon, transKey: 'garden.facilities.labels.bonfire' },
{ name: 'water', icon: waterIcon, transKey: 'garden.facilities.labels.water' },
{
name: 'drinkableWater',
icon: waterIcon,
transKey: 'garden.facilities.labels.drinkable-water'
}
];
let isSearching = false;
let allFiltersTag = false;
let vw;
let vw: number;
const activeFacilities = (currentWidth) => {
const activeFacilities = (currentWidth: number) => {
let activeFacilitiesFiltered = facilities.filter(
(facility) => filter.facilities[facility.name] === true
);
Expand Down Expand Up @@ -128,8 +156,9 @@
showFilterModal = true;
}}
on:close={() => {
// Reset filters
filter = {
facilities: [],
facilities: unfilteredFacilities(),
capacity: {
min: 1,
max: 20
Expand All @@ -145,7 +174,7 @@
{/if}
</div>

<FacilitiesFilter bind:show={showFilterModal} bind:filteredGardens {facilities} bind:filter />
<FacilitiesFilter bind:show={showFilterModal} bind:filteredGardens bind:filter />

<style>
:root {
Expand Down
6 changes: 3 additions & 3 deletions src/lib/components/UI/Tag.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script>
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { crossIcon } from '$lib/images/icons';
import { Icon, Button } from '../UI';
import { Icon } from '../UI';
export let name;
export let icon = null;
export let icon: string | null = null;
export let closeButton = true;
export let pointer = false;
export let invert = false;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/types/Garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type LongLat = {

export type GardenFacilities = {
capacity: number;
toilets?: boolean;
toilet?: boolean;
shower?: boolean;
electricity?: boolean;
water?: boolean;
Expand Down

0 comments on commit 7cf35d2

Please sign in to comment.