diff --git a/docs/about/faq.mdx b/docs/about/faq.mdx index e28e6a81..1c307293 100644 --- a/docs/about/faq.mdx +++ b/docs/about/faq.mdx @@ -5,39 +5,51 @@ slug: /faq sidebar_position: 3 --- -## Overview +import { FaqAccordion } from "/src/components/FaqAccordion"; -### Where can I get additional help, ask questions, or bond with the Meshtastic community? - -This site (which has a great search function) is the preferred place for up-to-date documentation. Many of our users and developers hang out on the [Meshtastic Discord](https://discord.gg/ktMAKGBnBs) server where you may connect with like-minded people. - -### How can I contribute to Meshtastic? - -Everyone contributes in a different way. Join the [Meshtastic Discord](https://discord.gg/ktMAKGBnBs) and introduce yourself. We're all very friendly. If you'd like to pitch in some code, check out the [Development](/docs/developers) menu on the left. - - - -## Android Client - -### What versions of Android does the Meshtastic Android App require? - -Minimum requirement is Android 5 (Lollipop 2014, first BLE support), however at least Android 6 (Marshmallow 2015) is recommended as Bluetooth is more stable. While Android 5/6 are officially supported by Meshtastic, it is _not_ recommended that you purchase devices with these versions due to their limited OS support and limited battery life due to age. Many newer models exist that are very affordable. A good resource to use when researching affordable devices is the [LineageOS Supported Devices List](https://wiki.lineageos.org/devices/). - -### What does the icon next to the message mean? +export const GeneralFaq = [ + { + title: "Where can I get additional help, ask questions, or bond with the Meshtastic community?", + content: `This site (which has a great search function) is the preferred place for up-to-date documentation. Many of our users and developers hang out on the [Meshtastic Discord](https://discord.gg/ktMAKGBnBs) server where you may connect with like-minded people.`, + }, + { + title: "How can I contribute to Meshtastic?", + content: "Everyone contributes in a different way. Join the [Meshtastic Discord](https://discord.gg/ktMAKGBnBs) and introduce yourself. We're all very friendly. If you'd like to pitch in some code, check out the [Development](/docs/developers) menu on the left.", + }, +]; +export const AndroidFaq = [ + { + title: "What versions of Android does the Meshtastic Android App require?", + content: `Minimum requirement is Android 5 (Lollipop 2014, first BLE support), however at least Android 6 (Marshmallow 2015) is recommended as Bluetooth is more stable. While Android 5/6 are officially supported by Meshtastic, it is _not_ recommended that you purchase devices with these versions due to their limited OS support and limited battery life due to age. Many newer models exist that are very affordable. A good resource to use when researching affordable devices is the [LineageOS Supported Devices List](https://wiki.lineageos.org/devices/).`, + }, + { + title: "What does the icon next to the message mean?", + content: ` - Cloud with an up arrow - Queued on the app to be sent to your device. - Cloud only - Queued on the device to be sent over the mesh. - Cloud with a check mark - At least one other node on the mesh acknowledged the message. - Person with a check mark - The intended recipient of your direct message acknowledged the message. -- Cloud crossed out - Not acknowledged or message error. +- Cloud crossed out - Not acknowledged or message error.`, + }, + { + title: "How can I clear the message history?", + content: `Long press any message to select and show the menu with "delete" and "select all" buttons.`, + }, + { + title: "After a fresh firmware install, my node is not connecting via Bluetooth. What should I do?", + content: `Try forgetting the Bluetooth connection from the Android Bluetooth Settings menu. Re-pair and try again. This is a security measure and there is no workaround for it. It prevents apps and other accessories from spoofing an existing accessory by un-pairing and "re-pairing" themselves without the users' knowledge.`, + }, +]; -### How can I clear the message history? +## Overview -Long press any message to select and show the menu with "delete" and "select all" buttons. + -### After a fresh firmware install, my node is not connecting via Bluetooth. What should I do? + -Try forgetting the Bluetooth connection from the Android Bluetooth Settings menu. Re-pair and try again. This is a security measure and there is no workaround for it. It prevents apps and other accessories from spoofing an existing accessory by un-pairing and "re-pairing" themselves without the users' knowledge. +## Android Client + diff --git a/package.json b/package.json index 67ac5d29..9c6cd02d 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,10 @@ "dotenv": "^16.3.1", "postcss": "^8.4.33", "react": "^18.2.0", + "react-accessible-accordion": "^5.0.0", "react-dom": "^18.2.0", "react-icons": "^4.12.0", + "react-markdown": "^9.0.1", "remark-deflist": "^1.0.0", "swr": "^2.2.4", "tailwindcss": "^3.4.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56fe66f7..756fa2f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,12 +50,18 @@ dependencies: react: specifier: ^18.2.0 version: 18.2.0 + react-accessible-accordion: + specifier: ^5.0.0 + version: 5.0.0(react-dom@18.2.0)(react@18.2.0) react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) react-icons: specifier: ^4.12.0 version: 4.12.0(react@18.2.0) + react-markdown: + specifier: ^9.0.1 + version: 9.0.1(@types/react@18.2.47)(react@18.2.0) remark-deflist: specifier: ^1.0.0 version: 1.0.0 @@ -5793,6 +5799,10 @@ packages: engines: {node: '>=8'} dev: false + /html-url-attributes@3.0.0: + resolution: {integrity: sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==} + dev: false + /html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} dev: false @@ -8413,6 +8423,16 @@ packages: strip-json-comments: 2.0.1 dev: false + /react-accessible-accordion@5.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-MT2obYpTgLIIfPr9d7hEyvPB5rg8uJcHpgA83JSRlEUHvzH48+8HJPvzSs+nM+XprTugDgLfhozO5qyJpBvYRQ==} + peerDependencies: + react: ^16.3.2 || ^17.0.0 || ^18.0.0 + react-dom: ^16.3.3 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /react-dev-utils@12.0.1(typescript@5.3.3)(webpack@5.89.0): resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==} engines: {node: '>=14'} @@ -8529,6 +8549,28 @@ packages: webpack: 5.89.0 dev: false + /react-markdown@9.0.1(@types/react@18.2.47)(react@18.2.0): + resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + dependencies: + '@types/hast': 3.0.3 + '@types/react': 18.2.47 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.0 + html-url-attributes: 3.0.0 + mdast-util-to-hast: 13.0.2 + react: 18.2.0 + remark-parse: 11.0.0 + remark-rehype: 11.0.0 + unified: 11.0.4 + unist-util-visit: 5.0.0 + vfile: 6.0.1 + transitivePeerDependencies: + - supports-color + dev: false + /react-router-config@5.1.1(react-router@5.3.4)(react@18.2.0): resolution: {integrity: sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==} peerDependencies: diff --git a/src/components/FaqAccordion.tsx b/src/components/FaqAccordion.tsx new file mode 100644 index 00000000..bc4f360e --- /dev/null +++ b/src/components/FaqAccordion.tsx @@ -0,0 +1,99 @@ +import React from "react"; +import ReactMarkdown from "react-markdown"; +import { + Accordion, + AccordionItem, + AccordionItemHeading, + AccordionItemButton, + AccordionItemPanel, +} from "react-accessible-accordion"; +import "react-accessible-accordion/dist/fancy-example.css"; + +export interface Faq { + title: string; + content: string; +} + +/** + * Gets the query parameter `openFaqItems` which is an array of + * faq items that should be pre-opened + * @type {Function} + */ +const getOpenFaqItemsFromUrl = (slug: String): string[] => { + // Use URLSearchParams to parse the query parameters from the current URL + const searchParams = new URLSearchParams(window.location.search); + + // Get the 'openFaqItems' parameter as a comma-separated string + const openFaqItemsString = searchParams.get(`openFaqItems-${slug}`); + + // If the parameter exists, split it by commas into an array; otherwise, return an empty array + return openFaqItemsString ? openFaqItemsString.split(',') : []; +}; + +export const FaqAccordion = ({ + rows, + slug +}: { rows: Faq[], slug: String }): JSX.Element => { + // Set the faq structured data + const faqStructuredData = { + "@context": "https://schema.org", + "@type": "FAQPage", + mainEntity: rows.map((row) => ({ + "@type": "Question", + name: row.title, + acceptedAnswer: { + "@type": "Answer", + text: row.content, + }, + })), + }; + + // Use the getOpenFaqItemsFromUrl function to set the + // initial state of preExpanded based on URL parameters + const [preExpanded, setPreExpanded] = React.useState(getOpenFaqItemsFromUrl(slug)); + + /** + * Updates query parameters in the url when items are opened + * so that a link can be shared with the faq item already opened + */ + const handleChange = (openFaqItems: (string | number)[]): void => { + // Get current url params + const searchParams = new URLSearchParams(window.location.search); + + // Convert openFaqItems to a comma-separated string and update/add the parameter + searchParams.set(`openFaqItems-${slug}`, openFaqItems.map(String).join(',')); + + // Construct the new URL, preserve existing parameters + const newUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}?${searchParams.toString()}`; + + // Use history.pushState to change the URL without reloading the page + window.history.pushState({ path: newUrl }, '', newUrl); + }; + + return ( + <> +