pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 207 lines 6.0 kB view raw
1import classNames from "classnames"; 2import { useRef, useState } from "react"; 3import { Trans, useTranslation } from "react-i18next"; 4import { useNavigate } from "react-router-dom"; 5 6import { SearchBarInput } from "@/components/form/SearchBar"; 7import { ThinContainer } from "@/components/layout/ThinContainer"; 8import { MwLink } from "@/components/text/Link"; 9import { Ol } from "@/components/utils/Ol"; 10import { Heading1, Heading2, Paragraph } from "@/components/utils/Text"; 11import { PageTitle } from "@/pages/parts/util/PageTitle"; 12 13import { SubPageLayout } from "./layouts/SubPageLayout"; 14 15function Question(props: { title: string; children: React.ReactNode }) { 16 return ( 17 <> 18 <p className="text-white mb-2 font-medium">{props.title}</p> 19 <div className="text-type-text">{props.children}</div> 20 </> 21 ); 22} 23 24export function Button(props: { 25 className: string; 26 onClick?: () => void; 27 children: React.ReactNode; 28 disabled?: boolean; 29}) { 30 return ( 31 <button 32 className={classNames( 33 "font-bold rounded h-10 w-40 scale-90 hover:scale-95 transition-all duration-200", 34 props.className, 35 )} 36 type="button" 37 onClick={props.onClick} 38 disabled={props.disabled} 39 > 40 {props.children} 41 </button> 42 ); 43} 44 45function SectionHeading(props: { title: string }) { 46 return ( 47 <h3 className="text-white font-medium text-lg my-8 pt-4">{props.title}</h3> 48 ); 49} 50 51type SectionKey = 52 | "general" 53 | "search" 54 | "playback" 55 | "connections" 56 | "language"; 57 58interface Sections { 59 general: JSX.Element[]; 60 search: JSX.Element[]; 61 playback: JSX.Element[]; 62 connections: JSX.Element[]; 63 language: JSX.Element[]; 64} 65 66export function AboutPage() { 67 const { t } = useTranslation(); 68 const navigate = useNavigate(); 69 const [searchQuery, setSearchQuery] = useState(""); 70 const searchRef = useRef<HTMLInputElement>(null); 71 72 const questionKeys = Object.keys(t("about", { returnObjects: true })) 73 .filter((key) => key.startsWith("q")) 74 .sort((a, b) => parseInt(a.slice(1), 10) - parseInt(b.slice(1), 10)); 75 76 const sections: Sections = { 77 general: [], 78 search: [], 79 playback: [], 80 connections: [], 81 language: [], 82 }; 83 84 questionKeys.forEach((key) => { 85 const section = t(`about.${key}.section`) as SectionKey; 86 if (section && sections[section]) { 87 sections[section].push( 88 <Question title={t(`about.${key}.title`)}> 89 {t(`about.${key}.body`)} 90 </Question>, 91 ); 92 } 93 }); 94 95 const allFaqItems = [ 96 ...sections.general, 97 ...sections.search, 98 ...sections.playback, 99 ...sections.connections, 100 ...sections.language, 101 ]; 102 103 const filteredItems = allFaqItems.filter((item: JSX.Element) => { 104 try { 105 const title = item?.props?.title?.toLowerCase() || ""; 106 const body = item?.props?.children?.toLowerCase() || ""; 107 const query = searchQuery.toLowerCase(); 108 return title.includes(query) || body.includes(query); 109 } catch (e) { 110 return false; 111 } 112 }); 113 114 const showFilteredItems = searchQuery.length > 0; 115 116 return ( 117 <SubPageLayout> 118 <PageTitle subpage k="global.pages.about" /> 119 <ThinContainer> 120 <Heading1>{t("about.title")}</Heading1> 121 <Paragraph>{t("about.description")}</Paragraph> 122 123 <div> 124 <SearchBarInput 125 ref={searchRef} 126 value={searchQuery} 127 onChange={(value) => setSearchQuery(value)} 128 onUnFocus={() => {}} 129 placeholder={t("about.searchPlaceholder")} 130 hideTooltip 131 /> 132 </div> 133 <div className="pt-4"> 134 <Trans i18nKey="about.help"> 135 <MwLink url="/support" /> 136 </Trans> 137 </div> 138 139 <Heading2 className="mt-10">{t("about.faqTitle")}</Heading2> 140 141 {showFilteredItems ? ( 142 <Ol items={filteredItems} /> 143 ) : ( 144 <> 145 {/* General Section */} 146 {sections.general.length > 0 && ( 147 <> 148 <SectionHeading title={t("about.sections.general")} /> 149 <Ol items={sections.general} /> 150 </> 151 )} 152 153 {/* Search Section */} 154 {sections.search.length > 0 && ( 155 <> 156 <SectionHeading title={t("about.sections.search")} /> 157 <Ol items={sections.search} /> 158 </> 159 )} 160 161 {/* Playback Section */} 162 {sections.playback.length > 0 && ( 163 <> 164 <SectionHeading title={t("about.sections.playback")} /> 165 <Ol items={sections.playback} /> 166 </> 167 )} 168 169 {/* FED API Section */} 170 {sections.connections.length > 0 && ( 171 <> 172 <SectionHeading title={t("about.sections.connections")} /> 173 <Ol items={sections.connections} /> 174 </> 175 )} 176 177 {/* Language Section */} 178 {sections.language.length > 0 && ( 179 <> 180 <SectionHeading title={t("about.sections.language")} /> 181 <Ol items={sections.language} /> 182 </> 183 )} 184 </> 185 )} 186 187 <div 188 style={{ display: "flex", justifyContent: "space-between" }} 189 className="pt-2 w-full" 190 > 191 <Button 192 className="py-px mt-8 box-content bg-buttons-secondary hover:bg-buttons-secondaryHover bg-opacity-90 text-buttons-secondaryText justify-center items-center" 193 onClick={() => navigate("/discover")} 194 > 195 Discover 196 </Button> 197 <Button 198 className="py-px mt-8 box-content bg-buttons-secondary hover:bg-buttons-secondaryHover bg-opacity-90 text-buttons-secondaryText justify-center items-center" 199 onClick={() => navigate("/support")} 200 > 201 Support 202 </Button> 203 </div> 204 </ThinContainer> 205 </SubPageLayout> 206 ); 207}