diff --git a/components/CurrentCommitteeCard/CurrentCommitteeCard.tsx b/components/CurrentCommitteeCard/CurrentCommitteeCard.tsx deleted file mode 100644 index 0fd156958..000000000 --- a/components/CurrentCommitteeCard/CurrentCommitteeCard.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { FC } from "react" -import styled from "styled-components" -import { Card as MapleCard } from "../Card" -import { Card as BootstrapCard } from "react-bootstrap" - -const Chamber = styled.span` - font-size: 16px; -` - -const Committee = styled.div` - display: flex; - width: 100%; - text-align: center; - justify-content: center; - font-size: 22px; -` - -const Container = styled.div` - max-width: 255px; - font-family: Nunito; -` -const Head = styled(BootstrapCard.Header)` - background-color: var(--bs-blue); - color: white; - font-size: 22px; -` - -export const CurrentCommitteeCard: FC< - React.PropsWithChildren<{ - chamber: "House" | "Senate" - committee: string - }> -> = ({ committee, chamber }) => ( - - Committee} - body={ - - {chamber} -
- {committee} -
- } - /> -
-) diff --git a/components/formatting.tsx b/components/formatting.tsx index 7e34daaca..9868e07bb 100644 --- a/components/formatting.tsx +++ b/components/formatting.tsx @@ -1,8 +1,3 @@ -import { Timestamp } from "firebase/firestore" -import { useMediaQuery } from "usehooks-ts" -import { Testimony } from "../functions/src/testimony/types" -import { Bill, BillContent } from "./db" - const billIdFormat = /^(?\D+)(?\d+)$/ /** Formats H123 as H.123 */ @@ -15,56 +10,5 @@ export const formatBillId = (id: string) => { } } -const MISSING_TIMESTAMP = Timestamp.fromMillis(0) -export const formatTimestamp = (t?: Timestamp) => { - if (!t || t.toMillis() == MISSING_TIMESTAMP.toMillis()) { - return undefined - } - return t.toDate().toLocaleDateString() -} - -export const FormattedBillTitle = ({ bill }: { bill: Bill | BillContent }) => { - const isMobile = useMediaQuery("(max-width: 768px)") - const billInfo = "content" in bill ? bill.content : bill - - const { BillNumber, Title } = billInfo - - return ( -
- {formatBillId(BillNumber)}:{" "} - {isMobile ? Title.substring(0, 45) + "..." : Title} -
- ) -} - -export const FormattedTestimonyTitle = ({ - testimony -}: { - testimony: Testimony -}) => { - const { authorDisplayName, publishedAt, position } = testimony - - return ( -
- - Author: {authorDisplayName || "anonymous"} - -

- - Published on: {publishedAt.toDate().toLocaleDateString()} - -

- - Position: {position} - -
- ) -} - -export const decodeHtmlCharCodes = (s: string) => - s.replace(/(&#(\d+);)/g, (match, capture, charCode) => - String.fromCharCode(charCode) - ) - export const truncateText = (s: string | undefined, maxLength: number) => !!s && s.length > maxLength ? s.substring(0, maxLength) + "..." : s diff --git a/components/search/bills/BillSearch.tsx b/components/search/bills/BillSearch.tsx index 80cefa3ec..faadd018e 100644 --- a/components/search/bills/BillSearch.tsx +++ b/components/search/bills/BillSearch.tsx @@ -7,23 +7,21 @@ import { SearchBox, useInstantSearch } from "react-instantsearch" -import { createInstantSearchRouterNext } from "react-instantsearch-router-nextjs" -import singletonRouter from "next/router" +import { currentGeneralCourt } from "functions/src/shared" import styled from "styled-components" import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter" -import { currentGeneralCourt } from "functions/src/shared" import { Col, Container, Row, Spinner } from "../../bootstrap" import { NoResults } from "../NoResults" import { ResultCount } from "../ResultCount" import { SearchContainer } from "../SearchContainer" import { SearchErrorBoundary } from "../SearchErrorBoundary" +import { useRouting } from "../useRouting" import { BillHit } from "./BillHit" import { useBillRefinements } from "./useBillRefinements" import { SortBy, SortByWithConfigurationItem } from "../SortBy" import { getServerConfig, VirtualFilters } from "../common" import { useBillSort } from "./useBillSort" import { FC, useState } from "react" -import { pathToSearchState, searchStateToUrl } from "../routingHelpers" const searchClient = new TypesenseInstantSearchAdapter({ server: getServerConfig(), @@ -72,16 +70,8 @@ export const BillSearch = () => { } }} searchClient={searchClient} - routing={{ - router: createInstantSearchRouterNext({ - singletonRouter, - routerOptions: { - cleanUrlOnDispose: false, - createURL: args => searchStateToUrl(args), - parseURL: args => pathToSearchState(args) - } - }) - }} + routing={useRouting()} + future={{ preserveSharedStateOnUnmount: true }} > diff --git a/components/search/routingHelpers.ts b/components/search/routingHelpers.ts deleted file mode 100644 index 42e139b30..000000000 --- a/components/search/routingHelpers.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { UiState } from "instantsearch.js" -import QueryString from "qs" - -export const searchStateToUrl = (createUrlArgs: { - location: Location - qsModule: typeof QueryString - routeState: UiState -}) => { - const { location, qsModule: qs, routeState: searchState } = createUrlArgs - const base = location.origin + location.pathname - - const flagQueries = Object.fromEntries( - Object.entries(qs.parse(window.location.search.slice(1))).filter( - ([key]) => !Object.keys(searchState).includes(key) - ) - ) - - const query = qs.stringify({ - ...searchState, - ...flagQueries - }) - - return query ? `${base}?${query}` : base -} - -export const pathToSearchState = (parseUrlArgs: { - location: Location - qsModule: typeof QueryString -}) => { - const { location, qsModule: qs } = parseUrlArgs - const path = location.href - - return ( - path.includes("?") ? qs.parse(path.substring(path.indexOf("?") + 1)) : {} - ) as UiState -} diff --git a/components/search/testimony/TestimonySearch.tsx b/components/search/testimony/TestimonySearch.tsx index f9fe19271..e77b42645 100644 --- a/components/search/testimony/TestimonySearch.tsx +++ b/components/search/testimony/TestimonySearch.tsx @@ -6,8 +6,6 @@ import { SearchBox, useInstantSearch } from "react-instantsearch" -import { createInstantSearchRouterNext } from "react-instantsearch-router-nextjs" -import singletonRouter from "next/router" import { StyledTabContent, StyledTabNav @@ -25,10 +23,10 @@ import { SearchContainer } from "../SearchContainer" import { SearchErrorBoundary } from "../SearchErrorBoundary" import { SortBy } from "../SortBy" import { getServerConfig, VirtualFilters } from "../common" +import { useRouting } from "../useRouting" import { TestimonyHit } from "./TestimonyHit" import { useTestimonyRefinements } from "./useTestimonyRefinements" import { FollowContext, OrgFollowStatus } from "components/shared/FollowContext" -import { pathToSearchState, searchStateToUrl } from "../routingHelpers" const searchClient = new TypesenseInstantSearchAdapter({ server: getServerConfig(), @@ -65,16 +63,8 @@ export const TestimonySearch = () => ( } }} searchClient={searchClient} - routing={{ - router: createInstantSearchRouterNext({ - singletonRouter, - routerOptions: { - cleanUrlOnDispose: false, - createURL: args => searchStateToUrl(args), - parseURL: args => pathToSearchState(args) - } - }) - }} + routing={useRouting()} + future={{ preserveSharedStateOnUnmount: true }} > diff --git a/components/search/useRouting.tsx b/components/search/useRouting.tsx new file mode 100644 index 000000000..e4ef3b9fc --- /dev/null +++ b/components/search/useRouting.tsx @@ -0,0 +1,55 @@ +import { UiState } from "instantsearch.js" +import { RouterProps } from "instantsearch.js/es/middlewares" +import Router from "next/router" +import qs from "qs" +import { useMemo } from "react" + +const pathToSearchState = (path: string) => + (path.includes("?") + ? qs.parse(path.substring(path.indexOf("?") + 1)) + : {}) as UiState + +const searchStateToUrl = (searchState: UiState) => { + const base = window.location.pathname + + const flagQueries = Object.fromEntries( + Object.entries(qs.parse(window.location.search.slice(1))).filter( + ([key]) => !Object.keys(searchState).includes(key) + ) + ) + + const query = qs.stringify({ + ...searchState, + ...flagQueries + }) + + return query ? `${base}?${query}` : base +} + +export function useRouting(): RouterProps { + return useMemo(() => { + let disposed = false + return { + router: { + createURL: searchStateToUrl, + dispose() { + disposed = true + // Clear back listener + Router.beforePopState(() => true) + }, + onUpdate(callback) { + Router.beforePopState(({ url }) => { + callback(pathToSearchState(url)) + return true + }) + }, + read: () => pathToSearchState(window.location.href), + write(route) { + if (disposed) return + const url = searchStateToUrl(route) + Router.push(url, url, { shallow: true }) + } + } + } + }, []) +} diff --git a/firestore.rules b/firestore.rules index 9ce36c3d7..e33d279e2 100644 --- a/firestore.rules +++ b/firestore.rules @@ -99,6 +99,12 @@ service cloud.firestore { allow read: if true allow write: if false } + + // public, read-only + match /paragraphs/{pid} { + allow read: if true + allow write: if false + } } } } \ No newline at end of file diff --git a/package.json b/package.json index 69d09debe..1d006efd6 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,6 @@ "react-i18next": "^13.2.2", "react-inlinesvg": "^3.0.1", "react-instantsearch": "^7.12.4", - "react-instantsearch-router-nextjs": "^7.15.5", "react-is": "^18.2.0", "react-markdown": "^8.0.4", "react-overlays": "^5.1.1", diff --git a/scripts/stories.yml b/scripts/stories.yml index 33c5c25a6..3db5579a0 100644 --- a/scripts/stories.yml +++ b/scripts/stories.yml @@ -156,11 +156,6 @@ folder: billDetail grouping: Bill Detail figmaUrl: https://www.figma.com/file/3ifz37EOwDfmnEG8320KlD/CS1---MAPLE?node-id=180%3A7916 --- -name: CurrentCommitteeCard -folder: billDetail -grouping: Bill Detail -figmaUrl: https://www.figma.com/file/3ifz37EOwDfmnEG8320KlD/CS1---MAPLE?node-id=159%3A4939 ---- name: BillStatus folder: billDetail grouping: Bill Detail diff --git a/stories/organisms/billDetail/CurrentCommitteeCard.stories.tsx b/stories/organisms/billDetail/CurrentCommitteeCard.stories.tsx deleted file mode 100644 index 479913200..000000000 --- a/stories/organisms/billDetail/CurrentCommitteeCard.stories.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { Meta, StoryObj } from "@storybook/react" -import { CurrentCommitteeCard } from "components/CurrentCommitteeCard/CurrentCommitteeCard" - -const meta: Meta = { - title: "Organisms/Bill Detail/CurrentCommitteeCard", - component: CurrentCommitteeCard -} - -export default meta - -type Story = StoryObj -export const Primary: Story = { - args: { chamber: "House", committee: "Committee of Ways and Means" }, - name: "CurrentCommitteeCard" -} diff --git a/yarn.lock b/yarn.lock index c3169cf13..ceef2062b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5291,13 +5291,6 @@ algoliasearch-helper@3.22.5: dependencies: "@algolia/events" "^4.0.1" -algoliasearch-helper@3.24.3: - version "3.24.3" - resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.24.3.tgz#9a358c3110bcd912e79ef606a6e7bdd7725d22ee" - integrity sha512-3QKg5lzSfUiPN8Hn1ViHEGv6PjK7i4SFEDLzwlSzPO/4mVOsyos7B7/AsEtFQW5KHHPiCq6DyJl+mzg7CYlEgw== - dependencies: - "@algolia/events" "^4.0.1" - all-contributors-cli@^6.20.5: version "6.26.1" resolved "https://registry.yarnpkg.com/all-contributors-cli/-/all-contributors-cli-6.26.1.tgz#9f3358c9b9d0a7e66c8f84ffebf5a6432a859cae" @@ -10408,13 +10401,6 @@ install-artifact-from-github@^1.3.5: resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.5.tgz#88c96fe40e5eb21d45586d564208c648a1dbf38d" integrity sha512-gZHC7f/cJgXz7MXlHFBxPVMsvIbev1OQN1uKQYKVJDydGNm9oYf9JstbU4Atnh/eSvk41WtEovoRm+8IF686xg== -instantsearch-ui-components@0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/instantsearch-ui-components/-/instantsearch-ui-components-0.11.1.tgz#664ca03f657079946e459af72fa8d2674799c466" - integrity sha512-ZqUbJYYgObQ47J08ftXV1KNC1vdEoiD4/49qrkCdW46kRzLxLgYXJGuEuk48DQwK4aBtIoccgTyfbMGfcqNjxg== - dependencies: - "@babel/runtime" "^7.1.2" - instantsearch-ui-components@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/instantsearch-ui-components/-/instantsearch-ui-components-0.9.0.tgz#f7ae71fe623d18eff32b73071749f31826cb7b89" @@ -10445,24 +10431,6 @@ instantsearch.js@4.74.2: qs "^6.5.1 < 6.10" search-insights "^2.15.0" -instantsearch.js@4.78.1: - version "4.78.1" - resolved "https://registry.yarnpkg.com/instantsearch.js/-/instantsearch.js-4.78.1.tgz#cee799b920ba08c7c4e5af5ba591b86a1d80af1d" - integrity sha512-nDTWQ6DUxYzBZfkSxb/QJsYMZPPU8SGlGurn9147ABvA5Eumtxmk3Qy55EBMl0VxKVltGy3axAYMRB/gKIIHkg== - dependencies: - "@algolia/events" "^4.0.1" - "@types/dom-speech-recognition" "^0.0.1" - "@types/google.maps" "^3.55.12" - "@types/hogan.js" "^3.0.0" - "@types/qs" "^6.5.3" - algoliasearch-helper "3.24.3" - hogan.js "^3.0.2" - htm "^3.0.0" - instantsearch-ui-components "0.11.1" - preact "^10.10.0" - qs "^6.5.1 < 6.10" - search-insights "^2.17.2" - instantsearch.js@^4.43.0: version "4.62.0" resolved "https://registry.yarnpkg.com/instantsearch.js/-/instantsearch.js-4.62.0.tgz#68577f4f04866728f22441cbc7464c544678d342" @@ -14837,24 +14805,6 @@ react-instantsearch-core@7.13.2: instantsearch.js "4.74.2" use-sync-external-store "^1.0.0" -react-instantsearch-core@7.15.5: - version "7.15.5" - resolved "https://registry.yarnpkg.com/react-instantsearch-core/-/react-instantsearch-core-7.15.5.tgz#65d1edc440de8dc73d55230d13af8cbcf1724221" - integrity sha512-SFxiwwMf0f5F/8U0Y4ullvQ7bZtbYE516UOJbxaHhjV8yY0i8c22K4lrBFrYbxVRT7QAcp2wLGHiB7r/lD7eRA== - dependencies: - "@babel/runtime" "^7.1.2" - algoliasearch-helper "3.24.3" - instantsearch.js "4.78.1" - use-sync-external-store "^1.0.0" - -react-instantsearch-router-nextjs@^7.15.5: - version "7.15.5" - resolved "https://registry.yarnpkg.com/react-instantsearch-router-nextjs/-/react-instantsearch-router-nextjs-7.15.5.tgz#a8b13bc5ad9bd8c5a689d48f2714eab6bed2514f" - integrity sha512-kn325Nl6QkZlkSuOXwKUOb56QkSsKes9XQdBLythKt2oZzzAfcaXSYzYsFEyj96cMYDLkRHhHvLhdyq4C8Xezg== - dependencies: - instantsearch.js "4.78.1" - react-instantsearch-core "7.15.5" - react-instantsearch@^7.12.4: version "7.13.2" resolved "https://registry.yarnpkg.com/react-instantsearch/-/react-instantsearch-7.13.2.tgz#db84d04bd399596fb0078625bc75a6abc65e4bc6" @@ -15657,11 +15607,6 @@ search-insights@^2.15.0: resolved "https://registry.yarnpkg.com/search-insights/-/search-insights-2.17.2.tgz#d13b2cabd44e15ade8f85f1c3b65c8c02138629a" integrity sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g== -search-insights@^2.17.2: - version "2.17.3" - resolved "https://registry.yarnpkg.com/search-insights/-/search-insights-2.17.3.tgz#8faea5d20507bf348caba0724e5386862847b661" - integrity sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ== - search-insights@^2.6.0: version "2.11.0" resolved "https://registry.yarnpkg.com/search-insights/-/search-insights-2.11.0.tgz#0512ae3b801fed5ff3a2ae82840bf20ba29d82e5"