From bdc205b5381346eba8e35a8f7ea3db1ae7ed9d72 Mon Sep 17 00:00:00 2001 From: azizziy Date: Mon, 2 Mar 2026 09:49:02 +0500 Subject: [PATCH] fix: webview services --- .../e-services/ui/EServicesCategoryScreen.tsx | 166 ++++-------- screens/e-services/ui/EServicesScreen.tsx | 247 ++++++++---------- 2 files changed, 160 insertions(+), 253 deletions(-) diff --git a/screens/e-services/ui/EServicesCategoryScreen.tsx b/screens/e-services/ui/EServicesCategoryScreen.tsx index 2c50b72..8b888f5 100644 --- a/screens/e-services/ui/EServicesCategoryScreen.tsx +++ b/screens/e-services/ui/EServicesCategoryScreen.tsx @@ -1,42 +1,54 @@ -import Express_diagnistika from '@/assets/images/Express_diagnistika.png'; -import { useTheme } from '@/components/ThemeContext'; -import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { Image } from 'expo-image'; -import { router } from 'expo-router'; -import { ChevronLeft, XIcon } from 'lucide-react-native'; -import React, { useRef, useState } from 'react'; -import { ActivityIndicator, FlatList, Modal, Text, TouchableOpacity, View } from 'react-native'; -import { RefreshControl } from 'react-native-gesture-handler'; -import { SafeAreaView } from 'react-native-safe-area-context'; -import { WebView } from 'react-native-webview'; -import { eservices_api } from '../lib/api'; +import Express_diagnistika from "@/assets/images/Express_diagnistika.png"; +import { useTheme } from "@/components/ThemeContext"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { Image } from "expo-image"; +import { router } from "expo-router"; +import { useState } from "react"; +import { + ActivityIndicator, + FlatList, + Text, + ToastAndroid, + TouchableOpacity, + View, +} from "react-native"; +import { RefreshControl } from "react-native-gesture-handler"; +import * as WebBrowser from "expo-web-browser"; +import { eservices_api } from "../lib/api"; +import { useTranslation } from "react-i18next"; const dark = { - bg: '#0f172a', - card: '#334155', - border: '#1e293b', - muted: '#334155', - text: '#E5B037', - subText: '#0B0F2C', + bg: "#0f172a", + card: "#334155", + border: "#1e293b", + muted: "#334155", + text: "#E5B037", + subText: "#0B0F2C", }; export default function EServicesCategoryScreen() { const { isDark } = useTheme(); - const [modalVisible, setModalVisible] = useState(false); - const webviewRef = useRef(null); - const [webUrl, setWebUrl] = React.useState(null); const [refreshing, setRefreshing] = useState(false); const queryClient = useQueryClient(); + const { t } = useTranslation(); const { data, isLoading } = useQuery({ - queryKey: ['goverment_category'], + queryKey: ["goverment_category"], queryFn: () => eservices_api.category(), }); + const handleOpenBrowser = async (fileUrl: string) => { + try { + await WebBrowser.openBrowserAsync(fileUrl); + } catch (error) { + ToastAndroid.show(t("Xatolik yuz berdi"), ToastAndroid.TOP); + } + }; + const onRefresh = async () => { setRefreshing(true); try { - await queryClient.refetchQueries({ queryKey: ['goverment_category'] }); + await queryClient.refetchQueries({ queryKey: ["goverment_category"] }); } finally { setRefreshing(false); } @@ -52,22 +64,21 @@ export default function EServicesCategoryScreen() { const staticCategory = { id: 0, - name: 'Express Diagnostika', + name: "Express Diagnostika", image: Express_diagnistika, - url: 'https://myorg.uz/ru', + url: "https://myorg.uz/ru", }; const categories = [staticCategory, ...(data?.data?.data?.results ?? [])]; const handlePress = (item: any) => { if (item.id === 0 && item.url) { - setWebUrl(item.url); - setModalVisible(true); + handleOpenBrowser(item.url); return; } router.push({ - pathname: '/(dashboard)/e-service/e-services-category', + pathname: "/(dashboard)/e-service/e-services-category", params: { categoryId: item.id, categoryName: item.name, @@ -79,7 +90,7 @@ export default function EServicesCategoryScreen() { @@ -91,8 +102,8 @@ export default function EServicesCategoryScreen() { } renderItem={({ item }) => ( @@ -101,13 +112,13 @@ export default function EServicesCategoryScreen() { onPress={() => handlePress(item)} style={{ marginHorizontal: 1, - backgroundColor: isDark ? '#FDFDFD' : '#ffffff', + backgroundColor: isDark ? "#FDFDFD" : "#ffffff", borderRadius: 16, - flexDirection: 'row', - alignItems: 'center', + flexDirection: "row", + alignItems: "center", borderWidth: isDark ? 1 : 0, - borderColor: isDark ? dark.border : 'transparent', - shadowColor: '#000', + borderColor: isDark ? dark.border : "transparent", + shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.15, shadowRadius: 4, @@ -116,98 +127,31 @@ export default function EServicesCategoryScreen() { > {item.image ? ( ) : ( - {item.name[0]} + + {item.name[0]} + )} )} /> - - - {/* WebView Header */} - - webviewRef.current?.goBack()} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} - > - - - - - {webUrl} - - - setModalVisible(false)} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} - > - - - - - {/* WebView */} - {webUrl && ( - { - // iOS va Android uchun barcha URLlarni WebView ichida ochish - return true; - }} - source={{ uri: webUrl }} - style={{ flex: 1, backgroundColor: isDark ? '#0f172a' : '#f8fafc' }} - startInLoadingState - renderLoading={() => ( - - )} - /> - )} - - ); } diff --git a/screens/e-services/ui/EServicesScreen.tsx b/screens/e-services/ui/EServicesScreen.tsx index 71dfe39..66cd54b 100644 --- a/screens/e-services/ui/EServicesScreen.tsx +++ b/screens/e-services/ui/EServicesScreen.tsx @@ -1,28 +1,26 @@ -// EServicesScreen.tsx -import { useTheme } from '@/components/ThemeContext'; -import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; -import { Image } from 'expo-image'; -import { router, useLocalSearchParams } from 'expo-router'; -import { ChevronLeft, XIcon } from 'lucide-react-native'; -import React, { useCallback, useRef, useState } from 'react'; +import { useTheme } from "@/components/ThemeContext"; +import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; +import { Image } from "expo-image"; +import { router, useLocalSearchParams } from "expo-router"; +import { ChevronLeft } from "lucide-react-native"; +import { useCallback, useState } from "react"; import { ActivityIndicator, Dimensions, FlatList, - Modal, StyleSheet, Text, + ToastAndroid, TouchableOpacity, View, -} from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; -import { WebView } from 'react-native-webview'; +} from "react-native"; +import * as WebBrowser from "expo-web-browser"; -import { useTranslation } from 'react-i18next'; -import { RefreshControl } from 'react-native-gesture-handler'; -import { eservices_api } from '../lib/api'; +import { useTranslation } from "react-i18next"; +import { RefreshControl } from "react-native-gesture-handler"; +import { eservices_api } from "../lib/api"; -const { width: SCREEN_WIDTH } = Dimensions.get('window'); +const { width: SCREEN_WIDTH } = Dimensions.get("window"); const PAGE_SIZE = 10; export interface GovermentServiceDataRes { @@ -34,78 +32,100 @@ export interface GovermentServiceDataRes { export default function EServicesScreen() { const { isDark } = useTheme(); - const [webUrl, setWebUrl] = useState(null); - const [modalVisible, setModalVisible] = useState(false); - const webviewRef = useRef(null); const params = useLocalSearchParams(); const { t } = useTranslation(); const [refreshing, setRefreshing] = useState(false); const queryClient = useQueryClient(); - const { data, isLoading, isError, fetchNextPage, hasNextPage, isFetchingNextPage } = - useInfiniteQuery({ - queryKey: ['goverment_service', params.categoryId], - queryFn: async ({ pageParam = 1 }) => { - const response = await eservices_api.list({ - page: pageParam, - page_size: PAGE_SIZE, - category: Number(params.categoryId), - }); - return response.data.data; - }, - getNextPageParam: (lastPage) => - lastPage.current_page < lastPage.total_pages ? lastPage.current_page + 1 : undefined, - initialPageParam: 1, - }); + const { + data, + isLoading, + isError, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + } = useInfiniteQuery({ + queryKey: ["goverment_service", params.categoryId], + queryFn: async ({ pageParam = 1 }) => { + const response = await eservices_api.list({ + page: pageParam, + page_size: PAGE_SIZE, + category: Number(params.categoryId), + }); + return response.data.data; + }, + getNextPageParam: (lastPage) => + lastPage.current_page < lastPage.total_pages + ? lastPage.current_page + 1 + : undefined, + initialPageParam: 1, + }); - const services: GovermentServiceDataRes[] = data?.pages.flatMap((p) => p.results) ?? []; + const services: GovermentServiceDataRes[] = + data?.pages.flatMap((p) => p.results) ?? []; const onRefresh = async () => { setRefreshing(true); try { - await queryClient.refetchQueries({ queryKey: ['goverment_service'] }); + await queryClient.refetchQueries({ queryKey: ["goverment_service"] }); } finally { setRefreshing(false); } }; - const openWebView = (url: string) => { - setWebUrl(url); - setModalVisible(true); + const handleOpenBrowser = async (fileUrl: string) => { + ToastAndroid.show(t("Xatolik yuz berdi"), ToastAndroid.TOP); + try { + await WebBrowser.openBrowserAsync(fileUrl); + } catch (error) { + ToastAndroid.show(t("Xatolik yuz berdi"), ToastAndroid.TOP); + } }; const renderItem = useCallback( ({ item }: { item: GovermentServiceDataRes }) => ( - - {/* Logo (bosilganda WebView ochiladi) */} + openWebView(item.url)} + onPress={() => handleOpenBrowser(item.url)} > - + {/* Name (alog‘ifa, faqat ko‘rsatish) */} {item.name} ), - [isDark] + [isDark], ); if (isLoading) { return ( - + ); @@ -113,40 +133,45 @@ export default function EServicesScreen() { if (isError) { return ( - - Xatolik yuz berdi + + Xatolik yuz berdi ); } return ( - + {/* Header */} router.back()} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} > - + {params.categoryName} @@ -158,18 +183,18 @@ export default function EServicesScreen() { {t("Bu kategoriya bo'yicha xizmat topilmadi")} {t("Tez orada xizmat qo'shiladi")} @@ -182,96 +207,34 @@ export default function EServicesScreen() { } keyExtractor={(item) => item.id.toString()} renderItem={renderItem} numColumns={3} - columnWrapperStyle={{ justifyContent: 'space-between', marginBottom: 12 }} + columnWrapperStyle={{ + justifyContent: "space-between", + marginBottom: 12, + }} contentContainerStyle={{ padding: 16, paddingBottom: 80 }} - onEndReached={() => hasNextPage && !isFetchingNextPage && fetchNextPage()} + onEndReached={() => + hasNextPage && !isFetchingNextPage && fetchNextPage() + } onEndReachedThreshold={0.4} ListFooterComponent={ isFetchingNextPage ? ( - + ) : null } showsVerticalScrollIndicator={false} /> )} - - {/* WebView Modal */} - - - {/* WebView Header */} - - webviewRef.current?.goBack()} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} - > - - - - - {webUrl} - - - setModalVisible(false)} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} - > - - - - - {/* WebView */} - {webUrl && ( - { - // iOS va Android uchun barcha URLlarni WebView ichida ochish - return true; - }} - source={{ uri: webUrl }} - style={{ flex: 1, backgroundColor: isDark ? '#0f172a' : '#f8fafc' }} - startInLoadingState - renderLoading={() => ( - - )} - /> - )} - - ); } @@ -279,12 +242,12 @@ export default function EServicesScreen() { const CARD_WIDTH = (SCREEN_WIDTH - 50) / 3; const styles = StyleSheet.create({ - center: { flex: 1, justifyContent: 'center', alignItems: 'center' }, + center: { flex: 1, justifyContent: "center", alignItems: "center" }, card: { width: CARD_WIDTH, borderRadius: 12, padding: 12, - alignItems: 'center', + alignItems: "center", }, logo: { width: 80, @@ -293,18 +256,18 @@ const styles = StyleSheet.create({ }, name: { fontSize: 14, - fontWeight: '600', - textAlign: 'center', + fontWeight: "600", + textAlign: "center", }, darkShadow: { - shadowColor: '#000', + shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.4, shadowRadius: 6, elevation: 3, }, lightShadow: { - shadowColor: '#000', + shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4,