update code scanner, layout
This commit is contained in:
parent
75ebc01e7f
commit
045c068074
@ -15,6 +15,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
logger.debug("AppDelegate: didFinishLaunchingWithOptions")
|
logger.debug("AppDelegate: didFinishLaunchingWithOptions")
|
||||||
application.registerForRemoteNotifications()
|
application.registerForRemoteNotifications()
|
||||||
if #available(iOS 17.0, *) { trackKeyboard() }
|
if #available(iOS 17.0, *) { trackKeyboard() }
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(pasteboardChanged), name: UIPasteboard.changedNotification, object: nil)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +37,10 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
ChatModel.shared.keyboardHeight = 0
|
ChatModel.shared.keyboardHeight = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func pasteboardChanged() {
|
||||||
|
ChatModel.shared.pasteboardHasURLs = UIPasteboard.general.hasStrings
|
||||||
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||||
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
|
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
|
||||||
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
|
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
|
||||||
|
@ -95,6 +95,7 @@ final class ChatModel: ObservableObject {
|
|||||||
@Published var draftChatId: String?
|
@Published var draftChatId: String?
|
||||||
// tracks keyboard height via subscription in AppDelegate
|
// tracks keyboard height via subscription in AppDelegate
|
||||||
@Published var keyboardHeight: CGFloat = 0
|
@Published var keyboardHeight: CGFloat = 0
|
||||||
|
@Published var pasteboardHasURLs: Bool = UIPasteboard.general.hasStrings
|
||||||
|
|
||||||
var messageDelivery: Dictionary<Int64, () -> Void> = [:]
|
var messageDelivery: Dictionary<Int64, () -> Void> = [:]
|
||||||
|
|
||||||
|
@ -26,10 +26,10 @@ struct SimpleXApp: App {
|
|||||||
@State private var showInitializationView = false
|
@State private var showInitializationView = false
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
DispatchQueue.global(qos: .background).sync {
|
// DispatchQueue.global(qos: .background).sync {
|
||||||
haskell_init()
|
haskell_init()
|
||||||
// hs_init(0, nil)
|
// hs_init(0, nil)
|
||||||
}
|
// }
|
||||||
UserDefaults.standard.register(defaults: appDefaults)
|
UserDefaults.standard.register(defaults: appDefaults)
|
||||||
setGroupDefaults()
|
setGroupDefaults()
|
||||||
registerGroupDefaults()
|
registerGroupDefaults()
|
||||||
|
@ -12,7 +12,8 @@ import SimpleXChat
|
|||||||
struct ChatListView: View {
|
struct ChatListView: View {
|
||||||
@EnvironmentObject var chatModel: ChatModel
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
@Binding var showSettings: Bool
|
@Binding var showSettings: Bool
|
||||||
@FocusState private var searchMode
|
@State private var searchMode = false
|
||||||
|
@FocusState private var searchFocussed
|
||||||
@State private var searchText = ""
|
@State private var searchText = ""
|
||||||
@State private var newChatMenuOption: NewChatMenuOption? = nil
|
@State private var newChatMenuOption: NewChatMenuOption? = nil
|
||||||
@State private var userPickerVisible = false
|
@State private var userPickerVisible = false
|
||||||
@ -144,7 +145,7 @@ struct ChatListView: View {
|
|||||||
VStack {
|
VStack {
|
||||||
List {
|
List {
|
||||||
if !chatModel.chats.isEmpty {
|
if !chatModel.chats.isEmpty {
|
||||||
ChatListSearchBar(searchMode: $searchMode, searchText: $searchText)
|
ChatListSearchBar(searchMode: $searchMode, searchFocussed: $searchFocussed, searchText: $searchText)
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
@ -254,9 +255,13 @@ struct ChatListView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct ChatListSearchBar: View {
|
struct ChatListSearchBar: View {
|
||||||
@FocusState.Binding var searchMode: Bool
|
@EnvironmentObject var m: ChatModel
|
||||||
|
@Binding var searchMode: Bool
|
||||||
|
@FocusState.Binding var searchFocussed: Bool
|
||||||
@Binding var searchText: String
|
@Binding var searchText: String
|
||||||
|
@State private var cancelVisible = false
|
||||||
|
@State private var pasteboardHasString = false
|
||||||
@State private var showScanCodeSheet = false
|
@State private var showScanCodeSheet = false
|
||||||
@State private var alert: PlanAndConnectAlert?
|
@State private var alert: PlanAndConnectAlert?
|
||||||
@State private var sheet: PlanAndConnectActionSheet?
|
@State private var sheet: PlanAndConnectActionSheet?
|
||||||
@ -266,20 +271,20 @@ private struct ChatListSearchBar: View {
|
|||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Image(systemName: "magnifyingglass")
|
Image(systemName: "magnifyingglass")
|
||||||
TextField("Search or paste link", text: $searchText)
|
TextField("Search or paste SimpleX link", text: $searchText)
|
||||||
.focused($searchMode)
|
.truncationMode(.middle)
|
||||||
|
.focused($searchFocussed)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
if searchMode {
|
if searchMode {
|
||||||
Image(systemName: "xmark.circle.fill")
|
Image(systemName: "xmark.circle.fill")
|
||||||
.opacity(searchText == "" ? 0 : 1)
|
.opacity(searchText == "" ? 0 : 1)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
searchText = ""
|
searchText = ""
|
||||||
}
|
}
|
||||||
} else {
|
} else if searchText == "" {
|
||||||
HStack(spacing: 24) {
|
HStack(spacing: 24) {
|
||||||
if UIPasteboard.general.hasURLs {
|
if m.pasteboardHasURLs {
|
||||||
Image(systemName: "doc")
|
Image(systemName: "doc")
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if let str = UIPasteboard.general.string {
|
if let str = UIPasteboard.general.string {
|
||||||
@ -288,10 +293,10 @@ private struct ChatListSearchBar: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image(systemName: "qrcode.viewfinder")
|
Image(systemName: "qrcode")
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: 27, height: 27)
|
.frame(width: 20, height: 20)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
showScanCodeSheet = true
|
showScanCodeSheet = true
|
||||||
}
|
}
|
||||||
@ -306,15 +311,12 @@ private struct ChatListSearchBar: View {
|
|||||||
|
|
||||||
if searchMode {
|
if searchMode {
|
||||||
Text("Cancel")
|
Text("Cancel")
|
||||||
.foregroundColor(.accentColor)
|
.foregroundColor(cancelVisible ? .accentColor : .clear)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
hideKeyboard()
|
|
||||||
searchText = ""
|
searchText = ""
|
||||||
withAnimation {
|
searchFocussed = false
|
||||||
searchMode = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.transition(.move(edge: .trailing))
|
.transition(.move(edge: .trailing).combined(with: .opacity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Divider()
|
Divider()
|
||||||
@ -323,19 +325,26 @@ private struct ChatListSearchBar: View {
|
|||||||
NewChatView(selection: .connect, showQRCodeScanner: true)
|
NewChatView(selection: .connect, showQRCodeScanner: true)
|
||||||
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil) // fixes .refreshable in ChatListView affecting nested view
|
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, 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
|
.onChange(of: searchText) { t in
|
||||||
let link = t.trimmingCharacters(in: .whitespaces)
|
let link = t.trimmingCharacters(in: .whitespaces)
|
||||||
if strIsSimplexLink(link) { // if SimpleX link is pasted, show connection dialogue
|
if strIsSimplexLink(link) { // if SimpleX link is pasted, show connection dialogue
|
||||||
hideKeyboard()
|
searchFocussed = false
|
||||||
searchText = ""
|
|
||||||
withAnimation {
|
|
||||||
searchMode = false
|
|
||||||
}
|
|
||||||
connect(link)
|
connect(link)
|
||||||
} else if t != "" && !searchMode { // if some other text is pasted, enter search mode
|
} else if t != "" { // if some other text is pasted, enter search mode
|
||||||
withAnimation {
|
searchFocussed = true
|
||||||
searchMode = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.alert(item: $alert) { a in
|
.alert(item: $alert) { a in
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SimpleXChat
|
import SimpleXChat
|
||||||
import CodeScanner
|
import CodeScanner
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
enum SomeAlert: Identifiable {
|
enum SomeAlert: Identifiable {
|
||||||
case someAlert(alert: Alert, id: String)
|
case someAlert(alert: Alert, id: String)
|
||||||
@ -210,6 +211,7 @@ private struct InviteView: View {
|
|||||||
Section("Share this 1-time invite link") {
|
Section("Share this 1-time invite link") {
|
||||||
shareLinkView()
|
shareLinkView()
|
||||||
}
|
}
|
||||||
|
.listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 10))
|
||||||
|
|
||||||
qrCodeView()
|
qrCodeView()
|
||||||
|
|
||||||
@ -246,8 +248,10 @@ private struct InviteView: View {
|
|||||||
setInvitationUsed()
|
setInvitationUsed()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "square.and.arrow.up")
|
Image(systemName: "square.and.arrow.up")
|
||||||
|
.padding(.top, -7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func qrCodeView() -> some View {
|
private func qrCodeView() -> some View {
|
||||||
@ -299,6 +303,10 @@ private struct ConnectView: View {
|
|||||||
let link = str.trimmingCharacters(in: .whitespaces)
|
let link = str.trimmingCharacters(in: .whitespaces)
|
||||||
if strIsSimplexLink(link) {
|
if strIsSimplexLink(link) {
|
||||||
pastedLink = 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)
|
connect(pastedLink)
|
||||||
} else {
|
} else {
|
||||||
alert = .newChatSomeAlert(alert: .someAlert(
|
alert = .newChatSomeAlert(alert: .someAlert(
|
||||||
@ -308,18 +316,11 @@ private struct ConnectView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Text("Tap to paste link")
|
Label("Tap to paste link", systemImage: "link")
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
} else {
|
} else {
|
||||||
HStack {
|
linkTextView(pastedLink)
|
||||||
linkTextView(pastedLink)
|
|
||||||
Button {
|
|
||||||
pastedLink = ""
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "xmark.circle")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,13 +343,7 @@ private struct ConnectView: View {
|
|||||||
.aspectRatio(contentMode: .fill)
|
.aspectRatio(contentMode: .fill)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.foregroundColor(Color.clear)
|
.foregroundColor(Color.clear)
|
||||||
VStack {
|
Label("Tap to scan", systemImage: "qrcode")
|
||||||
Image(systemName: "camera")
|
|
||||||
.resizable()
|
|
||||||
.aspectRatio(contentMode: .fit)
|
|
||||||
.frame(width: 28)
|
|
||||||
Text("Tap to scan")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
||||||
@ -416,7 +411,7 @@ struct InfoSheetButton<Content: View>: View {
|
|||||||
Image(systemName: "info.circle")
|
Image(systemName: "info.circle")
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: 20, height: 20)
|
.frame(width: 24, height: 24)
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $showInfoSheet) {
|
.sheet(isPresented: $showInfoSheet) {
|
||||||
content
|
content
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/twostraws/CodeScanner",
|
"location" : "https://github.com/twostraws/CodeScanner",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "c27a66149b7483fe42e2ec6aad61d5c3fffe522d",
|
"revision" : "bf5d7087015620b250ee6c865b3c9039fc159d1a",
|
||||||
"version" : "2.1.1"
|
"version" : "2.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user