Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix follow button on browse testimony page #1632

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
129 changes: 67 additions & 62 deletions components/bill/BillDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { useTranslation } from "next-i18next"
import { isCurrentCourt } from "functions/src/shared"
import { FollowBillButton } from "components/shared/FollowButton"
import { PendingUpgradeBanner } from "components/PendingUpgradeBanner"
import { FollowContext, OrgFollowStatus } from "components/shared/FollowContext"

const StyledContainer = styled(Container)`
font-family: "Nunito";
Expand All @@ -47,79 +48,83 @@ export const BillDetails = ({ bill }: BillProps) => {
const isPendingUpgrade = useAuth().claims?.role === "pendingUpgrade"
const flags = useFlags()

const [followStatus, setFollowStatus] = useState<OrgFollowStatus>({})

return (
<>
{isPendingUpgrade && <PendingUpgradeBanner />}
{!isCurrentCourt(bill.court) && (
<Banner>
this bill is from session {bill.court} - not the current session
</Banner>
)}
<FollowContext.Provider value={{ followStatus, setFollowStatus }}>
{isPendingUpgrade && <PendingUpgradeBanner />}
{!isCurrentCourt(bill.court) && (
<Banner>
this bill is from session {bill.court} - not the current session
</Banner>
)}

<StyledContainer className="mt-3 mb-3">
<Row>
<Col>
<Back href="/bills">{t("back_to_bills")}</Back>
</Col>
</Row>
{bill.history.length > 0 ? (
<>
<Row className="align-items-end justify-content-start">
<Col md={2}>
<StyledContainer className="mt-3 mb-3">
<Row>
<Col>
<Back href="/bills">{t("back_to_bills")}</Back>
</Col>
</Row>
{bill.history.length > 0 ? (
<>
<Row className="align-items-end justify-content-start">
<Col md={2}>
<BillNumber bill={bill} />
</Col>
<Col
xs={10}
md={6}
className="mb-3 ms-auto d-flex justify-content-end"
>
<Status bill={bill} />
</Col>
</Row>
<Row className="mb-4">
<Col xs={12} className="d-flex justify-content-end">
{flags.notifications && <FollowBillButton bill={bill} />}
</Col>
</Row>
</>
) : (
<Row>
<Col>
<BillNumber bill={bill} />
</Col>
<Col
xs={10}
md={6}
className="mb-3 ms-auto d-flex justify-content-end"
>
<Status bill={bill} />
<Col xs={6} className="d-flex justify-content-end">
<Styled>
{flags.notifications && <FollowBillButton bill={bill} />}
</Styled>
</Col>
</Row>
<Row className="mb-4">
<Col xs={12} className="d-flex justify-content-end">
{flags.notifications && <FollowBillButton bill={bill} />}
</Col>
</Row>
</>
) : (
<Row>
)}
<Row className="mt-2">
<Col>
<BillNumber bill={bill} />
<Summary bill={bill} />
</Col>
</Row>
<Row>
<Col md={8}>
<Sponsors bill={bill} className="mt-4 pb-1" />
<BillTestimonies bill={bill} className="mt-4" />
{flags.lobbyingTable && (
<LobbyingTable bill={bill} className="mt-4 pb-1" />
)}
</Col>
<Col xs={6} className="d-flex justify-content-end">
<Styled>
{flags.notifications && <FollowBillButton bill={bill} />}
</Styled>
<Col md={4}>
<Committees bill={bill} className="mt-4 pb-1" />
<Hearing
bill={bill}
className="bg-secondary d-flex justify-content-center mt-4 pb-1 text-light"
/>
<TestimonyFormPanel bill={bill} />
{flags.billTracker && (
<BillTrackerConnectedView bill={bill} className="mt-4" />
)}
</Col>
</Row>
)}
<Row className="mt-2">
<Col>
<Summary bill={bill} />
</Col>
</Row>
<Row>
<Col md={8}>
<Sponsors bill={bill} className="mt-4 pb-1" />
<BillTestimonies bill={bill} className="mt-4" />
{flags.lobbyingTable && (
<LobbyingTable bill={bill} className="mt-4 pb-1" />
)}
</Col>
<Col md={4}>
<Committees bill={bill} className="mt-4 pb-1" />
<Hearing
bill={bill}
className="bg-secondary d-flex justify-content-center mt-4 pb-1 text-light"
/>
<TestimonyFormPanel bill={bill} />
{flags.billTracker && (
<BillTrackerConnectedView bill={bill} className="mt-4" />
)}
</Col>
</Row>
</StyledContainer>
</StyledContainer>
</FollowContext.Provider>
</>
)
}
98 changes: 53 additions & 45 deletions components/search/testimony/TestimonySearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { getServerConfig } from "../common"
import { useRouting } from "../useRouting"
import { TestimonyHit } from "./TestimonyHit"
import { useTestimonyRefinements } from "./useTestimonyRefinements"
import { FollowContext, OrgFollowStatus } from "components/shared/FollowContext"

const searchClient = new TypesenseInstantSearchAdapter({
server: getServerConfig(),
Expand Down Expand Up @@ -118,54 +119,61 @@ const Layout = () => {
})
}

const [followStatus, setFollowStatus] = useState<OrgFollowStatus>({})

return (
<>
<TabContainer activeKey={key} onSelect={(k: any) => setKey(k)}>
<StyledTabNav>
{tabs.map((t, i) => (
<Nav.Item key={t}>
<Nav.Link
eventKey={t}
className={`rounded-top m-0 p-0`}
onClick={e => onTabClick(t)}
>
<p className={`my-0 ${i == 0 ? "" : "mx-4"}`}>{t}</p>
<hr className={`my-0`} />
</Nav.Link>
</Nav.Item>
))}
</StyledTabNav>
<StyledTabContent></StyledTabContent>
</TabContainer>
<SearchContainer>
<Row>
<SearchBox placeholder="Search For Testimony" className="mt-2 mb-3" />
</Row>
<Row>
{refinements.options}
<Col className="d-flex flex-column">
<RefinementRow>
<ResultCount className="flex-grow-1 m-1" />
<SortBy items={items} />
{refinements.show}
</RefinementRow>
<CurrentRefinements
className="mt-2 mb-2"
excludedAttributes={["authorRole"]}
<FollowContext.Provider value={{ followStatus, setFollowStatus }}>
<TabContainer activeKey={key} onSelect={(k: any) => setKey(k)}>
<StyledTabNav>
{tabs.map((t, i) => (
<Nav.Item key={t}>
<Nav.Link
eventKey={t}
className={`rounded-top m-0 p-0`}
onClick={e => onTabClick(t)}
>
<p className={`my-0 ${i == 0 ? "" : "mx-4"}`}>{t}</p>
<hr className={`my-0`} />
</Nav.Link>
</Nav.Item>
))}
</StyledTabNav>
<StyledTabContent></StyledTabContent>
</TabContainer>
<SearchContainer>
<Row>
<SearchBox
placeholder="Search For Testimony"
className="mt-2 mb-3"
/>
{status === "empty" ? (
<NoResults>
Your search has yielded zero results!
<br />
<b>Try another search term</b>
</NoResults>
) : (
<Hits hitComponent={TestimonyHit} />
)}
<Pagination className="mx-auto mt-2 mb-3" />
</Col>
</Row>
</SearchContainer>
</Row>
<Row>
{refinements.options}
<Col className="d-flex flex-column">
<RefinementRow>
<ResultCount className="flex-grow-1 m-1" />
<SortBy items={items} />
{refinements.show}
</RefinementRow>
<CurrentRefinements
className="mt-2 mb-2"
excludedAttributes={["authorRole"]}
/>
{status === "empty" ? (
<NoResults>
Your search has yielded zero results!
<br />
<b>Try another search term</b>
</NoResults>
) : (
<Hits hitComponent={TestimonyHit} />
)}
<Pagination className="mx-auto mt-2 mb-3" />
</Col>
</Row>
</SearchContainer>
</FollowContext.Provider>
</>
)
}
22 changes: 14 additions & 8 deletions components/shared/FollowButton.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { StyledImage } from "components/ProfilePage/StyledProfileComponents"
import { useTranslation } from "next-i18next"
import { useEffect, useState } from "react"
import { useEffect, useContext } from "react"
import { Button } from "react-bootstrap"
import { useAuth } from "../auth"
import { Bill } from "../db"
import { TopicQuery, setFollow, setUnfollow } from "./FollowingQueries"
import { FollowContext } from "./FollowContext"

export const BaseFollowButton = ({
topicName,
Expand All @@ -22,30 +23,35 @@ export const BaseFollowButton = ({
const { user } = useAuth()
const uid = user?.uid

const [queryResult, setQueryResult] = useState("")
const { followStatus, setFollowStatus } = useContext(FollowContext)

useEffect(() => {
uid
? TopicQuery(uid, topicName).then(result => setQueryResult(result))
? TopicQuery(uid, topicName).then(result => {
setFollowStatus(prevOrgFollowGroup => {
return { ...prevOrgFollowGroup, [topicName]: Boolean(result) }
})
})
: null
}, [uid, topicName, setQueryResult])
}, [uid, topicName, setFollowStatus])

const FollowClick = async () => {
await followAction()
setQueryResult(topicName)
setFollowStatus({ ...followStatus, [topicName]: true })
}

const UnfollowClick = async () => {
await unfollowAction()
setQueryResult("")
setFollowStatus({ ...followStatus, [topicName]: false })
}

const isFollowing = queryResult
const isFollowing = followStatus[topicName]
const text = isFollowing ? t("button.following") : t("button.follow")
const checkmark = isFollowing ? (
<StyledImage src="/check-white.svg" alt="checkmark" />
) : null
const handleClick = () => {
const handleClick = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
isFollowing ? UnfollowClick() : FollowClick()
}

Expand Down
13 changes: 13 additions & 0 deletions components/shared/FollowContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createContext, Dispatch, SetStateAction } from "react"

export type OrgFollowStatus = Record<string, boolean>

interface FollowContextType {
followStatus: OrgFollowStatus
setFollowStatus: Dispatch<SetStateAction<OrgFollowStatus>>
}

export const FollowContext = createContext<FollowContextType>({
followStatus: {},
setFollowStatus: () => {}
})