From 045c068074a3c319a4ac80a05951efb5944080c1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 2 Dec 2023 11:39:56 +0000 Subject: [PATCH] update code scanner, layout --- apps/ios/Shared/AppDelegate.swift | 5 ++ apps/ios/Shared/Model/ChatModel.swift | 1 + apps/ios/Shared/SimpleXApp.swift | 6 +- .../Shared/Views/ChatList/ChatListView.swift | 61 +++++++++++-------- .../Shared/Views/NewChat/NewChatView.swift | 29 ++++----- .../xcshareddata/swiftpm/Package.resolved | 4 +- 6 files changed, 58 insertions(+), 48 deletions(-) diff --git a/apps/ios/Shared/AppDelegate.swift b/apps/ios/Shared/AppDelegate.swift index 9e6073c10..cb90583df 100644 --- a/apps/ios/Shared/AppDelegate.swift +++ b/apps/ios/Shared/AppDelegate.swift @@ -15,6 +15,7 @@ class AppDelegate: NSObject, UIApplicationDelegate { logger.debug("AppDelegate: didFinishLaunchingWithOptions") application.registerForRemoteNotifications() if #available(iOS 17.0, *) { trackKeyboard() } + NotificationCenter.default.addObserver(self, selector: #selector(pasteboardChanged), name: UIPasteboard.changedNotification, object: nil) return true } @@ -36,6 +37,10 @@ class AppDelegate: NSObject, UIApplicationDelegate { ChatModel.shared.keyboardHeight = 0 } + @objc func pasteboardChanged() { + ChatModel.shared.pasteboardHasURLs = UIPasteboard.general.hasStrings + } + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let token = deviceToken.map { String(format: "%02hhx", $0) }.joined() logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)") diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 62ff48c4a..054aa08da 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -95,6 +95,7 @@ final class ChatModel: ObservableObject { @Published var draftChatId: String? // tracks keyboard height via subscription in AppDelegate @Published var keyboardHeight: CGFloat = 0 + @Published var pasteboardHasURLs: Bool = UIPasteboard.general.hasStrings var messageDelivery: Dictionary Void> = [:] diff --git a/apps/ios/Shared/SimpleXApp.swift b/apps/ios/Shared/SimpleXApp.swift index fd1ec9511..86ea91456 100644 --- a/apps/ios/Shared/SimpleXApp.swift +++ b/apps/ios/Shared/SimpleXApp.swift @@ -26,10 +26,10 @@ struct SimpleXApp: App { @State private var showInitializationView = false init() { - DispatchQueue.global(qos: .background).sync { - haskell_init() +// DispatchQueue.global(qos: .background).sync { + haskell_init() // hs_init(0, nil) - } +// } UserDefaults.standard.register(defaults: appDefaults) setGroupDefaults() registerGroupDefaults() diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index 9a7ffd6f5..8cf8830ad 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -12,7 +12,8 @@ import SimpleXChat struct ChatListView: View { @EnvironmentObject var chatModel: ChatModel @Binding var showSettings: Bool - @FocusState private var searchMode + @State private var searchMode = false + @FocusState private var searchFocussed @State private var searchText = "" @State private var newChatMenuOption: NewChatMenuOption? = nil @State private var userPickerVisible = false @@ -144,7 +145,7 @@ struct ChatListView: View { VStack { List { if !chatModel.chats.isEmpty { - ChatListSearchBar(searchMode: $searchMode, searchText: $searchText) + ChatListSearchBar(searchMode: $searchMode, searchFocussed: $searchFocussed, searchText: $searchText) .listRowSeparator(.hidden) .frame(maxWidth: .infinity) } @@ -254,9 +255,13 @@ struct ChatListView: View { } } -private struct ChatListSearchBar: View { - @FocusState.Binding var searchMode: Bool +struct ChatListSearchBar: View { + @EnvironmentObject var m: ChatModel + @Binding var searchMode: Bool + @FocusState.Binding var searchFocussed: Bool @Binding var searchText: String + @State private var cancelVisible = false + @State private var pasteboardHasString = false @State private var showScanCodeSheet = false @State private var alert: PlanAndConnectAlert? @State private var sheet: PlanAndConnectActionSheet? @@ -266,20 +271,20 @@ private struct ChatListSearchBar: View { HStack(spacing: 12) { HStack(spacing: 4) { Image(systemName: "magnifyingglass") - TextField("Search or paste link", text: $searchText) - .focused($searchMode) + TextField("Search or paste SimpleX link", text: $searchText) + .truncationMode(.middle) + .focused($searchFocussed) .foregroundColor(.primary) .frame(maxWidth: .infinity) - if searchMode { Image(systemName: "xmark.circle.fill") .opacity(searchText == "" ? 0 : 1) .onTapGesture { searchText = "" } - } else { + } else if searchText == "" { HStack(spacing: 24) { - if UIPasteboard.general.hasURLs { + if m.pasteboardHasURLs { Image(systemName: "doc") .onTapGesture { if let str = UIPasteboard.general.string { @@ -288,10 +293,10 @@ private struct ChatListSearchBar: View { } } - Image(systemName: "qrcode.viewfinder") + Image(systemName: "qrcode") .resizable() .scaledToFit() - .frame(width: 27, height: 27) + .frame(width: 20, height: 20) .onTapGesture { showScanCodeSheet = true } @@ -306,15 +311,12 @@ private struct ChatListSearchBar: View { if searchMode { Text("Cancel") - .foregroundColor(.accentColor) + .foregroundColor(cancelVisible ? .accentColor : .clear) .onTapGesture { - hideKeyboard() searchText = "" - withAnimation { - searchMode = false - } + searchFocussed = false } - .transition(.move(edge: .trailing)) + .transition(.move(edge: .trailing).combined(with: .opacity)) } } Divider() @@ -323,19 +325,26 @@ private struct ChatListSearchBar: View { NewChatView(selection: .connect, showQRCodeScanner: true) .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) // fixes .refreshable in ChatListView affecting nested view } + .onChange(of: searchFocussed) { sf in + if sf { + withAnimation { searchMode = true } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + withAnimation { cancelVisible = true } + } + } else { + withAnimation { + searchMode = false + cancelVisible = false + } + } + } .onChange(of: searchText) { t in let link = t.trimmingCharacters(in: .whitespaces) if strIsSimplexLink(link) { // if SimpleX link is pasted, show connection dialogue - hideKeyboard() - searchText = "" - withAnimation { - searchMode = false - } + searchFocussed = false connect(link) - } else if t != "" && !searchMode { // if some other text is pasted, enter search mode - withAnimation { - searchMode = true - } + } else if t != "" { // if some other text is pasted, enter search mode + searchFocussed = true } } .alert(item: $alert) { a in diff --git a/apps/ios/Shared/Views/NewChat/NewChatView.swift b/apps/ios/Shared/Views/NewChat/NewChatView.swift index 0793c7ff1..c81764e2b 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatView.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatView.swift @@ -9,6 +9,7 @@ import SwiftUI import SimpleXChat import CodeScanner +import AVFoundation enum SomeAlert: Identifiable { case someAlert(alert: Alert, id: String) @@ -210,6 +211,7 @@ private struct InviteView: View { Section("Share this 1-time invite link") { shareLinkView() } + .listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 10)) qrCodeView() @@ -246,8 +248,10 @@ private struct InviteView: View { setInvitationUsed() } label: { Image(systemName: "square.and.arrow.up") + .padding(.top, -7) } } + .frame(maxWidth: .infinity) } private func qrCodeView() -> some View { @@ -299,6 +303,10 @@ private struct ConnectView: View { let link = str.trimmingCharacters(in: .whitespaces) if strIsSimplexLink(link) { pastedLink = link + // It would be good to hide it, but right now it is not clear how to release camera in CodeScanner + // https://github.com/twostraws/CodeScanner/issues/121 + // No known tricks worked (changing view ID, wrapping it in another view, etc.) + // showQRCodeScanner = false connect(pastedLink) } else { alert = .newChatSomeAlert(alert: .someAlert( @@ -308,18 +316,11 @@ private struct ConnectView: View { } } } label: { - Text("Tap to paste link") + Label("Tap to paste link", systemImage: "link") } .frame(maxWidth: .infinity, alignment: .center) } else { - HStack { - linkTextView(pastedLink) - Button { - pastedLink = "" - } label: { - Image(systemName: "xmark.circle") - } - } + linkTextView(pastedLink) } } @@ -342,13 +343,7 @@ private struct ConnectView: View { .aspectRatio(contentMode: .fill) .frame(maxWidth: .infinity, maxHeight: .infinity) .foregroundColor(Color.clear) - VStack { - Image(systemName: "camera") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 28) - Text("Tap to scan") - } + Label("Tap to scan", systemImage: "qrcode") } } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) @@ -416,7 +411,7 @@ struct InfoSheetButton: View { Image(systemName: "info.circle") .resizable() .scaledToFit() - .frame(width: 20, height: 20) + .frame(width: 24, height: 24) } .sheet(isPresented: $showInfoSheet) { content diff --git a/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cfacb2381..bd184513f 100644 --- a/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/twostraws/CodeScanner", "state" : { - "revision" : "c27a66149b7483fe42e2ec6aad61d5c3fffe522d", - "version" : "2.1.1" + "revision" : "bf5d7087015620b250ee6c865b3c9039fc159d1a", + "version" : "2.3.3" } }, {