Peace 1 week ago
parent 256b222d69
commit 036ed5441a
  1. 57
      every-jap-dict/App.tsx
  2. 10
      every-jap-dict/app.json
  3. 12
      every-jap-dict/components/AdBannerPlaceholder.tsx
  4. 1600
      every-jap-dict/components/ui/icon/index.tsx
  5. 1573
      every-jap-dict/components/ui/icon/index.web.tsx
  6. 10
      every-jap-dict/lib/providers/dictionaries.ts
  7. 27807
      every-jap-dict/package-lock.json
  8. 87
      every-jap-dict/package.json

@ -1,5 +1,5 @@
import { StatusBar } from 'expo-status-bar'; import { StatusBar } from 'expo-status-bar';
import { FlatList, Keyboard, Modal, Pressable, StyleSheet, View } from 'react-native'; import { FlatList, Keyboard, Modal, Pressable, ScrollView, StyleSheet, View } from 'react-native';
import { SafeAreaProvider, SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import { SafeAreaProvider, SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'; import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider';
@ -10,11 +10,15 @@ import { useMemo, 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';
import { Button, ButtonText } from './components/ui/button'; import { Button, ButtonIcon, ButtonText } from './components/ui/button';
import DictWebView from './components/DictWebView'; import DictWebView from './components/DictWebView';
import AdBannerPlaceholder from './components/AdBannerPlaceholder'; import AdBannerPlaceholder from './components/AdBannerPlaceholder';
import { Toast, ToastDescription, ToastTitle, useToast } from './components/ui/toast'; import { Toast, ToastDescription, ToastTitle, useToast } from './components/ui/toast';
import mobileAds from 'react-native-google-mobile-ads';
import { Icon } from './components/ui/icon';
import { Bookmark, BookMarked, BookOpen, Search } from 'lucide-react-native';
function MainScreen() { function MainScreen() {
const [q, setQ] = useState(''); const [q, setQ] = useState('');
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
@ -54,6 +58,7 @@ function MainScreen() {
add(q.trim(), pack.code); add(q.trim(), pack.code);
toast.show({ toast.show({
id: 'onSave',
placement: 'top', placement: 'top',
duration: 2000, duration: 2000,
render: () => ( render: () => (
@ -82,29 +87,17 @@ function MainScreen() {
<View className="flex-1"> <View className="flex-1">
{/* 상단 검색창, 버튼 */} {/* 상단 검색창, 버튼 */}
<View className="p-4 gap-3"> <View className="p-4 gap-3">
{/* 액션 버튼 */} {/* 검색창 */}
<HStack space="md" className="flex-row"> <HStack space="md" className="flex-row">
<Button <Button
className="flex-1" className="p-1"
onPress={() => setWordbookOpen(true)} onPress={() => setWordbookOpen(true)}
variant="outline" variant="outline"
action="secondary" action="secondary"
accessibilityLabel="단어장 보기"
> >
<ButtonText> </ButtonText> <Icon as={BookMarked} />
</Button> </Button>
<Button
className="flex-1"
onPress={onSave}
variant="outline"
action="secondary"
isDisabled={!canSearch}
>
<ButtonText> </ButtonText>
</Button>
</HStack>
{/* 검색창 */}
<HStack space="md" className="flex-row">
<Input className="flex-1"> <Input className="flex-1">
<InputField <InputField
placeholder="검색어를 입력하세요" placeholder="검색어를 입력하세요"
@ -115,15 +108,29 @@ function MainScreen() {
onSubmitEditing={onSearch} onSubmitEditing={onSearch}
/> />
</Input> </Input>
<Button
className="p-1"
onPress={onSave}
variant="outline"
action="secondary"
isDisabled={!canSearch}
accessibilityLabel="단어장 저장"
>
<Icon as={Bookmark} />
</Button>
<Button onPress={onSearch} isDisabled={!canSearch}> <Button onPress={onSearch} isDisabled={!canSearch}>
<ButtonText></ButtonText> <ButtonText></ButtonText>
</Button> </Button>
</HStack> </HStack>
</View> </View>
{/* 커스텀 탭 헤더 */} {/* 커스텀 탭 헤더 (가로스크롤) */}
<View className="px-4 pb-2"> <View className="px-4 pb-2">
<HStack space="sm" className="flex-row"> <ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerClassName="gap-1"
>
{providers.map((p) => { {providers.map((p) => {
const selected = activeId ? activeId === p.id : false; const selected = activeId ? activeId === p.id : false;
return ( return (
@ -138,7 +145,7 @@ function MainScreen() {
</Button> </Button>
); );
})} })}
</HStack> </ScrollView>
</View> </View>
{/* 패널: WebView 모두 마운트 후 토글 */} {/* 패널: WebView 모두 마운트 후 토글 */}
@ -186,7 +193,7 @@ function MainScreen() {
data={items} data={items}
keyExtractor={(x) => x.id} keyExtractor={(x) => x.id}
renderItem={({ item }) => ( renderItem={({ item }) => (
<View className="flex-row justify-between py-3 gap-4"> <View className="flex-row justify-between py-1">
<Pressable <Pressable
onPress={() => onPickWord(item.text)} onPress={() => onPickWord(item.text)}
style={{ flex: 1 }} style={{ flex: 1 }}
@ -208,6 +215,12 @@ function MainScreen() {
} }
export default function App() { export default function App() {
// mobileAds()
// .initialize()
// .then(() => {
// // ready
// });
return ( return (
<GluestackUIProvider mode="light"> <GluestackUIProvider mode="light">
<SafeAreaProvider> <SafeAreaProvider>

@ -24,6 +24,14 @@
}, },
"web": { "web": {
"favicon": "./assets/favicon.png" "favicon": "./assets/favicon.png"
} },
"plugins": [
[
"react-native-google-mobile-ads",
{
"androidAppId": "ca-app-pub-7854988771210967~3806885931"
}
]
]
} }
} }

@ -1,5 +1,15 @@
import { View } from 'react-native'; import { View } from 'react-native';
import { BannerAd, BannerAdSize, TestIds } from 'react-native-google-mobile-ads';
// const unitId = __DEV__ ? TestIds.BANNER : 'ca-app-pub-7854988771210967~3806885931';
export default function AdBannerPlaceholder() { export default function AdBannerPlaceholder() {
return <View className="w-full h-14" />; return (
<View className="w-full h-14" />
// <BannerAd
// unitId={unitId}
// size={BannerAdSize.BANNER}
// requestOptions={{ requestNonPersonalizedAdsOnly: true }}
// />
);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -25,6 +25,16 @@ export const jaPack: LanguagePack = {
label: '다음', label: '다음',
buildUrl: (q) => `https://dic.daum.net/search.do?q=${encodeURIComponent(q)}&dic=jp`, buildUrl: (q) => `https://dic.daum.net/search.do?q=${encodeURIComponent(q)}&dic=jp`,
}, },
{
id: 'papago-ko-to-ja',
label: '파파고(한→일)',
buildUrl: (q) => `https://papago.naver.com/?sk=ko&tk=ja&hn=0&st=${encodeURIComponent(q)}`,
},
{
id: 'papago-ja-to-ko',
label: '파파고(일→한)',
buildUrl: (q) => `https://papago.naver.com/?sk=ja&tk=ko&st=${encodeURIComponent(q)}`,
},
{ {
id: 'weblio', id: 'weblio',
label: 'Weblio', label: 'Weblio',

File diff suppressed because it is too large Load Diff

@ -1,42 +1,45 @@
{ {
"name": "every-jap-dict", "name": "every-jap-dict",
"version": "1.0.0", "version": "1.0.0",
"main": "index.ts", "main": "index.ts",
"scripts": { "scripts": {
"start": "expo start --offline", "start": "expo start --offline",
"android": "expo start --android --offline", "android": "expo start --android --offline",
"ios": "expo start --ios --offline", "ios": "expo start --ios --offline",
"web": "expo start --web --offline" "web": "expo start --web --offline"
}, },
"dependencies": { "dependencies": {
"@expo/html-elements": "^0.10.1", "@expo/html-elements": "^0.10.1",
"@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": "~53.0.22", "expo": "~53.0.22",
"expo-status-bar": "~2.2.3", "expo-dev-client": "~5.2.4",
"install": "^0.13.0", "expo-status-bar": "~2.2.3",
"nativewind": "^4.1.23", "install": "^0.13.0",
"npm": "^11.6.0", "lucide-react-native": "^0.543.0",
"react": "19.0.0", "nativewind": "^4.1.23",
"react-aria": "^3.33.0", "npm": "^11.6.0",
"react-dom": "^19.1.1", "react": "19.0.0",
"react-native": "0.79.6", "react-aria": "^3.33.0",
"react-native-reanimated": "^4.1.0", "react-dom": "^19.1.1",
"react-native-safe-area-context": "^5.6.1", "react-native": "0.79.6",
"react-native-svg": "^15.2.0", "react-native-google-mobile-ads": "^15.7.0",
"react-native-webview": "^13.16.0", "react-native-reanimated": "^4.1.0",
"react-native-worklets": "^0.5.0", "react-native-safe-area-context": "^5.6.1",
"react-stately": "^3.39.0", "react-native-svg": "^15.12.0",
"tailwind-variants": "^0.1.20", "react-native-webview": "^13.16.0",
"tailwindcss": "^3.4.17" "react-native-worklets": "^0.5.0",
}, "react-stately": "^3.39.0",
"devDependencies": { "tailwind-variants": "^0.1.20",
"@babel/core": "^7.25.2", "tailwindcss": "^3.4.17"
"@types/react": "~19.0.10", },
"typescript": "~5.8.3" "devDependencies": {
}, "@babel/core": "^7.25.2",
"private": true "@types/react": "~19.0.10",
} "typescript": "~5.8.3"
},
"private": true
}

Loading…
Cancel
Save