diff --git a/.vs/LEARNIHON/FileContentIndex/18ab66a5-4a11-4328-bc91-cd3376b0ec91.vsidx b/.vs/LEARNIHON/FileContentIndex/18ab66a5-4a11-4328-bc91-cd3376b0ec91.vsidx deleted file mode 100644 index 049725d..0000000 Binary files a/.vs/LEARNIHON/FileContentIndex/18ab66a5-4a11-4328-bc91-cd3376b0ec91.vsidx and /dev/null differ diff --git a/.vs/LEARNIHON/FileContentIndex/985f779d-3e71-41e7-8e72-34b885e698a7.vsidx b/.vs/LEARNIHON/FileContentIndex/985f779d-3e71-41e7-8e72-34b885e698a7.vsidx new file mode 100644 index 0000000..1fcabfc Binary files /dev/null and b/.vs/LEARNIHON/FileContentIndex/985f779d-3e71-41e7-8e72-34b885e698a7.vsidx differ diff --git a/.vs/LEARNIHON/FileContentIndex/a731465e-eb3c-499c-8b0d-31f641e360d5.vsidx b/.vs/LEARNIHON/FileContentIndex/a731465e-eb3c-499c-8b0d-31f641e360d5.vsidx deleted file mode 100644 index 4954627..0000000 Binary files a/.vs/LEARNIHON/FileContentIndex/a731465e-eb3c-499c-8b0d-31f641e360d5.vsidx and /dev/null differ diff --git a/.vs/LEARNIHON/FileContentIndex/435d2972-bbd4-45e0-8354-cf69c2c152c7.vsidx b/.vs/LEARNIHON/FileContentIndex/ae2e6dd4-98b8-42d0-8ba6-f2bd60e1aa88.vsidx similarity index 53% rename from .vs/LEARNIHON/FileContentIndex/435d2972-bbd4-45e0-8354-cf69c2c152c7.vsidx rename to .vs/LEARNIHON/FileContentIndex/ae2e6dd4-98b8-42d0-8ba6-f2bd60e1aa88.vsidx index 5150a6d..6674bf8 100644 Binary files a/.vs/LEARNIHON/FileContentIndex/435d2972-bbd4-45e0-8354-cf69c2c152c7.vsidx and b/.vs/LEARNIHON/FileContentIndex/ae2e6dd4-98b8-42d0-8ba6-f2bd60e1aa88.vsidx differ diff --git a/.vs/LEARNIHON/FileContentIndex/c4f4ae09-c384-4dd2-87f4-19f2615f5a9c.vsidx b/.vs/LEARNIHON/FileContentIndex/c4f4ae09-c384-4dd2-87f4-19f2615f5a9c.vsidx new file mode 100644 index 0000000..7060fc4 Binary files /dev/null and b/.vs/LEARNIHON/FileContentIndex/c4f4ae09-c384-4dd2-87f4-19f2615f5a9c.vsidx differ diff --git a/.vs/LEARNIHON/v17/.wsuo b/.vs/LEARNIHON/v17/.wsuo index 2f2f413..17ea5fa 100644 Binary files a/.vs/LEARNIHON/v17/.wsuo and b/.vs/LEARNIHON/v17/.wsuo differ diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json index 1048f14..834b9e4 100644 --- a/.vs/VSWorkspaceState.json +++ b/.vs/VSWorkspaceState.json @@ -1,13 +1,15 @@ { "ExpandedNodes": [ "", - "\\assets", "\\components", "\\model", "\\navigation", "\\pages", - "\\redux" + "\\redux", + "\\redux\\actions", + "\\redux\\reducers", + "\\redux\\thunks" ], - "SelectedNode": "\\components\\KanjiListCell.tsx", + "SelectedNode": "\\components\\KanjiListSearchPanel.tsx", "PreviewInSolutionExplorer": false } \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite index feede14..baf150b 100644 Binary files a/.vs/slnx.sqlite and b/.vs/slnx.sqlite differ diff --git a/App.tsx b/App.tsx index b9c4b88..32822ee 100644 --- a/App.tsx +++ b/App.tsx @@ -1,26 +1,25 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import store from "./redux/store"; -import { Provider } from 'react-redux'; +import { Provider, useDispatch } from 'react-redux'; import { StatusBar } from 'expo-status-bar'; import { Keyboard, SafeAreaView, StyleSheet, TouchableWithoutFeedback} from 'react-native'; import Header from './components/Header'; import TabBar from './navigation/TabBar'; +import { InitStack } from './navigation/Startup'; export default function App() { - return ( - { Keyboard.dismiss(); }}> - -
- - - - + { Keyboard.dismiss(); }}> + + + + + ); diff --git a/assets/answerAnimation.ts b/assets/animations/answerAnimation.ts similarity index 98% rename from assets/answerAnimation.ts rename to assets/animations/answerAnimation.ts index a1dc765..9cd181c 100644 --- a/assets/answerAnimation.ts +++ b/assets/animations/answerAnimation.ts @@ -23,7 +23,7 @@ export const animatedStyles = { { translateY: animation.interpolate({ inputRange: [0, 1], - outputRange: [0, -Dimensions.get('window').height / 2] + outputRange: [0, -Dimensions.get('window').height/2] }), }, ], diff --git a/components/DetailExamples.tsx b/components/DetailExamples.tsx index a923f99..c430e97 100644 --- a/components/DetailExamples.tsx +++ b/components/DetailExamples.tsx @@ -29,7 +29,8 @@ const DetailExamples = (props: detailExamplesProps) => { const detailExamplesStyle_light = StyleSheet.create({ container: { width: '100%', - paddingBottom: 50, + paddingRight: 20, + paddingLeft: 20, }, cellContainer: { flex: 1, @@ -47,7 +48,8 @@ const detailExamplesStyle_light = StyleSheet.create({ const detailExamplesStyle_dark = StyleSheet.create({ container: { width: '100%', - paddingBottom: 50, + paddingRight: 20, + paddingLeft: 20, }, cellContainer: { flex: 1, diff --git a/components/DrawingCanva.tsx b/components/DrawingCanva.tsx index c58c186..d2dae57 100644 --- a/components/DrawingCanva.tsx +++ b/components/DrawingCanva.tsx @@ -1,11 +1,13 @@ import React, {useEffect, useRef, useState } from 'react'; import { SketchCanvas, SketchCanvasRef } from 'rn-perfect-sketch-canvas'; -import { StyleSheet, Button, View, Text, useColorScheme } from 'react-native'; +import { StyleSheet, Button, View, Text, useColorScheme, Touchable } from 'react-native'; import { SvgXml } from 'react-native-svg'; import Slider from '@react-native-community/slider' import { useSelector } from 'react-redux'; -import { Kanji, KanjiMapper } from '../model/kanji'; +import { Kanji } from '../model/kanji'; +import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; +import { KanjiMapper } from '../model/kanjiMapper'; type DrawingCanvaProps = { backgroundImage: string; @@ -13,16 +15,15 @@ type DrawingCanvaProps = { const DrawingCanva = (props: DrawingCanvaProps) => { - const style = useColorScheme() == 'light' ? style_light : style_dark; + const style = useColorScheme() == 'light' ? style_light : style_dark; const canvasRef = useRef(null); const [strokeWidth, setStroke] = useState(5); const [isCanvasReady, setIsCanvasReady] = useState(false); const [imgXml, setImgXml] = useState(''); - + const [drawnStrokes, setDrawnStrokes] = useState(0); const selectedKanji = KanjiMapper.SerializedObjectToKanji(useSelector(state => state.kanjiReducer.selectedKanji)); - useEffect(() => { fetchXml(); if (canvasRef.current) { @@ -30,6 +31,10 @@ const DrawingCanva = (props: DrawingCanvaProps) => { } }, [canvasRef.current, selectedKanji]); + const getCanvasStrokeCount = () => { + return canvasRef.current?.toPoints().length; + } + const fetchXml = async () => { if (selectedKanji instanceof Kanji) { const xml = await (await fetch(selectedKanji.image)).text(); @@ -37,7 +42,6 @@ const DrawingCanva = (props: DrawingCanvaProps) => { } } - return ( {selectedKanji && ( @@ -49,17 +53,18 @@ const DrawingCanva = (props: DrawingCanvaProps) => { opacity={0.1} style={style.back} />)} - + + setStroke(val)} minimumValue={5} - maximumValue={10} + maximumValue={15} minimumTrackTintColor={"#FF5C5C"} /> {isCanvasReady && ( diff --git a/components/GradeChip.tsx b/components/GradeChip.tsx new file mode 100644 index 0000000..8b36830 --- /dev/null +++ b/components/GradeChip.tsx @@ -0,0 +1,78 @@ +import { useNavigation } from '@react-navigation/native'; +import React, { useEffect, useState } from 'react'; +import { Text, View, StyleSheet, TouchableOpacity, useColorScheme, Animated } from 'react-native'; +import { Check } from "react-native-feather"; + +interface gradeChipProps { + grade: number; + onSelect: (item: string, isSelected: Boolean) => void; +} + +const GradeChip = (props: gradeChipProps) => { + + const [chipStyle, setChipStyle] = useState(style); + + const [isSelected, setIsSelected] = useState(false); + + useEffect(() => { + setChipStyle(isSelected ? styleSELECTED : style); + }, [isSelected]); + + const select = () => { + props.onSelect("Grade "+props.grade, isSelected); + setIsSelected(!isSelected); + } + + return ( + { select() }}> + Grade {props.grade} + {isSelected && ()} + + ); +}; + +const style = StyleSheet.create({ + chip: { + backgroundColor: "#FF5C5C", + borderRadius: 10, + margin: 5, + justifyContent: "center", + }, + icon: { + color: "white", + margin: 5 + }, + text: { + color: "white", + fontWeight: "bold", + fontSize: "18em", + padding: 5 + + }, +}) + +const styleSELECTED = StyleSheet.create({ + chip: { + backgroundColor: "#FF5C5C", + borderRadius: 10, + margin: 5, + justifyContent: "center", + flexDirection: "row", + alignItems: "center", + }, + icon: { + color: "white", + margin: 5 + }, + text: { + color: "white", + fontWeight: "bold", + fontSize: "18em", + padding: 5 + + }, +}) + + + +export default GradeChip; \ No newline at end of file diff --git a/components/KanjiAnswerField.tsx b/components/KanjiAnswerField.tsx index 7d0b136..84fdfb2 100644 --- a/components/KanjiAnswerField.tsx +++ b/components/KanjiAnswerField.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Animated, StyleSheet, TextInput } from 'react-native'; -import { startAnimation, stopAnimation, animatedStyles } from '../assets/answerAnimation' +import { startAnimation, stopAnimation, animatedStyles } from '../assets/animations/answerAnimation' const KanjiAnswerField = () => { diff --git a/components/KanjiCard.tsx b/components/KanjiCard.tsx index 9987701..5154b8f 100644 --- a/components/KanjiCard.tsx +++ b/components/KanjiCard.tsx @@ -1,6 +1,10 @@ import React, { useEffect, useState } from 'react'; import { StyleSheet, Text, View, Button, useColorScheme } from 'react-native'; import { SvgUri, SvgXml } from 'react-native-svg'; +import { useSelector } from 'react-redux'; +import { Kanji } from '../model/kanji'; +import { KanjiListByGrade } from '../model/kanjiListByGrades'; +import { KanjiMapper } from '../model/kanjiMapper'; import KanjiAnswerField from './KanjiAnswerField'; type KanjiProps = { @@ -12,6 +16,7 @@ const KanjiCard = (props: KanjiProps) => { const kanjiCardStyle = useColorScheme() == 'light' ? kanjiCardStyle_light : kanjiCardStyle_dark; + const options = { method: 'GET', headers: { @@ -21,32 +26,50 @@ const KanjiCard = (props: KanjiProps) => { } const [loading, setLoading] = useState(true); - const [res, setData] = useState(null); + const [kanji, setKanji] = useState((): Kanji | null => { return null }); const [imgXml, setImgXml] = useState(''); + + var kanjis: KanjiListByGrade = useSelector(state => state.kanjiReducer.kanjis); + + const allKanjis: Kanji[] = [].concat(...Object.values(kanjis)) + const selectedKanji = allKanjis[Math.floor(Math.random() * allKanjis.length)] + const fetchData = async () => { await fetch(`https://kanjialive-api.p.rapidapi.com/api/public/kanji/${props.kanji}`, options) .then(async response => { const data = await response.json() - setData(data); - const xml = await (await fetch(data.kanji.video.poster)).text(); - setImgXml(xml); + setKanji(KanjiMapper.ApiJsonToKanji(data)); }) .catch(err => console.log(err)); } + const fetchXml = async () => { + if (kanji) { + const xml = await (await fetch(kanji?.image!)).text(); + setImgXml(xml); + } + } + useEffect(() => { setLoading(true); fetchData().then(_ => { + setKanji(selectedKanji); setLoading(false); }); }, []); + useEffect(() => { + setLoading(true); + fetchXml().then(_ => { + setLoading(false); + }); + }, [kanji]); return ( - {loading ? Loading... : {res.kanji.onyomi.katakana}} + {loading ? Loading... : {kanji?.onyomi}} {!loading && ( { height="200" /> )} - {loading ? : {res.kanji.meaning.english}} + {loading ? : {kanji?.meaning}}