refresh profile
This commit is contained in:
		
							parent
							
								
									8fd955d3cb
								
							
						
					
					
						commit
						a716b296d5
					
				@ -3,10 +3,10 @@ import UIKit
 | 
			
		||||
 | 
			
		||||
struct RefreshableScrollView<Content: View>: UIViewRepresentable {
 | 
			
		||||
    var content: Content
 | 
			
		||||
    var onRefresh: (UIRefreshControl) -> Void
 | 
			
		||||
    var onRefresh: () -> Void
 | 
			
		||||
    var isRefreshing: Binding<Bool>
 | 
			
		||||
 | 
			
		||||
    init(isRefreshing: Binding<Bool>, onRefresh: @escaping (UIRefreshControl) -> Void, @ViewBuilder content: () -> Content) {
 | 
			
		||||
    init(isRefreshing: Binding<Bool>, onRefresh: @escaping () -> Void, @ViewBuilder content: () -> Content) {
 | 
			
		||||
        self.content = content()
 | 
			
		||||
        self.onRefresh = onRefresh
 | 
			
		||||
        self.isRefreshing = isRefreshing
 | 
			
		||||
@ -15,17 +15,14 @@ struct RefreshableScrollView<Content: View>: UIViewRepresentable {
 | 
			
		||||
    func makeUIView(context: Context) -> UIScrollView {
 | 
			
		||||
        let scrollView = UIScrollView()
 | 
			
		||||
        
 | 
			
		||||
        // Создаем UIRefreshControl и добавляем его
 | 
			
		||||
        let refreshControl = UIRefreshControl()
 | 
			
		||||
        refreshControl.addTarget(context.coordinator, action: #selector(Coordinator.handleRefresh), for: .valueChanged)
 | 
			
		||||
        scrollView.refreshControl = refreshControl
 | 
			
		||||
 | 
			
		||||
        // Создаем хостинг для нашего SwiftUI контента
 | 
			
		||||
        let hostingController = UIHostingController(rootView: content)
 | 
			
		||||
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
 | 
			
		||||
        scrollView.addSubview(hostingController.view)
 | 
			
		||||
 | 
			
		||||
        // Настраиваем Auto Layout
 | 
			
		||||
        NSLayoutConstraint.activate([
 | 
			
		||||
            hostingController.view.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
 | 
			
		||||
            hostingController.view.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
 | 
			
		||||
@ -40,14 +37,15 @@ struct RefreshableScrollView<Content: View>: UIViewRepresentable {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func updateUIView(_ uiView: UIScrollView, context: Context) {
 | 
			
		||||
        // Обновляем состояние индикатора
 | 
			
		||||
        if isRefreshing.wrappedValue {
 | 
			
		||||
            uiView.refreshControl?.beginRefreshing()
 | 
			
		||||
        } else {
 | 
			
		||||
            uiView.refreshControl?.endRefreshing()
 | 
			
		||||
            // Отложенное завершение, чтобы избежать цикла обновлений
 | 
			
		||||
            DispatchQueue.main.async {
 | 
			
		||||
                uiView.refreshControl?.endRefreshing()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Обновляем SwiftUI View, если нужно
 | 
			
		||||
        context.coordinator.hostingController?.rootView = content
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -63,9 +61,8 @@ struct RefreshableScrollView<Content: View>: UIViewRepresentable {
 | 
			
		||||
            self.parent = parent
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @objc func handleRefresh(_ sender: UIRefreshControl) {
 | 
			
		||||
            parent.isRefreshing.wrappedValue = true
 | 
			
		||||
            parent.onRefresh(sender)
 | 
			
		||||
        @objc func handleRefresh() {
 | 
			
		||||
            parent.onRefresh()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ struct HomeTab: View {
 | 
			
		||||
                if isLoading {
 | 
			
		||||
                    ProgressView("Загрузка ленты...")
 | 
			
		||||
                } else {
 | 
			
		||||
                    RefreshableScrollView(isRefreshing: $isRefreshing, onRefresh: { _ in
 | 
			
		||||
                    RefreshableScrollView(isRefreshing: $isRefreshing, onRefresh: {
 | 
			
		||||
                        fetchData()
 | 
			
		||||
                    }) {
 | 
			
		||||
                        LazyVStack(spacing: 24) {
 | 
			
		||||
 | 
			
		||||
@ -2,12 +2,12 @@ import SwiftUI
 | 
			
		||||
 | 
			
		||||
struct ProfileContentTabbedGrid: View {
 | 
			
		||||
    let isContentLoaded: Bool
 | 
			
		||||
    @Binding var allPosts: [Post]
 | 
			
		||||
    @Binding var isLoading: Bool
 | 
			
		||||
    @State private var selectedTabIndex = 0
 | 
			
		||||
    @State private var selectedCategory = "#все"
 | 
			
		||||
    @State private var searchQuery = ""
 | 
			
		||||
    @State private var selectedSort = "По дате"
 | 
			
		||||
    @State private var allPosts: [Post] = []
 | 
			
		||||
    @State private var isLoading = true
 | 
			
		||||
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        VStack(spacing: 0) {
 | 
			
		||||
@ -73,15 +73,6 @@ struct ProfileContentTabbedGrid: View {
 | 
			
		||||
                isLoading: isLoading
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        .onAppear {
 | 
			
		||||
            if allPosts.isEmpty {
 | 
			
		||||
                isLoading = true
 | 
			
		||||
                PostService.shared.fetchAllPosts { result in
 | 
			
		||||
                    self.allPosts = result
 | 
			
		||||
                    self.isLoading = false
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // MARK: - Вкладка с меню
 | 
			
		||||
 | 
			
		||||
@ -2,13 +2,6 @@ import SwiftUI
 | 
			
		||||
 | 
			
		||||
struct ProfileTab: View {
 | 
			
		||||
    @ObservedObject var viewModel: LoginViewModel
 | 
			
		||||
//    @State private var selectedTabIndex = 0
 | 
			
		||||
//    @State private var selectedSort = "По дате"
 | 
			
		||||
//    @State private var selectedCategory = "#все"
 | 
			
		||||
//    @State private var searchQuery = ""
 | 
			
		||||
//    @State private var searchQueryArchive = ""
 | 
			
		||||
//    @State private var searchQuerySaved = ""
 | 
			
		||||
//    @State private var searchQueryLiked = ""
 | 
			
		||||
    @State private var isContentLoaded = true
 | 
			
		||||
 | 
			
		||||
    @State private var accounts = ["@user1", "@user2", "@user3"]
 | 
			
		||||
@ -20,84 +13,93 @@ struct ProfileTab: View {
 | 
			
		||||
    @State private var sheetType: SheetType? = nil
 | 
			
		||||
    enum SheetType: Identifiable {
 | 
			
		||||
        case accountShare
 | 
			
		||||
        var id: Int { self.hashValue }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        var id: Int {
 | 
			
		||||
            switch self {
 | 
			
		||||
            case .accountShare: return 1
 | 
			
		||||
    @State private var allPosts: [Post] = []
 | 
			
		||||
    @State private var isLoading = true
 | 
			
		||||
    @State private var isRefreshing = false
 | 
			
		||||
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        NavigationView {
 | 
			
		||||
            if !isContentLoaded {
 | 
			
		||||
                SplashScreenView()
 | 
			
		||||
            } else {
 | 
			
		||||
                RefreshableScrollView(isRefreshing: $isRefreshing, onRefresh: {
 | 
			
		||||
                    fetchData()
 | 
			
		||||
                }) {
 | 
			
		||||
                    VStack(spacing: 0) {
 | 
			
		||||
                        header
 | 
			
		||||
                            .frame(minHeight: 300)
 | 
			
		||||
                            .frame(maxWidth: .infinity)
 | 
			
		||||
                            .background(Color(.systemBackground))
 | 
			
		||||
                        
 | 
			
		||||
                        ProfileContentTabbedGrid(
 | 
			
		||||
                            isContentLoaded: isContentLoaded,
 | 
			
		||||
                            allPosts: $allPosts,
 | 
			
		||||
                            isLoading: $isLoading
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .navigationBarTitleDisplayMode(.inline)
 | 
			
		||||
                .toolbar {
 | 
			
		||||
                    ToolbarItem(placement: .principal) {
 | 
			
		||||
                        Button(action: { sheetType = .accountShare }) {
 | 
			
		||||
                            HStack(spacing: 4) {
 | 
			
		||||
                                Text("custom_user_name")
 | 
			
		||||
                                    .font(.headline)
 | 
			
		||||
                                    .foregroundColor(.primary)
 | 
			
		||||
                                Image(systemName: "chevron.down")
 | 
			
		||||
                                    .font(.subheadline)
 | 
			
		||||
                                    .foregroundColor(.gray)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    ToolbarItem(placement: .navigationBarTrailing) {
 | 
			
		||||
                        NavigationLink(destination: SettingsView(viewModel: viewModel)) {
 | 
			
		||||
                            Image(systemName: "wrench")
 | 
			
		||||
                                .imageScale(.large)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .sheet(item: $sheetType) { type in
 | 
			
		||||
                    switch type {
 | 
			
		||||
                    case .accountShare:
 | 
			
		||||
                        AccountShareSheet(
 | 
			
		||||
                            isPresented: Binding(
 | 
			
		||||
                                get: { sheetType != nil },
 | 
			
		||||
                                set: { if !$0 { sheetType = nil } }
 | 
			
		||||
                            ),
 | 
			
		||||
                            selectedAccount: $selectedAccount,
 | 
			
		||||
                            accounts: accounts
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .navigationViewStyle(StackNavigationViewStyle())
 | 
			
		||||
        .onAppear {
 | 
			
		||||
            if allPosts.isEmpty {
 | 
			
		||||
                fetchData(isInitialLoad: true)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        NavigationView {
 | 
			
		||||
            if !isContentLoaded{
 | 
			
		||||
                SplashScreenView()
 | 
			
		||||
            } else {
 | 
			
		||||
            GeometryReader { geometry in
 | 
			
		||||
                VStack(spacing: 0) {
 | 
			
		||||
                    ScrollView {
 | 
			
		||||
                        // ───── Шапка профиля (занимает ~50% экрана) ─────
 | 
			
		||||
                        header
 | 
			
		||||
                            .frame(minHeight: geometry.size.height * 0.5)
 | 
			
		||||
                            .frame(maxWidth: .infinity)
 | 
			
		||||
                            .background(Color(.systemBackground))
 | 
			
		||||
 | 
			
		||||
                        // ───── Скролл с контентом ─────
 | 
			
		||||
                    
 | 
			
		||||
                        ProfileContentTabbedGrid(isContentLoaded: isContentLoaded)
 | 
			
		||||
                        
 | 
			
		||||
                    }
 | 
			
		||||
                    .frame(maxWidth: .infinity)
 | 
			
		||||
                }
 | 
			
		||||
                .frame(maxWidth: .infinity, maxHeight: .infinity)
 | 
			
		||||
            }
 | 
			
		||||
            .navigationBarTitleDisplayMode(.inline)
 | 
			
		||||
            .toolbar {
 | 
			
		||||
                ToolbarItem(placement: .principal) {
 | 
			
		||||
                    Button(action: {
 | 
			
		||||
                        sheetType = .accountShare
 | 
			
		||||
                    }) {
 | 
			
		||||
                        HStack(spacing: 4) {
 | 
			
		||||
                            Text("custom_user_name")
 | 
			
		||||
                                .font(.headline)
 | 
			
		||||
                                .foregroundColor(.primary)
 | 
			
		||||
                            Image(systemName: "chevron.down")
 | 
			
		||||
                                .font(.subheadline)
 | 
			
		||||
                                .foregroundColor(.gray)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ToolbarItem(placement: .navigationBarTrailing) {
 | 
			
		||||
                    NavigationLink(destination: SettingsView(viewModel: viewModel)) {
 | 
			
		||||
                        Image(systemName: "wrench")
 | 
			
		||||
                            .imageScale(.large)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .sheet(item: $sheetType) { type in
 | 
			
		||||
                switch type {
 | 
			
		||||
                case .accountShare:
 | 
			
		||||
                    AccountShareSheet(
 | 
			
		||||
                        isPresented: Binding(
 | 
			
		||||
                            get: { sheetType != nil },
 | 
			
		||||
                            set: { if !$0 { sheetType = nil } }
 | 
			
		||||
                        ),
 | 
			
		||||
                        selectedAccount: $selectedAccount,
 | 
			
		||||
                        accounts: accounts
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
    private func fetchData(isInitialLoad: Bool = false) {
 | 
			
		||||
        if isInitialLoad {
 | 
			
		||||
            isLoading = true
 | 
			
		||||
        } else {
 | 
			
		||||
            isRefreshing = true
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        PostService.shared.fetchAllPosts { fetchedPosts in
 | 
			
		||||
            self.allPosts = fetchedPosts
 | 
			
		||||
            
 | 
			
		||||
            if isInitialLoad {
 | 
			
		||||
                self.isLoading = false
 | 
			
		||||
            }
 | 
			
		||||
            self.isRefreshing = false
 | 
			
		||||
        }
 | 
			
		||||
        .navigationViewStyle(StackNavigationViewStyle())
 | 
			
		||||
//        .onAppear {
 | 
			
		||||
//            if !isContentLoaded {
 | 
			
		||||
//                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
 | 
			
		||||
//                    isContentLoaded = true
 | 
			
		||||
//                }
 | 
			
		||||
//            }
 | 
			
		||||
//        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // MARK: - Шапка профиля
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user