Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"error",
{
"name": "react-redux",
"importNames": ["useSelector", "useDispatch", "useFirestoreConnect"],
"message": "Import typed version from hooks/{useSelector|useDispatch|useFirestoreConnect}"
"importNames": ["useSelector", "useDispatch"],
"message": "Import typed version from hooks/{useSelector|useDispatch}"
}
],
"react/prop-types": "off",
Expand Down
28 changes: 12 additions & 16 deletions src/components/molecules/NavBar/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import React, { useState, useMemo, useRef, useCallback } from "react";
import {
ReduxFirestoreQuerySetting,
useFirestoreConnect,
} from "react-redux-firebase";
import { useHistory } from "react-router-dom";
import { OverlayTrigger, Popover } from "react-bootstrap";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
Expand All @@ -16,10 +20,6 @@ import { useRadio } from "hooks/useRadio";
import { useSelector } from "hooks/useSelector";
import { useUser } from "hooks/useUser";
import { useVenueId } from "hooks/useVenueId";
import {
SparkleRFDocQuery,
useFirestoreConnect,
} from "hooks/useFirestoreConnect";

import AuthenticationModal from "components/organisms/AuthenticationModal";
import { GiftTicketModal } from "components/organisms/GiftTicketModal/GiftTicketModal";
Expand Down Expand Up @@ -89,18 +89,14 @@ const NavBar: React.FC<NavBarPropsType> = ({ redirectionUrl }) => {
const radioStations = useSelector(radioStationsSelector);
const parentVenue = useSelector(parentVenueSelector);

const venueParentQuery = useMemo<SparkleRFDocQuery[]>(
() =>
venue?.parentId
? [
{
collection: "venues",
doc: venue.parentId,
storeAs: "parentVenue",
},
]
: [],
[venue]
const venueParentId = venue?.parentId;
const venueParentQuery = useMemo<ReduxFirestoreQuerySetting>(
() => ({
collection: "venues",
doc: venueParentId,
storeAs: "parentVenue",
}),
[venueParentId]
);
useFirestoreConnect(venueParentQuery);

Expand Down
10 changes: 7 additions & 3 deletions src/hooks/roles.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { isEmpty, isLoaded } from "react-redux-firebase";
import {
isEmpty,
isLoaded,
ReduxFirestoreQuerySetting,
useFirestoreConnect,
} from "react-redux-firebase";
import { SparkleSelector } from "types/SparkleSelector";
import { SparkleRFDocQuery, useFirestoreConnect } from "./useFirestoreConnect";
import { useSelector } from "./useSelector";

export type AdminRole = {
allowAll: boolean;
users: string[];
};

export const adminRoleQuery: SparkleRFDocQuery = {
export const adminRoleQuery: ReduxFirestoreQuerySetting = {
collection: "roles",
doc: "admin",
storeAs: "adminRole",
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useConnectCurrentVenueNG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { VenueEvent } from "types/VenueEvent";
import { withId } from "utils/id";

import { useSelector } from "./useSelector";
import { useFirestoreConnect } from "./useFirestoreConnect";
import { useSparkleFirestoreConnect } from "./useSparkleFirestoreConnect";

export const currentVenueNGSelector: SparkleSelector<AnyVenue | undefined> = (
state
Expand All @@ -18,7 +18,7 @@ export const currentVenueEventsNGSelector: SparkleSelector<
> = (state) => state.firestore.data.currentVenueEventsNG;

export const useConnectCurrentVenueNG = (venueId?: string) => {
useFirestoreConnect(
useSparkleFirestoreConnect(
!!venueId
? [
{
Expand Down
26 changes: 12 additions & 14 deletions src/hooks/useConnectRelatedVenues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ import {
import { useConnectCurrentVenueNG } from "./useConnectCurrentVenueNG";
import { useSelector } from "./useSelector";
import {
AnySparkleRFQuery,
SparkleRFDocQuery,
SparkleRFQuery,
useFirestoreConnect,
} from "./useFirestoreConnect";
SparkleRFQConfig,
useSparkleFirestoreConnect,
} from "./useSparkleFirestoreConnect";

const toEventsWithVenueIds = (venueId: string) => (event: VenueEvent) =>
withVenueId(event, venueId);
Expand Down Expand Up @@ -156,7 +154,7 @@ export const useConnectRelatedVenues: ReactHook<
/////////////////////////////////

// Sibling
const siblingVenuesQuery: SparkleRFQuery | undefined = !!parentId
const siblingVenuesQuery: SparkleRFQConfig | undefined = !!parentId
? {
collection: "venues",
where: [["parentId", "==", parentId]],
Expand All @@ -165,7 +163,7 @@ export const useConnectRelatedVenues: ReactHook<
: undefined;

// Sub
const subvenuesQuery: SparkleRFQuery | undefined = !!venueId
const subvenuesQuery: SparkleRFQConfig | undefined = !!venueId
? {
collection: "venues",
where: [["parentId", "==", venueId]],
Expand All @@ -176,37 +174,37 @@ export const useConnectRelatedVenues: ReactHook<
const makeEventsQueryConfig = (
doc: string,
storeAs: string
): SparkleRFDocQuery =>
): SparkleRFQConfig =>
({
collection: "venues",
doc,
subcollections: [{ collection: "events" }],
orderBy: ["start_utc_seconds", "asc"],
storeAs,
} as SparkleRFDocQuery); // @debt a little hacky, but we're consciously subverting our helper protections;
} as SparkleRFQConfig); // @debt a little hacky, but we're consciously subverting our helper protections;

// Parent Events
const parentVenueEventsQuery: SparkleRFDocQuery | undefined =
const parentVenueEventsQuery: SparkleRFQConfig | undefined =
parentId && withEvents
? makeEventsQueryConfig(parentId, "parentVenueEvents")
: undefined;

// Sibling Events
const siblingVenueEventsQueries: SparkleRFDocQuery[] = withEvents
const siblingVenueEventsQueries: SparkleRFQConfig[] = withEvents
? siblingVenues.map((sibling) =>
makeEventsQueryConfig(sibling.id, `siblingVenueEvents-${sibling.id}`)
)
: [];

// Sub Events
const subvenueEventsQueries: SparkleRFDocQuery[] = withEvents
const subvenueEventsQueries: SparkleRFQConfig[] = withEvents
? subvenues.map((subvenue) =>
makeEventsQueryConfig(subvenue.id, `subvenueEvents-${subvenue.id}`)
)
: [];

// Combine / filter for valid queries
const allValidQueries: AnySparkleRFQuery[] = [
const allValidQueries: SparkleRFQConfig[] = [
siblingVenuesQuery,
subvenuesQuery,
parentVenueEventsQuery,
Expand All @@ -215,7 +213,7 @@ export const useConnectRelatedVenues: ReactHook<
].filter(isTruthyFilter);

// Connect
useFirestoreConnect(allValidQueries);
useSparkleFirestoreConnect(allValidQueries);

/////////////////////////////////
// Return
Expand Down
86 changes: 0 additions & 86 deletions src/hooks/useFirestoreConnect.ts

This file was deleted.

59 changes: 59 additions & 0 deletions src/hooks/useSparkleFirestoreConnect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
ReduxFirestoreQuerySetting,
useFirestoreConnect,
isLoaded as _isLoaded,
isEmpty as _isEmpty,
} from "react-redux-firebase";
import { ValidFirestoreKeys } from "types/Firestore";

/**
* Type helper representing all types of T except undefined
*/
export type Defined<T> = T & Exclude<T, undefined>;

/**
* This type allows us to automagically constrain the storeAs
* parameter to keys that exist within ValidFirestoreKeys, ensuring
* that we can't forget to define it in our types when using functions
* that rely on this type (eg. useSparkleFirestoreConnect)
*
* @see ValidFirestoreKeys
* @see useSparkleFirestoreConnect
* @see ReduxFirestoreQuerySetting
*/
export interface SparkleRFQConfig extends ReduxFirestoreQuerySetting {
storeAs?: ValidFirestoreKeys;
}

/**
* A wrapper for useFirestoreConnect() that ensures the config
* we pass it can only use a storeAs key that has been defined
* in our FirestoreData/FirestoreOrdered types.
*
* Note: the config does NOT need to be memo'd before being passed
* to this function as useSparkleFirestoreConnect() determines equality
* through a deep comparison using lodash's isEqual() function.
*
* @param config the config to be passed to useFirestoreConnect()
*
* @see SparkleRFQConfig
* @see ValidFirestoreKeys
* @see ReduxFirestoreQuerySetting
*/
export const useSparkleFirestoreConnect = (config: SparkleRFQConfig[]) =>
useFirestoreConnect(config);

/**
* Use react-redux-firestore's isEmpty helper with
* user-defined type guards to properly narrow types
* when using this helper.
*
* @param item item fetched by react-redux-firestore
*/
export const hasData = <T>(item: T): item is Defined<T> =>
!_isEmpty(item) && item !== undefined;

/**
* Re-export react-redux-firestore's isLoaded helper for convenience.
*/
export const isLoaded = <T>(item: T) => _isLoaded(item);
7 changes: 5 additions & 2 deletions src/pages/Admin/Admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import {
useRouteMatch,
useHistory,
} from "react-router-dom";
import {
ReduxFirestoreQuerySetting,
useFirestoreConnect,
} from "react-redux-firebase";
import dayjs from "dayjs";
import advancedFormat from "dayjs/plugin/advancedFormat";
import "firebase/storage";
Expand Down Expand Up @@ -63,7 +67,6 @@ import VenueDeleteModal from "./Venue/VenueDeleteModal";
import { VenueOwnersModal } from "./VenueOwnersModal";

import "./Admin.scss";
import { SparkleRFQuery, useFirestoreConnect } from "hooks/useFirestoreConnect";

dayjs.extend(advancedFormat);

Expand Down Expand Up @@ -427,7 +430,7 @@ const Admin: React.FC = () => {

const { isAdminUser, isLoading: isAdminUserLoading } = useIsAdminUser(userId);

const venuesOwnedByUserQuery = useMemo<SparkleRFQuery>(
const venuesOwnedByUserQuery = useMemo<ReduxFirestoreQuerySetting>(
() => ({
collection: "venues",
where: [["owners", "array-contains", userId]],
Expand Down
5 changes: 0 additions & 5 deletions src/types/utility.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* Type helper representing all types of T except undefined
*/
export type Defined<T> = T & Exclude<T, undefined>;

export type ExtractProps<T> = T extends React.FunctionComponent<infer U>
? U
: never;
Expand Down