Peace 5 days ago
parent bbde69c687
commit dbbf97286f
  1. 33
      every-jap-dict/App.tsx
  2. 1047
      every-jap-dict/package-lock.json
  3. 9
      every-jap-dict/package.json

@ -6,7 +6,7 @@ import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider';
import '@/global.css'; import '@/global.css';
import { useWordbook, WordbookProvider } from './lib/wordbook/context'; import { useWordbook, WordbookProvider } from './lib/wordbook/context';
import { Text } from './components/ui/text'; import { Text } from './components/ui/text';
import { useMemo, useState } from 'react'; import { useMemo, useRef, useState } from 'react';
import { jaPack, languages } from './lib/providers/dictionaries'; import { jaPack, languages } from './lib/providers/dictionaries';
import { Input, InputField } from './components/ui/input'; import { Input, InputField } from './components/ui/input';
import { HStack } from './components/ui/hstack'; import { HStack } from './components/ui/hstack';
@ -17,7 +17,8 @@ import { Toast, ToastDescription, ToastTitle, useToast } from './components/ui/t
import mobileAds from 'react-native-google-mobile-ads'; import mobileAds from 'react-native-google-mobile-ads';
import { Icon } from './components/ui/icon'; import { Icon } from './components/ui/icon';
import { Bookmark, BookMarked, BookOpen, Search } from 'lucide-react-native'; import { Bookmark, BookmarkCheck, BookMarked, BookOpen, Search } from 'lucide-react-native';
import * as Animatable from 'react-native-animatable';
function MainScreen() { function MainScreen() {
const [q, setQ] = useState(''); const [q, setQ] = useState('');
@ -36,6 +37,11 @@ function MainScreen() {
const { add, items, remove, clear, loading } = useWordbook(); const { add, items, remove, clear, loading } = useWordbook();
const baseText = (query || q).trim();
const isSaved =
!!baseText &&
items.some((it) => it.text.toLowerCase() === baseText.toLowerCase() && it.lang === pack.code);
const urls = useMemo( const urls = useMemo(
() => () =>
providers.reduce<Record<string, string>>((acc, p) => { providers.reduce<Record<string, string>>((acc, p) => {
@ -55,23 +61,28 @@ function MainScreen() {
const onSave = () => { const onSave = () => {
if (!canSearch) return; if (!canSearch) return;
Keyboard.dismiss(); Keyboard.dismiss();
add(q.trim(), pack.code);
const toastDesc = isSaved ? '이미 단어장에 있어요' : '단어장에 저장되었습니다';
if (!isSaved) add(q.trim(), pack.code);
toast.show({ toast.show({
id: 'onSave',
placement: 'top', placement: 'top',
duration: 2000, duration: 2000,
render: () => ( render: ({ id }) => {
console.log('toast render called');
return (
<View style={{ marginTop: insets.top + 12, alignItems: 'center', width: '100%' }}> <View style={{ marginTop: insets.top + 12, alignItems: 'center', width: '100%' }}>
<Toast <Toast
className="rounded-full opacity-80 mx-5 py-2 flex-row items-center gap-2 shadow-lg max-w-[90%]" nativeID={`toast-${id}`}
variant="solid" variant="solid"
className="rounded-full opacity-90 mx-5 px-5 py-2 flex-row items-center gap-2 shadow-lg max-w-[90%]"
> >
<ToastTitle></ToastTitle> <ToastTitle></ToastTitle>
<ToastDescription> </ToastDescription> <ToastDescription>{toastDesc}</ToastDescription>
</Toast> </Toast>
</View> </View>
), );
},
}); });
}; };
@ -108,6 +119,7 @@ function MainScreen() {
onSubmitEditing={onSearch} onSubmitEditing={onSearch}
/> />
</Input> </Input>
<Animatable.View animation="tada" duration={600} useNativeDriver>
<Button <Button
className="p-1" className="p-1"
onPress={onSave} onPress={onSave}
@ -116,8 +128,10 @@ function MainScreen() {
isDisabled={!canSearch} isDisabled={!canSearch}
accessibilityLabel="단어장 저장" accessibilityLabel="단어장 저장"
> >
<Icon as={Bookmark} /> <Icon as={isSaved ? BookmarkCheck : Bookmark} />
</Button> </Button>
</Animatable.View>
<Button onPress={onSearch} isDisabled={!canSearch}> <Button onPress={onSearch} isDisabled={!canSearch}>
<ButtonText></ButtonText> <ButtonText></ButtonText>
</Button> </Button>
@ -228,6 +242,7 @@ export default function App() {
<StatusBar style="auto" /> <StatusBar style="auto" />
<WordbookProvider> <WordbookProvider>
<MainScreen /> <MainScreen />
<Toast />
</WordbookProvider> </WordbookProvider>
</SafeAreaView> </SafeAreaView>
</SafeAreaProvider> </SafeAreaProvider>

File diff suppressed because it is too large Load Diff

@ -13,7 +13,7 @@
"@gluestack-ui/core": "^3.0.0", "@gluestack-ui/core": "^3.0.0",
"@gluestack-ui/utils": "^3.0.0", "@gluestack-ui/utils": "^3.0.0",
"@legendapp/motion": "^2.4.0", "@legendapp/motion": "^2.4.0",
"@react-native-async-storage/async-storage": "^2.2.0", "@react-native-async-storage/async-storage": "2.2.0",
"babel-plugin-module-resolver": "^5.0.2", "babel-plugin-module-resolver": "^5.0.2",
"expo": "^54.0.7", "expo": "^54.0.7",
"expo-status-bar": "~3.0.8", "expo-status-bar": "~3.0.8",
@ -25,9 +25,10 @@
"react-aria": "^3.33.0", "react-aria": "^3.33.0",
"react-dom": "19.1.0", "react-dom": "19.1.0",
"react-native": "0.81.4", "react-native": "0.81.4",
"react-native-animatable": "^1.4.0",
"react-native-google-mobile-ads": "^15.7.0", "react-native-google-mobile-ads": "^15.7.0",
"react-native-reanimated": "^4.1.0", "react-native-reanimated": "~4.1.0",
"react-native-safe-area-context": "^5.6.1", "react-native-safe-area-context": "~5.6.0",
"react-native-svg": "15.12.1", "react-native-svg": "15.12.1",
"react-native-webview": "13.15.0", "react-native-webview": "13.15.0",
"react-native-worklets": "0.5.1", "react-native-worklets": "0.5.1",
@ -37,7 +38,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",
"@expo/config-plugins": "^54.0.1", "@expo/config-plugins": "~54.0.1",
"@types/react": "~19.1.10", "@types/react": "~19.1.10",
"typescript": "~5.9.2" "typescript": "~5.9.2"
}, },

Loading…
Cancel
Save