feat: integrate FAQ to webapp

This commit is contained in:
Jixun Wu 2023-09-05 01:34:42 +01:00
parent b6f3a761fc
commit b120f740d8
18 changed files with 380 additions and 50 deletions

View File

@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { MdSettings, MdHome } from 'react-icons/md';
import { MdSettings, MdHome, MdQuestionAnswer } from 'react-icons/md';
import { ChakraProvider, Tabs, TabList, TabPanels, Tab, TabPanel, Icon, chakra } from '@chakra-ui/react';
import { MainTab } from '~/tabs/MainTab';
@ -10,6 +10,7 @@ import { theme } from '~/theme';
import { persistSettings } from '~/features/settings/persistSettings';
import { setupStore } from '~/store';
import { Footer } from '~/components/Footer';
import { FaqTab } from '~/tabs/FaqTab';
// Private to this file only.
const store = setupStore();
@ -30,6 +31,10 @@ export function AppRoot() {
<Icon as={MdSettings} mr="1" />
<chakra.span></chakra.span>
</Tab>
<Tab>
<Icon as={MdQuestionAnswer} mr="1" />
<chakra.span></chakra.span>
</Tab>
</TabList>
<TabPanels overflow="auto" minW={0} flexDir="column" flex={1} display="flex">
@ -39,6 +44,9 @@ export function AppRoot() {
<TabPanel flex={1} display="flex">
<SettingsTab />
</TabPanel>
<TabPanel>
<FaqTab />
</TabPanel>
</TabPanels>
</Tabs>

View File

@ -0,0 +1,26 @@
import { Heading } from '@chakra-ui/react';
import React from 'react';
export interface Header3Props {
children: React.ReactNode;
id?: string;
className?: string;
}
export function Header3({ children, className, id }: Header3Props) {
return (
<Heading
as="h3"
id={id}
className={className}
pt={3}
pb={1}
borderBottom={'1px solid'}
borderColor="gray.300"
color="gray.800"
size="lg"
>
{children}
</Heading>
);
}

View File

@ -0,0 +1,16 @@
import { Heading } from '@chakra-ui/react';
import React from 'react';
export interface Header4Props {
children: React.ReactNode;
id?: string;
className?: string;
}
export function Header4({ children, className, id }: Header4Props) {
return (
<Heading as="h4" id={id} className={className} pt={3} pb={1} color="gray.700" size="md">
{children}
</Heading>
);
}

View File

@ -0,0 +1,9 @@
import { Mark } from '@chakra-ui/react';
export function HiWord({ children }: { children: React.ReactNode }) {
return (
<Mark bg="orange.100" borderRadius={5} px={2} mx={1}>
{children}
</Mark>
);
}

View File

@ -0,0 +1,13 @@
import { chakra, css } from '@chakra-ui/react';
const cssUnselectable = css({ pointerEvents: 'none', userSelect: 'none' });
export function VQuote({ children }: { children: React.ReactNode }) {
return (
<>
<chakra.span css={cssUnselectable}></chakra.span>
{children}
<chakra.span css={cssUnselectable}></chakra.span>
</>
);
}

View File

@ -0,0 +1,17 @@
export interface ProjectIssueProps {
id: number | string;
title?: string;
}
export function ProjectIssue({ id, title }: ProjectIssueProps) {
return (
<a
rel="noopener noreferrer nofollow"
target="_blank"
href={`https://git.unlock-music.dev/um/um-react/issues/${id}`}
>
{`#${id}`}
{title && ` - ${title}`}
</a>
);
}

50
src/faq/KuwoFAQ.tsx Normal file
View File

@ -0,0 +1,50 @@
import { Alert, AlertIcon, Container, List, ListItem, Text, VStack, chakra } from '@chakra-ui/react';
import { Header4 } from '~/components/HelpText/Header4';
import { VQuote } from '~/components/HelpText/VQuote';
import { SegmentTryOfficialPlayer } from './SegmentTryOfficialPlayer';
import { HiWord } from '~/components/HelpText/HiWord';
import { KWMv2AllInstructions } from '~/features/settings/panels/KWMv2/KWMv2AllInstructions';
import { SegmentKeyImportInstructions } from './SegmentKeyImportInstructions';
export function KuwoFAQ() {
return (
<>
<Header4></Header4>
<List spacing={2}>
<ListItem>
<SegmentTryOfficialPlayer />
</ListItem>
<ListItem>
<Text>
<chakra.strong>2</chakra.strong>
</Text>
<Text>
<HiWord></HiWord>
<VQuote>
<strong></strong>
</VQuote>
<VQuote>
<strong></strong>
</VQuote>
{'音质的音乐文件采用新版加密。'}
</Text>
<Text></Text>
<Text>PC平台暂未推出使用新版加密的音质</Text>
<Container p={2}>
<Alert status="warning" borderRadius={5}>
<AlertIcon />
<VStack>
<Text>Android root </Text>
<Text>使使</Text>
</VStack>
</Alert>
</Container>
<SegmentKeyImportInstructions tab="KWMv2 密钥" clientInstructions={<KWMv2AllInstructions />} />
</ListItem>
</List>
</>
);
}

28
src/faq/OtherFAQ.tsx Normal file
View File

@ -0,0 +1,28 @@
import { ExternalLinkIcon } from '@chakra-ui/icons';
import { Link, Text } from '@chakra-ui/react';
import { Header4 } from '~/components/HelpText/Header4';
import { ProjectIssue } from '~/components/ProjectIssue';
export function OtherFAQ() {
return (
<>
<Header4></Header4>
<Text></Text>
<Text>使</Text>
<Header4></Header4>
<Text>
<ProjectIssue id={34} title="[UI] 全部下载功能" /> {' '}
<ProjectIssue id={43} title="批量下载" />
</Text>
<Header4></Header4>
<Text>
{'欢迎进入 '}
<Link href={'https://t.me/unlock_music_chat'} isExternal>
Telegram -
<ExternalLinkIcon />
</Link>
{' 一起探讨。'}
</Text>
</>
);
}

40
src/faq/QQMusicFAQ.tsx Normal file
View File

@ -0,0 +1,40 @@
import { Alert, AlertIcon, Container, List, ListItem, Text, chakra } from '@chakra-ui/react';
import { Header4 } from '~/components/HelpText/Header4';
import { SegmentTryOfficialPlayer } from './SegmentTryOfficialPlayer';
import { QMCv2AllInstructions } from '~/features/settings/panels/QMCv2/QMCv2AllInstructions';
import { SegmentKeyImportInstructions } from './SegmentKeyImportInstructions';
export function QQMusicFAQ() {
return (
<>
<Header4></Header4>
<List spacing={2}>
<ListItem>
<SegmentTryOfficialPlayer />
</ListItem>
<ListItem>
<Text>
<chakra.strong>2</chakra.strong>
</Text>
<Text>Windows客户端下载的歌曲无需密钥</Text>
<Container p={2}>
<Alert status="warning" borderRadius={5}>
<AlertIcon />
iOS用户提取歌曲困难Android用户提取密钥需要root
</Alert>
</Container>
<Container p={2} pt={0}>
<Alert status="info" borderRadius={5}>
<AlertIcon />
</Alert>
</Container>
<SegmentKeyImportInstructions tab="QMCv2 密钥" clientInstructions={<QMCv2AllInstructions />} />
</ListItem>
</List>
</>
);
}

View File

@ -0,0 +1,25 @@
import { Flex, IconButton } from '@chakra-ui/react';
import { MdExpandMore } from 'react-icons/md';
import { HiWord } from '~/components/HelpText/HiWord';
import { VQuote } from '~/components/HelpText/VQuote';
export function SegmentAddKeyDropdown() {
return (
<Flex as="span" alignItems="center">
<VQuote></VQuote>
<HiWord></HiWord>
<IconButton
colorScheme="purple"
variant="outline"
size="sm"
icon={<MdExpandMore />}
ml="2"
borderTopLeftRadius={0}
borderBottomLeftRadius={0}
isDisabled
css={{ ':disabled': { opacity: 1 } }}
aria-label="示例按钮"
/>
</Flex>
);
}

View File

@ -0,0 +1,44 @@
import { Flex, Icon, ListItem, OrderedList, Tabs, Text } from '@chakra-ui/react';
import { SegmentTopNavSettings } from './SegmentTopNavSettings';
import { VQuote } from '~/components/HelpText/VQuote';
import { SegmentAddKeyDropdown } from './SegmentAddKeyDropdown';
import React from 'react';
import { MdFileUpload } from 'react-icons/md';
export interface SegmentKeyImportInstructionsProps {
clientInstructions: React.ReactNode;
tab: string;
}
export function SegmentKeyImportInstructions({ clientInstructions, tab }: SegmentKeyImportInstructionsProps) {
return (
<>
<Text></Text>
<OrderedList>
<ListItem>
<SegmentTopNavSettings />
</ListItem>
<ListItem>
<VQuote>{tab}</VQuote>
</ListItem>
<ListItem>
<SegmentAddKeyDropdown />
</ListItem>
<ListItem>
<Flex flexDir="row" alignItems="center">
{'选择 '}
<VQuote>
<Icon as={MdFileUpload} boxSize={5} mr={2} />
</VQuote>
</Flex>
</ListItem>
<ListItem>
<Text></Text>
<Tabs display="flex" flexDir="column" border="1px solid" borderColor="gray.300" borderRadius={5}>
{clientInstructions}
</Tabs>
</ListItem>
</OrderedList>
</>
);
}

View File

@ -0,0 +1,9 @@
import { VQuote } from '~/components/HelpText/VQuote';
export function SegmentTopNavSettings() {
return (
<>
<VQuote></VQuote>
</>
);
}

View File

@ -0,0 +1,12 @@
import { Text, chakra } from '@chakra-ui/react';
export function SegmentTryOfficialPlayer() {
return (
<>
<Text>
<chakra.strong>1</chakra.strong>
</Text>
<Text></Text>
</>
);
}

View File

@ -0,0 +1,25 @@
import { Tab, TabList, TabPanel, TabPanels } from '@chakra-ui/react';
import { AndroidADBPullInstruction } from '~/components/AndroidADBPullInstruction/AndroidADBPullInstruction';
import { InstructionsPC } from './InstructionsPC';
export function KWMv2AllInstructions() {
return (
<>
<TabList>
<Tab></Tab>
<Tab>Windows</Tab>
</TabList>
<TabPanels flex={1} overflow="auto">
<TabPanel>
<AndroidADBPullInstruction
dir="/data/data/cn.kuwo.player/files/mmkv"
file="cn.kuwo.player.mmkv.defaultconfig"
/>
</TabPanel>
<TabPanel>
<InstructionsPC />
</TabPanel>
</TabPanels>
</>
);
}

View File

@ -14,10 +14,6 @@ import {
MenuDivider,
MenuItem,
MenuList,
Tab,
TabList,
TabPanel,
TabPanels,
Text,
useToast,
} from '@chakra-ui/react';
@ -32,8 +28,7 @@ import { kwm2AddKey, kwm2ClearKeys, kwm2ImportKeys } from '../settingsSlice';
import { selectStagingKWMv2Keys } from '../settingsSelector';
import { KWMv2EKeyItem } from './KWMv2/KWMv2EKeyItem';
import type { StagingKWMv2Key } from '../keyFormats';
import { InstructionsPC } from './KWMv2/InstructionsPC';
import { AndroidADBPullInstruction } from '~/components/AndroidADBPullInstruction/AndroidADBPullInstruction';
import { KWMv2AllInstructions } from './KWMv2/KWMv2AllInstructions';
export function PanelKWMv2Key() {
const toast = useToast();
@ -120,21 +115,7 @@ export function PanelKWMv2Key() {
onClose={() => setShowImportModal(false)}
onImport={handleSecretImport}
>
<TabList>
<Tab></Tab>
<Tab>Windows</Tab>
</TabList>
<TabPanels flex={1} overflow="auto">
<TabPanel>
<AndroidADBPullInstruction
dir="/data/data/cn.kuwo.player/files/mmkv"
file="cn.kuwo.player.mmkv.defaultconfig"
/>
</TabPanel>
<TabPanel>
<InstructionsPC />
</TabPanel>
</TabPanels>
<KWMv2AllInstructions />
</ImportSecretModal>
</Flex>
);

View File

@ -14,10 +14,6 @@ import {
MenuDivider,
MenuItem,
MenuList,
Tab,
TabList,
TabPanel,
TabPanels,
Text,
Tooltip,
useToast,
@ -34,10 +30,7 @@ import { StagingQMCv2Key } from '../keyFormats';
import { DatabaseKeyExtractor } from '~/util/DatabaseKeyExtractor';
import { MMKVParser } from '~/util/MMKVParser';
import { getFileName } from '~/util/pathHelper';
import { InstructionsIOS } from './QMCv2/InstructionsIOS';
import { InstructionsMac } from './QMCv2/InstructionsMac';
import { InstructionsPC } from './QMCv2/InstructionsPC';
import { AndroidADBPullInstruction } from '~/components/AndroidADBPullInstruction/AndroidADBPullInstruction';
import { QMCv2AllInstructions } from './QMCv2/QMCv2AllInstructions';
export function PanelQMCv2Key() {
const toast = useToast();
@ -171,26 +164,7 @@ export function PanelQMCv2Key() {
onClose={() => setShowImportModal(false)}
onImport={handleSecretImport}
>
<TabList>
<Tab></Tab>
<Tab>iOS</Tab>
<Tab>Mac</Tab>
<Tab>Windows</Tab>
</TabList>
<TabPanels flex={1} overflow="auto">
<TabPanel>
<AndroidADBPullInstruction dir="/data/data/com.tencent.qqmusic/databases" file="player_process_db" />
</TabPanel>
<TabPanel>
<InstructionsIOS />
</TabPanel>
<TabPanel>
<InstructionsMac />
</TabPanel>
<TabPanel>
<InstructionsPC />
</TabPanel>
</TabPanels>
<QMCv2AllInstructions />
</ImportSecretModal>
</Flex>
);

View File

@ -0,0 +1,32 @@
import { Tab, TabList, TabPanel, TabPanels } from '@chakra-ui/react';
import { AndroidADBPullInstruction } from '~/components/AndroidADBPullInstruction/AndroidADBPullInstruction';
import { InstructionsIOS } from './InstructionsIOS';
import { InstructionsMac } from './InstructionsMac';
import { InstructionsPC } from './InstructionsPC';
export function QMCv2AllInstructions() {
return (
<>
<TabList>
<Tab></Tab>
<Tab>iOS</Tab>
<Tab>Mac</Tab>
<Tab>Windows</Tab>
</TabList>
<TabPanels flex={1} overflow="auto">
<TabPanel>
<AndroidADBPullInstruction dir="/data/data/com.tencent.qqmusic/databases" file="player_process_db" />
</TabPanel>
<TabPanel>
<InstructionsIOS />
</TabPanel>
<TabPanel>
<InstructionsMac />
</TabPanel>
<TabPanel>
<InstructionsPC />
</TabPanel>
</TabPanels>
</>
);
}

21
src/tabs/FaqTab.tsx Normal file
View File

@ -0,0 +1,21 @@
import { Center, Container, Heading } from '@chakra-ui/react';
import { Header3 } from '~/components/HelpText/Header3';
import { KuwoFAQ } from '~/faq/KuwoFAQ';
import { OtherFAQ } from '~/faq/OtherFAQ';
import { QQMusicFAQ } from '~/faq/QQMusicFAQ';
export function FaqTab() {
return (
<Container pb={10} maxW="container.md">
<Center>
<Heading as="h2"></Heading>
</Center>
<Header3>QQ </Header3>
<QQMusicFAQ />
<Header3></Header3>
<KuwoFAQ />
<Header3></Header3>
<OtherFAQ />
</Container>
);
}