ios: programmatic navigation between list/chat (#980)

* ios: programmatic navigation between list/chat

* prevent chat info sheet from showing when switching conversation

* add direct chat with member to model

* set status to connected

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
This commit is contained in:
Evgeny Poberezkin 2022-08-29 14:08:46 +01:00 committed by GitHub
parent 7343e4a51a
commit 51a2fa8c28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 66 additions and 27 deletions

View file

@ -59,6 +59,16 @@ final class ChatModel: ObservableObject {
chats.first(where: { $0.id == id })
}
func getContactChat(_ contactId: Int64) -> Chat? {
chats.first { chat in
if case let .direct(contact) = chat.chatInfo {
return contact.contactId == contactId
} else {
return false
}
}
}
private func getChatIndex(_ id: String) -> Int? {
chats.firstIndex(where: { $0.id == id })
}

View file

@ -206,8 +206,9 @@ func apiGetChatItems(type: ChatType, id: Int64, pagination: ChatPagination, sear
func loadChat(chat: Chat, search: String = "") {
do {
let cInfo = chat.chatInfo
let chat = try apiGetChat(type: cInfo.chatType, id: cInfo.apiId, search: search)
let m = ChatModel.shared
m.reversedChatItems = []
let chat = try apiGetChat(type: cInfo.chatType, id: cInfo.apiId, search: search)
m.updateChatInfo(chat.chatInfo)
m.reversedChatItems = chat.chatItems.reversed()
} catch let error {

View file

@ -216,6 +216,7 @@ struct ChatInfoView: View {
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId)
await MainActor.run {
chatModel.removeChat(chat.chatInfo.id)
chatModel.chatId = nil
dismiss()
}
} catch let error {

View file

@ -72,7 +72,10 @@ struct ChatView: View {
}
}
} label: {
Image(systemName: "chevron.backward")
HStack(spacing: 0) {
Image(systemName: "chevron.backward")
Text("Chats")
}
}
}
ToolbarItem(placement: .principal) {
@ -235,6 +238,15 @@ struct ChatView: View {
.onChange(of: searchText) { _ in
loadChat(chat: chat, search: searchText)
}
.onChange(of: chatModel.chatId) { _ in
if let chatId = chatModel.chatId, let chat = chatModel.getChat(chatId) {
showChatInfoSheet = false
loadChat(chat: chat)
DispatchQueue.main.async {
scrollToBottom(proxy)
}
}
}
}
}
.scaleEffect(x: 1, y: -1, anchor: .center)

View file

@ -221,6 +221,7 @@ struct GroupChatInfoView: View {
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId)
await MainActor.run {
chatModel.removeChat(chat.chatInfo.id)
chatModel.chatId = nil
dismiss()
}
} catch let error {

View file

@ -30,11 +30,11 @@ struct GroupMemberInfoView: View {
groupMemberInfoHeader()
.listRowBackground(Color.clear)
// if let contactId = member.memberContactId {
// Section {
// openDirectChatButton(contactId)
// }
// }
if let contactId = member.memberContactId {
Section {
openDirectChatButton(contactId)
}
}
Section("Member") {
infoRow("Group", groupInfo.displayName)
@ -80,18 +80,25 @@ struct GroupMemberInfoView: View {
func openDirectChatButton(_ contactId: Int64) -> some View {
Button {
if let i = chatModel.chats.firstIndex(where: { chat in
switch chat.chatInfo {
case let .direct(contact): return contact.contactId == contactId
default: return false
var chat = chatModel.getContactChat(contactId)
if chat == nil {
do {
chat = try apiGetChat(type: .direct, id: contactId)
if let chat = chat {
// TODO it's not correct to blindly set network status to connected - we should manage network status in model / backend
chat.serverInfo = Chat.ServerInfo(networkStatus: .connected)
chatModel.addChat(chat)
}
} catch let error {
logger.error("openDirectChatButton apiGetChat error: \(responseError(error))")
}
}) {
}
if let chat = chat {
dismissAllSheets(animated: true)
chatModel.chatId = chatModel.chats[i].chatInfo.id
chatModel.chatId = chat.id
}
} label: {
Label("Send direct message", systemImage: "message")
.foregroundColor(.accentColor)
}
}

View file

@ -28,16 +28,10 @@ struct ChatListNavLink: View {
}
}
private func chatView() -> some View {
ChatView(chat: chat)
.onAppear { loadChat(chat: chat) }
}
@ViewBuilder private func contactNavLink(_ contact: Contact) -> some View {
let v = NavLinkPlain(
tag: chat.chatInfo.id,
selection: $chatModel.chatId,
destination: { chatView() },
label: { ChatPreviewView(chat: chat) },
disabled: !contact.ready
)
@ -97,7 +91,6 @@ struct ChatListNavLink: View {
NavLinkPlain(
tag: chat.chatInfo.id,
selection: $chatModel.chatId,
destination: { chatView() },
label: { ChatPreviewView(chat: chat) },
disabled: !groupInfo.ready
)

View file

@ -14,6 +14,7 @@ struct ChatListView: View {
// not really used in this view
@State private var showSettings = false
@State private var searchText = ""
@State private var selectedChat: ChatId?
var body: some View {
let v = NavigationView {
@ -25,6 +26,7 @@ struct ChatListView: View {
}
}
.onChange(of: chatModel.chatId) { _ in
selectedChat = chatModel.chatId
if chatModel.chatId == nil, let chatId = chatModel.chatToTop {
chatModel.chatToTop = nil
chatModel.popChat(chatId)
@ -63,6 +65,15 @@ struct ChatListView: View {
}
}
}
.background(
NavigationLink(
destination: chatView(selectedChat),
isActive: Binding(
get: { selectedChat != nil },
set: { _, _ in selectedChat = nil }
)
) { EmptyView() }
)
}
.navigationViewStyle(.stack)
@ -73,6 +84,14 @@ struct ChatListView: View {
}
}
@ViewBuilder private func chatView(_ chatId: ChatId?) -> some View {
if let chatId = chatId, let chat = chatModel.getChat(chatId) {
ChatView(chat: chat).onAppear {
loadChat(chat: chat)
}
}
}
private func filteredChats() -> [Chat] {
let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
return s == ""

View file

@ -8,10 +8,9 @@
import SwiftUI
struct NavLinkPlain<V: Hashable, Destination: View, Label: View>: View {
struct NavLinkPlain<V: Hashable, Label: View>: View {
@State var tag: V
@Binding var selection: V?
@ViewBuilder var destination: () -> Destination
@ViewBuilder var label: () -> Label
var disabled = false
@ -21,10 +20,6 @@ struct NavLinkPlain<V: Hashable, Destination: View, Label: View>: View {
.disabled(disabled)
label()
}
.background {
NavigationLink("", tag: tag, selection: $selection, destination: destination)
.hidden()
}
}
}