open chat everywhere
This commit is contained in:
parent
3658d5a963
commit
ed91efacf5
@ -9,6 +9,7 @@ struct TopBarView: View {
|
|||||||
var accounts: [String]
|
var accounts: [String]
|
||||||
// var viewModel: LoginViewModel
|
// var viewModel: LoginViewModel
|
||||||
@ObservedObject var viewModel: LoginViewModel
|
@ObservedObject var viewModel: LoginViewModel
|
||||||
|
@Binding var isSettingsPresented: Bool
|
||||||
|
|
||||||
// Привязка для управления боковым меню
|
// Привязка для управления боковым меню
|
||||||
@Binding var isSideMenuPresented: Bool
|
@Binding var isSideMenuPresented: Bool
|
||||||
@ -101,7 +102,9 @@ struct TopBarView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if isProfileTab {
|
} else if isProfileTab {
|
||||||
NavigationLink(destination: SettingsView(viewModel: viewModel)) {
|
NavigationLink(isActive: $isSettingsPresented) {
|
||||||
|
SettingsView(viewModel: viewModel)
|
||||||
|
} label: {
|
||||||
Image(systemName: "wrench")
|
Image(systemName: "wrench")
|
||||||
.imageScale(.large)
|
.imageScale(.large)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
@ -213,6 +216,7 @@ struct TopBarView_Previews: PreviewProvider {
|
|||||||
@State private var revealProgress: CGFloat = 1
|
@State private var revealProgress: CGFloat = 1
|
||||||
@StateObject private var viewModel = LoginViewModel()
|
@StateObject private var viewModel = LoginViewModel()
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
|
@State private var isSettingsPresented = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TopBarView(
|
TopBarView(
|
||||||
@ -220,6 +224,7 @@ struct TopBarView_Previews: PreviewProvider {
|
|||||||
selectedAccount: $selectedAccount,
|
selectedAccount: $selectedAccount,
|
||||||
accounts: [selectedAccount],
|
accounts: [selectedAccount],
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
|
isSettingsPresented: $isSettingsPresented,
|
||||||
isSideMenuPresented: $isSideMenuPresented,
|
isSideMenuPresented: $isSideMenuPresented,
|
||||||
chatSearchRevealProgress: $revealProgress,
|
chatSearchRevealProgress: $revealProgress,
|
||||||
chatSearchText: $searchText
|
chatSearchText: $searchText
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
struct ChatDeepLink: Identifiable {
|
|
||||||
let id = UUID()
|
|
||||||
let chatId: String
|
|
||||||
let chatProfile: ChatProfile?
|
|
||||||
let message: MessageItem?
|
|
||||||
}
|
|
||||||
@ -3,7 +3,7 @@ import Combine
|
|||||||
|
|
||||||
final class IncomingMessageCenter: ObservableObject {
|
final class IncomingMessageCenter: ObservableObject {
|
||||||
@Published private(set) var banner: IncomingMessageBanner?
|
@Published private(set) var banner: IncomingMessageBanner?
|
||||||
@Published var pendingDeepLink: ChatDeepLink?
|
@Published var presentedChat: PrivateChatListItem?
|
||||||
var currentUserId: String?
|
var currentUserId: String?
|
||||||
|
|
||||||
private var dismissWorkItem: DispatchWorkItem?
|
private var dismissWorkItem: DispatchWorkItem?
|
||||||
@ -29,11 +29,7 @@ final class IncomingMessageCenter: ObservableObject {
|
|||||||
|
|
||||||
func openCurrentChat() {
|
func openCurrentChat() {
|
||||||
guard let banner else { return }
|
guard let banner else { return }
|
||||||
pendingDeepLink = ChatDeepLink(
|
presentedChat = makeChatItem(from: banner.message)
|
||||||
chatId: banner.message.chatId,
|
|
||||||
chatProfile: banner.message.senderData,
|
|
||||||
message: banner.message
|
|
||||||
)
|
|
||||||
dismissBanner()
|
dismissBanner()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +81,18 @@ final class IncomingMessageCenter: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func makeChatItem(from message: MessageItem) -> PrivateChatListItem {
|
||||||
|
let profile = message.senderData
|
||||||
|
return PrivateChatListItem(
|
||||||
|
chatId: message.chatId,
|
||||||
|
chatType: .privateChat,
|
||||||
|
chatData: profile,
|
||||||
|
lastMessage: message,
|
||||||
|
createdAt: message.createdAt,
|
||||||
|
unreadCount: 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private func scheduleDismiss(after delay: TimeInterval = 5) {
|
private func scheduleDismiss(after delay: TimeInterval = 5) {
|
||||||
dismissWorkItem?.cancel()
|
dismissWorkItem?.cancel()
|
||||||
let workItem = DispatchWorkItem { [weak self] in
|
let workItem = DispatchWorkItem { [weak self] in
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import UIKit
|
|||||||
|
|
||||||
struct ChatsTab: View {
|
struct ChatsTab: View {
|
||||||
@ObservedObject private var loginViewModel: LoginViewModel
|
@ObservedObject private var loginViewModel: LoginViewModel
|
||||||
@Binding private var pendingDeepLink: ChatDeepLink?
|
|
||||||
@Binding var searchRevealProgress: CGFloat
|
@Binding var searchRevealProgress: CGFloat
|
||||||
@Binding var searchText: String
|
@Binding var searchText: String
|
||||||
private let searchService = SearchService()
|
private let searchService = SearchService()
|
||||||
@ -41,12 +40,10 @@ struct ChatsTab: View {
|
|||||||
|
|
||||||
init(
|
init(
|
||||||
loginViewModel: LoginViewModel,
|
loginViewModel: LoginViewModel,
|
||||||
pendingDeepLink: Binding<ChatDeepLink?>,
|
|
||||||
searchRevealProgress: Binding<CGFloat>,
|
searchRevealProgress: Binding<CGFloat>,
|
||||||
searchText: Binding<String>
|
searchText: Binding<String>
|
||||||
) {
|
) {
|
||||||
self._loginViewModel = ObservedObject(wrappedValue: loginViewModel)
|
self._loginViewModel = ObservedObject(wrappedValue: loginViewModel)
|
||||||
self._pendingDeepLink = pendingDeepLink
|
|
||||||
self._searchRevealProgress = searchRevealProgress
|
self._searchRevealProgress = searchRevealProgress
|
||||||
self._searchText = searchText
|
self._searchText = searchText
|
||||||
}
|
}
|
||||||
@ -100,13 +97,6 @@ struct ChatsTab: View {
|
|||||||
globalSearchTask?.cancel()
|
globalSearchTask?.cancel()
|
||||||
globalSearchTask = nil
|
globalSearchTask = nil
|
||||||
}
|
}
|
||||||
.onChange(of: pendingDeepLink?.id) { _ in
|
|
||||||
guard let link = pendingDeepLink else { return }
|
|
||||||
handle(chatDeepLink: link)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
pendingDeepLink = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
@ -552,44 +542,6 @@ private extension ChatsTab {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle(chatDeepLink: ChatDeepLink) {
|
|
||||||
dismissKeyboard()
|
|
||||||
if !searchText.isEmpty {
|
|
||||||
searchText = ""
|
|
||||||
}
|
|
||||||
if searchRevealProgress > 0 {
|
|
||||||
withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) {
|
|
||||||
searchRevealProgress = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let existingChat = viewModel.chats.first(where: { $0.chatId == chatDeepLink.chatId })
|
|
||||||
pendingChatItem = existingChat ?? makeChatItem(from: chatDeepLink)
|
|
||||||
selectedChatId = chatDeepLink.chatId
|
|
||||||
isPendingChatActive = true
|
|
||||||
|
|
||||||
if existingChat == nil {
|
|
||||||
if loginViewModel.chatLoadingState != .loading {
|
|
||||||
loginViewModel.chatLoadingState = .loading
|
|
||||||
}
|
|
||||||
viewModel.refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeChatItem(from deepLink: ChatDeepLink) -> PrivateChatListItem {
|
|
||||||
let profile = deepLink.chatProfile ?? deepLink.message?.senderData
|
|
||||||
let lastMessage = deepLink.message
|
|
||||||
let createdAt = deepLink.message?.createdAt
|
|
||||||
return PrivateChatListItem(
|
|
||||||
chatId: deepLink.chatId,
|
|
||||||
chatType: .privateChat,
|
|
||||||
chatData: profile,
|
|
||||||
lastMessage: lastMessage,
|
|
||||||
createdAt: createdAt,
|
|
||||||
unreadCount: 0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleSearchQueryChange(_ query: String) {
|
func handleSearchQueryChange(_ query: String) {
|
||||||
let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines)
|
let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
@ -1209,12 +1161,10 @@ struct ChatsTab_Previews: PreviewProvider {
|
|||||||
@State private var progress: CGFloat = 1
|
@State private var progress: CGFloat = 1
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
@StateObject private var loginViewModel = LoginViewModel()
|
@StateObject private var loginViewModel = LoginViewModel()
|
||||||
@State private var deepLink: ChatDeepLink?
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ChatsTab(
|
ChatsTab(
|
||||||
loginViewModel: loginViewModel,
|
loginViewModel: loginViewModel,
|
||||||
pendingDeepLink: $deepLink,
|
|
||||||
searchRevealProgress: $progress,
|
searchRevealProgress: $progress,
|
||||||
searchText: $searchText
|
searchText: $searchText
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import SwiftUI
|
|||||||
|
|
||||||
struct MainView: View {
|
struct MainView: View {
|
||||||
@ObservedObject var viewModel: LoginViewModel
|
@ObservedObject var viewModel: LoginViewModel
|
||||||
@EnvironmentObject private var messageCenter: IncomingMessageCenter
|
|
||||||
@State private var selectedTab: Int = 0
|
@State private var selectedTab: Int = 0
|
||||||
// @StateObject private var newHomeTabViewModel = NewHomeTabViewModel()
|
// @StateObject private var newHomeTabViewModel = NewHomeTabViewModel()
|
||||||
|
|
||||||
@ -16,6 +15,7 @@ struct MainView: View {
|
|||||||
@State private var menuOffset: CGFloat = 0
|
@State private var menuOffset: CGFloat = 0
|
||||||
@State private var chatSearchRevealProgress: CGFloat = 0
|
@State private var chatSearchRevealProgress: CGFloat = 0
|
||||||
@State private var chatSearchText: String = ""
|
@State private var chatSearchText: String = ""
|
||||||
|
@State private var isSettingsPresented = false
|
||||||
|
|
||||||
private var tabTitle: String {
|
private var tabTitle: String {
|
||||||
switch selectedTab {
|
switch selectedTab {
|
||||||
@ -42,6 +42,7 @@ struct MainView: View {
|
|||||||
selectedAccount: $selectedAccount,
|
selectedAccount: $selectedAccount,
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
|
isSettingsPresented: $isSettingsPresented,
|
||||||
isSideMenuPresented: $isSideMenuPresented,
|
isSideMenuPresented: $isSideMenuPresented,
|
||||||
chatSearchRevealProgress: $chatSearchRevealProgress,
|
chatSearchRevealProgress: $chatSearchRevealProgress,
|
||||||
chatSearchText: $chatSearchText
|
chatSearchText: $chatSearchText
|
||||||
@ -56,10 +57,6 @@ struct MainView: View {
|
|||||||
|
|
||||||
ChatsTab(
|
ChatsTab(
|
||||||
loginViewModel: viewModel,
|
loginViewModel: viewModel,
|
||||||
pendingDeepLink: Binding(
|
|
||||||
get: { messageCenter.pendingDeepLink },
|
|
||||||
set: { messageCenter.pendingDeepLink = $0 }
|
|
||||||
),
|
|
||||||
searchRevealProgress: $chatSearchRevealProgress,
|
searchRevealProgress: $chatSearchRevealProgress,
|
||||||
searchText: $chatSearchText
|
searchText: $chatSearchText
|
||||||
)
|
)
|
||||||
@ -140,23 +137,6 @@ struct MainView: View {
|
|||||||
menuOffset = presented ? menuWidth : 0
|
menuOffset = presented ? menuWidth : 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
|
||||||
messageCenter.currentUserId = viewModel.userId.isEmpty ? nil : viewModel.userId
|
|
||||||
}
|
|
||||||
.onChange(of: viewModel.userId) { newValue in
|
|
||||||
messageCenter.currentUserId = newValue.isEmpty ? nil : newValue
|
|
||||||
}
|
|
||||||
.onChange(of: messageCenter.pendingDeepLink?.id) { _ in
|
|
||||||
guard messageCenter.pendingDeepLink != nil else { return }
|
|
||||||
withAnimation(.easeInOut) {
|
|
||||||
selectedTab = 2
|
|
||||||
isSideMenuPresented = false
|
|
||||||
menuOffset = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onDisappear {
|
|
||||||
messageCenter.currentUserId = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +144,6 @@ struct MainView_Previews: PreviewProvider {
|
|||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let mockViewModel = LoginViewModel()
|
let mockViewModel = LoginViewModel()
|
||||||
MainView(viewModel: mockViewModel)
|
MainView(viewModel: mockViewModel)
|
||||||
.environmentObject(IncomingMessageCenter())
|
|
||||||
.environmentObject(ThemeManager())
|
.environmentObject(ThemeManager())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,10 +41,31 @@ struct yobbleApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.animation(.spring(response: 0.35, dampingFraction: 0.8), value: messageCenter.banner != nil)
|
.animation(.spring(response: 0.35, dampingFraction: 0.8), value: messageCenter.banner != nil)
|
||||||
|
.sheet(item: $messageCenter.presentedChat) { chatItem in
|
||||||
|
NavigationView {
|
||||||
|
PrivateChatView(
|
||||||
|
chat: chatItem,
|
||||||
|
currentUserId: messageCenter.currentUserId
|
||||||
|
)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
|
Button(NSLocalizedString("Закрыть", comment: "")) {
|
||||||
|
messageCenter.presentedChat = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.environmentObject(messageCenter)
|
.environmentObject(messageCenter)
|
||||||
.environmentObject(themeManager)
|
.environmentObject(themeManager)
|
||||||
.preferredColorScheme(themeManager.theme.colorScheme)
|
.preferredColorScheme(themeManager.theme.colorScheme)
|
||||||
.environment(\.managedObjectContext, persistenceController.viewContext)
|
.environment(\.managedObjectContext, persistenceController.viewContext)
|
||||||
|
.onAppear {
|
||||||
|
messageCenter.currentUserId = viewModel.userId.isEmpty ? nil : viewModel.userId
|
||||||
|
}
|
||||||
|
.onChange(of: viewModel.userId) { newValue in
|
||||||
|
messageCenter.currentUserId = newValue.isEmpty ? nil : newValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user