Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
18 kB
1
Indexable
Never
import SwiftUI
import CoreLocation

struct ContentView: View {
    @StateObject private var locationManager = LocationManager()
    @State private var restaurants: [String] = []
    @State private var currentIndex: Int = 0
    @State private var isMenuOpen: Bool = false
    @ObservedObject private var favoritesManager = FavoritesManager()
    @State private var showFavorites: Bool = false
    @GestureState private var dragState: DragState = .inactive
    @State private var swipedRightIndices: Set<Int> = []
    @State private var swipedIndices: Set<Int> = []
    @State private var searchRadius: Int = 1500
    @State private var showNoMoreRestaurants: Bool = false
    
    
    var body: some View {
        ZStack {
            VStack {
                if !restaurants.isEmpty {
                    GeometryReader { geometry in
                        ZStack {
                            if !swipedIndices.contains(currentIndex) {
                                let components = restaurants[currentIndex].components(separatedBy: "|")
                                let distance = Double(components[2]) ?? 0.0
                                let rating = Float(components[3]) ?? 0.0
                                
                                RestaurantCard(restaurantInfo: restaurants[currentIndex], distance: distance, cardWidth: geometry.size.width * 0.9, cardHeight: geometry.size.height * 0.6, favoritesManager: favoritesManager)
                                    .offset(x: dragState.translation.width, y: dragState.translation.height)
                                    .animation(.spring(response: 0.6, dampingFraction: 0.6))
                                    .zIndex(1)
                                    .position(x: geometry.size.width / 2, y: geometry.size.height / 2)
                                    .gesture(
                                        DragGesture()
                                            .updating($dragState) { value, state, transaction in
                                                state = .active(translation: value.translation)
                                            }
                                            .onEnded { value in
                                                if value.translation.width < -50 || value.translation.width > 50 {
                                                    if value.translation.width > 50 {
                                                        favoritesManager.addFavorite(restaurant: restaurants[currentIndex])
                                                    }
                                                    swipedIndices.insert(currentIndex)
                                                    currentIndex += 1
                                                    if currentIndex == restaurants.count {
                                                        showNoMoreRestaurants = true
                                                    }
                                                }
                                            }
                                    )
                            }
                        }
                        
                    }
                } else {
                    if showNoMoreRestaurants {
                        VStack {
                            Text("No more restaurants nearby, increase radius?")
                                .font(.title2)
                                .multilineTextAlignment(.center)
                                .padding(.bottom, 10)
                            
                            Button(action: {
                                searchRadius += 1500
                                fetchNearbyRestaurants(latitude: locationManager.lastLocation?.coordinate.latitude ?? 0,
                                                       longitude: locationManager.lastLocation?.coordinate.longitude ?? 0)
                            }) {
                                Text("Find More")
                                    .padding(.horizontal, 24)
                                    .padding(.vertical, 12)
                                    .foregroundColor(.white)
                                    .background(Color.blue)
                                    .cornerRadius(8)
                            }
                        }
                    } else {
                        Text("Loading restaurants...")
                    }
                }
            }
            VStack {
                Spacer()
                HamburgerMenu(isOpen: $isMenuOpen, showFavorites: $showFavorites)
            }
            if showFavorites {
                NavigationView {
                    List(favoritesManager.favorites, id: \.self) { favorite in
                        let components = favorite.components(separatedBy: "|")
                        let restaurantName = components[0]
                        Text(restaurantName)
                    }
                    .navigationBarTitle("Favorites", displayMode: .inline)
                    .toolbar {
                        Button("Done") {
                            showFavorites = false
                        }
                    }
                }
            }
        }
        .onAppear {
            locationManager.requestAuthorization()
            
            
        }
        
        .onChange(of: locationManager.lastLocation) { location in
            if let location = location {
                fetchNearbyRestaurants(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
            }
        }
    }
    
    
    
    // MARK: - Fetch Nearby Restaurants
    
    func fetchNearbyRestaurants(latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
        let apiKey = "AIzaSyCWUFGPrVuiFqwqWhwvrw9Glf9HNlkuvM0"
        let urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(latitude),\(longitude)&radius=\(searchRadius)&type=restaurant&key=\(apiKey)"
        
        guard let url = URL(string: urlString) else {
            print("Invalid URL")
            return
        }
        
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data {
                do {
                    let jsonDecoder = JSONDecoder()
                    let response = try jsonDecoder.decode(GooglePlacesResponse.self, from: data)
                    DispatchQueue.main.async {
                        if response.results.count < 10 && searchRadius < 10000 {
                            fetchNearbyRestaurants(latitude: latitude, longitude: longitude)
                        } else {
                            let userLocation = CLLocation(latitude: latitude, longitude: longitude)
                            restaurants = response.results.map { result in
                                let restaurantLocation = CLLocation(latitude: result.geometry.location.lat, longitude: result.geometry.location.lng)
                                let distanceInMiles = restaurantLocation.distance(from: userLocation) * 0.000621371
                                let rating = result.rating ?? 0.0
                                if let photoReference = result.photos?.first?.photo_reference {
                                    let photoUrl = "https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=\(photoReference)&key=\(apiKey)"
                                    return "\(result.name)|\(photoUrl)|\(distanceInMiles)|\(rating)"
                                } else {
                                    return "\(result.name)||\(distanceInMiles)|\(rating)"
                                }
                                
                            }
                        }
                    } catch {
                        print("Error decoding JSON: \(error)")
                    }
                } else {
                    print("Error fetching nearby restaurants: \(error?.localizedDescription ?? "Unknown error")")
                }
            }.resume()
        }
        
        struct NoMoreRestaurantsView: View {
            @Binding var searchRadius: Int
            let fetchNearbyRestaurants: (CLLocationDegrees, CLLocationDegrees) -> Void
            let locationManager: LocationManager
            
            var body: some View {
                VStack {
                    Text("No more restaurants nearby, increase radius?")
                        .font(.title2)
                        .multilineTextAlignment(.center)
                        .padding(.bottom, 10)
                    
                    Button(action: {
                        searchRadius += 1500
                        if let location = locationManager.lastLocation {
                            fetchNearbyRestaurants(location.coordinate.latitude, location.coordinate.longitude)
                        }
                    }) {
                        Text("Find More")
                            .padding(.horizontal, 24)
                            .padding(.vertical, 12)
                            .foregroundColor(.white)
                            .background(Color.blue)
                            .cornerRadius(8)
                    }
                }
            }
        }
        //....RestaurantCard...........................................................................
        
        struct RestaurantCard: View {
            let restaurantInfo: String
            let distance: Double
            let cardWidth: CGFloat
            let cardHeight: CGFloat
            @ObservedObject var favoritesManager: FavoritesManager
            
            var body: some View {
                let components = restaurantInfo.components(separatedBy: "|")
                let restaurantName = components[0]
                let imageUrl = components.count > 1 ? components[1] : ""
                let rating = Float(components.count > 3 ? components[3] : "0") ?? 0.0
                
                VStack {
                    if !imageUrl.isEmpty {
                        AsyncImage(url: URL(string: imageUrl)) { image in
                            image.resizable()
                        } placeholder: {
                            ProgressView()
                        }
                        .frame(maxWidth: .infinity, maxHeight: cardHeight * 2 / 3, alignment: .top)
                        .aspectRatio(contentMode: .fill)
                        .clipped()
                    }
                    
                    VStack(alignment: .leading) {
                        HStack {
                            Text(restaurantName)
                                .font(.custom("Roboto-Thin", size: 24))
                                .foregroundColor(.primary)
                            
                            Spacer()
                            
                            if favoritesManager.isFavorite(restaurant: restaurantInfo) { // Check if the restaurant is a favorite
                                Image(systemName: "heart.fill")
                                    .foregroundColor(.red)
                            }
                        }
                        
                        Text(String(format: "%.2f miles", distance))
                            .font(.footnote)
                            .foregroundColor(.gray)
                            .padding(.bottom)
                        
                        StarsView(rating: rating)
                    }
                    .padding([.leading, .trailing])
                    
                    Spacer()
                    
                }
                .frame(width: cardWidth, height: cardHeight)
                .background(Color(.systemGray5))
                .cornerRadius(10)
                .shadow(radius: 10)
            }
        }
        
        
        
        struct StarsView: View {
            let rating: Float
            
            var body: some View {
                HStack {
                    ForEach(0..<5) { index in
                        Image(systemName: rating >= Float(index + 1) ? "star.fill" : (rating >= Float(index) + 0.5 ? "star.leadinghalf.fill" : "star"))
                            .resizable()
                            .scaledToFit()
                            .frame(height: 16)
                            .foregroundColor(.yellow)
                    }
                }
            }
        }
        
        //....End of RestaurnatCard...........................................................................
        
        //....HamburgerMenu...........................................................................
        
        struct HamburgerMenu: View {
            @Binding var isOpen: Bool
            @Binding var showFavorites: Bool
            
            var body: some View {
                VStack {
                    Spacer()
                    
                    if isOpen {
                        VStack {
                            HStack {
                                Button(action: {
                                    withAnimation {
                                        isOpen.toggle()
                                    }
                                }) {
                                    Image(systemName: "xmark")
                                        .font(.system(size: 20))
                                        .foregroundColor(.primary)
                                        .padding()
                                }
                                Spacer()
                            }
                            
                            Button("Filters") {
                                print("Option 1 selected")
                            }
                            .frame(maxWidth: .infinity, minHeight: 130)
                            .background(Color(.systemGray5))
                            .foregroundColor(.primary)
                            .font(.system(size: 36))
                            .background(Color.clear) // Add this line
                            
                            Button("Favorites") {
                                isOpen = false
                                showFavorites = true
                            }
                            .frame(maxWidth: .infinity, minHeight: 130)
                            .background(Color(.systemGray5))
                            .foregroundColor(.primary)
                            .font(.system(size: 36))
                            .background(Color.clear) // Add this line
                            
                            Button("About") {
                                print("Option 3 selected")
                            }
                            .frame(maxWidth: .infinity, minHeight: 130)
                            .background(Color(.systemGray5))
                            .foregroundColor(.primary)
                            .font(.system(size: 36))
                            .background(Color.clear) // Add this line
                            
                            Spacer()
                        }
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .background(Color(.systemBackground)) // Use systemBackground to adapt to dark and light modes
                        .edgesIgnoringSafeArea(.all)
                        .transition(.move(edge: .bottom))
                    }
                    
                    Button(action: {
                        withAnimation {
                            isOpen.toggle()
                        }
                    }) {
                        Image(systemName: "line.horizontal.3")
                            .font(.system(size: 20))
                            .foregroundColor(.primary)
                            .padding()
                    }
                }
            }
        }
        
        
        
        //....End of HamburgerMenu...........................................................................
        
        
        //....GooglePlacesResponse...........................................................................
        
        struct GooglePlacesResponse: Codable {
            let results: [Place]
            
            struct Place: Codable {
                let name: String
                let photos: [Photo]?
                let geometry: Geometry
                let rating: Float?
                
                struct Geometry: Codable {
                    let location: Location
                    
                    struct Location: Codable {
                        let lat: Double
                        let lng: Double
                    }
                }
                
                struct Photo: Codable {
                    let photo_reference: String
                }
            }
        }
    }
    
    //.... End of GooglePlacesResponse.....................................................................
    
    //.....DragState......................................................................................
    
    enum DragState {
        case inactive
        case active(translation: CGSize)
        
        var translation: CGSize {
            switch self {
            case .inactive:
                return .zero
            case .active(let translation):
                return translation
            }
        }
        
        var isActive: Bool {
            switch self {
            case .inactive:
                return false
            case .active:
                return true
            }
        }
    }
    
    //.....End of DragState.......................................................................................
}