From 415cf274b1dd878b34361be3609135ae7fcc39d5 Mon Sep 17 00:00:00 2001 From: Benoit Martins Date: Fri, 17 May 2024 11:17:00 +0200 Subject: [PATCH] Fix conversation for iOS 15 --- .../Fragments/ConversationFragment.swift | 127 ++++++++++++------ .../Main/Conversations/Fragments/UIList.swift | 19 +-- .../ViewModel/ConversationViewModel.swift | 1 - Linphone/Utils/Avatar.swift | 2 + 4 files changed, 95 insertions(+), 54 deletions(-) diff --git a/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift b/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift index c8bd4af36..011f85a35 100644 --- a/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift +++ b/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift @@ -42,6 +42,8 @@ struct ConversationFragment: View { @StateObject private var viewModel = ChatViewModel() @StateObject private var paginationState = PaginationState() + @State private var displayFloatingButton = false + var body: some View { NavigationView { GeometryReader { geometry in @@ -158,8 +160,7 @@ struct ConversationFragment: View { isScrolledToBottom: $isScrolledToBottom, showMessageMenuOnLongPress: showMessageMenuOnLongPress, geometryProxy: geometry, - sections: conversationViewModel.conversationMessagesSection, - ids: conversationViewModel.conversationMessagesIds + sections: conversationViewModel.conversationMessagesSection ) if !isScrolledToBottom { @@ -217,56 +218,99 @@ struct ConversationFragment: View { conversationViewModel.resetMessage() } } else { - /* ScrollViewReader { proxy in - List { - ForEach(0.. conversationViewModel.conversationMessagesList.count { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - conversationViewModel.getOldMessages() - } - } - } + ZStack(alignment: .bottomTrailing) { + List { + if conversationViewModel.conversationMessagesSection.first != nil { + let counter = conversationViewModel.conversationMessagesSection.first!.rows.count + ForEach(0.. conversationViewModel.conversationMessagesSection.first!.rows.count { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + conversationViewModel.getOldMessages() + } + } + + if index == 0 { + displayFloatingButton = false + } + } + .onDisappear { + if index == 0 { + displayFloatingButton = true + } + } + } } } + .scaleEffect(x: 1, y: -1, anchor: .center) + .listStyle(.plain) + + if displayFloatingButton { + Button { + if conversationViewModel.conversationMessagesSection.first != nil && conversationViewModel.conversationMessagesSection.first!.rows.first != nil { + withAnimation { + proxy.scrollTo(conversationViewModel.conversationMessagesSection.first!.rows.first!.id) + } + } + } label: { + ZStack { + + Image("caret-down") + .renderingMode(.template) + .foregroundStyle(.white) + .padding() + .background(Color.orangeMain500) + .clipShape(Circle()) + .shadow(color: .black.opacity(0.2), radius: 4) + + if conversationViewModel.displayedConversationUnreadMessagesCount > 0 { + VStack { + HStack { + Spacer() + + HStack { + Text( + conversationViewModel.displayedConversationUnreadMessagesCount < 99 + ? String(conversationViewModel.displayedConversationUnreadMessagesCount) + : "99+" + ) + .foregroundStyle(.white) + .default_text_style(styleSize: 10) + .lineLimit(1) + + } + .frame(width: 18, height: 18) + .background(Color.redDanger500) + .cornerRadius(50) + } + + Spacer() + } + } + } + + } + .frame(width: 50, height: 50) + .padding() + } } - .scaleEffect(x: 1, y: -1, anchor: .center) - .listStyle(.plain) .onTapGesture { UIApplication.shared.endEditing() } .onAppear { conversationViewModel.getMessages() } - .onChange(of: conversationViewModel.conversationMessagesList) { _ in - /* - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - if conversationViewModel.conversationMessagesList.count <= 30 { - proxy.scrollTo( - conversationViewModel.conversationMessagesList.first, anchor: .top - ) - } else if conversationViewModel.conversationMessagesList.count >= conversationViewModel.displayedConversationHistorySize { - proxy.scrollTo( - conversationViewModel.conversationMessagesList[conversationViewModel.displayedConversationHistorySize%30], anchor: .top - ) - } else { - proxy.scrollTo(30, anchor: .top) - } - } - */ - } .onDisappear { conversationViewModel.resetMessage() } } - */ } HStack(spacing: 0) { @@ -399,6 +443,13 @@ struct ConversationFragment: View { } } +struct ScrollOffsetPreferenceKey: PreferenceKey { + static var defaultValue: CGPoint = .zero + + static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) { + } +} + extension UIApplication { func endEditing() { sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) diff --git a/Linphone/UI/Main/Conversations/Fragments/UIList.swift b/Linphone/UI/Main/Conversations/Fragments/UIList.swift index 14fdfbf56..6bae3da69 100644 --- a/Linphone/UI/Main/Conversations/Fragments/UIList.swift +++ b/Linphone/UI/Main/Conversations/Fragments/UIList.swift @@ -35,7 +35,6 @@ struct UIList: UIViewRepresentable { let showMessageMenuOnLongPress: Bool let geometryProxy: GeometryProxy let sections: [MessagesSection] - let ids: [String] @State private var isScrolledToTop = false @@ -153,7 +152,6 @@ struct UIList: UIViewRepresentable { // apply the rest of the changes to table's dataSource, i.e. inserts //print("5 apply inserts") context.coordinator.sections = sections - context.coordinator.ids = ids tableView.beginUpdates() for operation in insertOperations { @@ -164,7 +162,6 @@ struct UIList: UIViewRepresentable { updateSemaphore.signal() } } else { - context.coordinator.ids = ids updateSemaphore.signal() } } @@ -312,8 +309,7 @@ struct UIList: UIViewRepresentable { isScrolledToTop: $isScrolledToTop, showMessageMenuOnLongPress: showMessageMenuOnLongPress, geometryProxy: geometryProxy, - sections: sections, - ids: ids + sections: sections ) } @@ -329,9 +325,8 @@ struct UIList: UIViewRepresentable { let showMessageMenuOnLongPress: Bool let geometryProxy: GeometryProxy var sections: [MessagesSection] - var ids: [String] - init(conversationViewModel: ConversationViewModel, viewModel: ChatViewModel, paginationState: PaginationState, isScrolledToBottom: Binding, isScrolledToTop: Binding, showMessageMenuOnLongPress: Bool, geometryProxy: GeometryProxy, sections: [MessagesSection], ids: [String]) { + init(conversationViewModel: ConversationViewModel, viewModel: ChatViewModel, paginationState: PaginationState, isScrolledToBottom: Binding, isScrolledToTop: Binding, showMessageMenuOnLongPress: Bool, geometryProxy: GeometryProxy, sections: [MessagesSection]) { self.conversationViewModel = conversationViewModel self.viewModel = viewModel self.paginationState = paginationState @@ -340,7 +335,6 @@ struct UIList: UIViewRepresentable { self.showMessageMenuOnLongPress = showMessageMenuOnLongPress self.geometryProxy = geometryProxy self.sections = sections - self.ids = ids } func numberOfSections(in tableView: UITableView) -> Int { @@ -384,8 +378,6 @@ struct UIList: UIViewRepresentable { } .minSize(width: 0, height: 0) .margins(.all, 0) - } else { - // Fallback on earlier versions } tableViewCell.transform = CGAffineTransformMakeScale(1, -1) @@ -395,7 +387,7 @@ struct UIList: UIViewRepresentable { func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { let row = sections[indexPath.section].rows[indexPath.row] - paginationState.handle(row, ids: ids) + paginationState.handle(row) } func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -452,13 +444,10 @@ final class PaginationState: ObservableObject { self.offset = offset } - func handle(_ message: Message, ids: [String]) { + func handle(_ message: Message) { guard shouldHandlePagination else { return } - if ids.prefix(offset + 1).contains(message.id) { - onEvent?(message) - } } } diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift index a717bd6fb..ba6ffab8a 100644 --- a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift +++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift @@ -36,7 +36,6 @@ class ConversationViewModel: ObservableObject { private var chatRoomSuscriptions = Set() - @Published var conversationMessagesIds: [String] = [] @Published var conversationMessagesSection: [MessagesSection] = [] @Published var participantConversationModel: [ContactAvatarModel] = [] diff --git a/Linphone/Utils/Avatar.swift b/Linphone/Utils/Avatar.swift index 768122219..deb0c4724 100644 --- a/Linphone/Utils/Avatar.swift +++ b/Linphone/Utils/Avatar.swift @@ -21,6 +21,7 @@ import SwiftUI import linphonesw struct Avatar: View { + @State var id = UUID() private var contactsManager = ContactsManager.shared @@ -73,6 +74,7 @@ struct Avatar: View { EmptyView() } } + .id(id) } else if !contactAvatarModel.name.isEmpty { Image(uiImage: contactsManager.textToImage( firstName: contactAvatarModel.name,