core, ios: include contact addresses in profiles (#2328)
* core: include contact links in profiles * add connection request link to contact and group profiles * set group link on update, view, api * core: include contact addresses in profiles * remove id from UserContactLink * schema, fix test * remove address from profile when deleting link, tests * remove diff * remove diff * fix * ios wip * learn more, confirm save, reset on delete * re-use in create link view * remove obsolete files * color * revert scheme * learn more with create * layout * layout * progress indicator * delete text * save on change, layout --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
@@ -1,131 +0,0 @@
|
||||
//
|
||||
// AcceptRequestsView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Evgeny on 23/10/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct AcceptRequestsView: View {
|
||||
@EnvironmentObject private var m: ChatModel
|
||||
@State var contactLink: UserContactLink
|
||||
@State private var a = AutoAcceptState()
|
||||
@State private var saved = AutoAcceptState()
|
||||
@FocusState private var keyboardVisible: Bool
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
settingsRow("checkmark") {
|
||||
Toggle("Automatically", isOn: $a.enable)
|
||||
}
|
||||
if a.enable {
|
||||
settingsRow(
|
||||
a.incognito ? "theatermasks.fill" : "theatermasks",
|
||||
color: a.incognito ? .indigo : .secondary
|
||||
) {
|
||||
Toggle("Incognito", isOn: $a.incognito)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Accept requests")
|
||||
} footer: {
|
||||
saveButtons()
|
||||
}
|
||||
if a.enable {
|
||||
Section {
|
||||
TextEditor(text: $a.welcomeText)
|
||||
.focused($keyboardVisible)
|
||||
.padding(.horizontal, -5)
|
||||
.padding(.top, -8)
|
||||
.frame(height: 90, alignment: .topLeading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
} header: {
|
||||
Text("Welcome message")
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
a = AutoAcceptState(contactLink: contactLink)
|
||||
saved = a
|
||||
}
|
||||
.onChange(of: a.enable) { _ in
|
||||
if !a.enable { a = AutoAcceptState() }
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func saveButtons() -> some View {
|
||||
HStack {
|
||||
Button {
|
||||
a = saved
|
||||
} label: {
|
||||
Label("Cancel", systemImage: "arrow.counterclockwise")
|
||||
}
|
||||
Spacer()
|
||||
Button {
|
||||
Task {
|
||||
do {
|
||||
if let link = try await userAddressAutoAccept(a.autoAccept) {
|
||||
contactLink = link
|
||||
m.userAddress = link
|
||||
saved = a
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("userAddressAutoAccept error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label("Save", systemImage: "checkmark")
|
||||
}
|
||||
}
|
||||
.font(.body)
|
||||
.disabled(a == saved)
|
||||
}
|
||||
|
||||
private struct AutoAcceptState: Equatable {
|
||||
var enable = false
|
||||
var incognito = false
|
||||
var welcomeText = ""
|
||||
|
||||
init(enable: Bool = false, incognito: Bool = false, welcomeText: String = "") {
|
||||
self.enable = enable
|
||||
self.incognito = incognito
|
||||
self.welcomeText = welcomeText
|
||||
}
|
||||
|
||||
init(contactLink: UserContactLink) {
|
||||
if let aa = contactLink.autoAccept {
|
||||
enable = true
|
||||
incognito = aa.acceptIncognito
|
||||
if let msg = aa.autoReply {
|
||||
welcomeText = msg.text
|
||||
} else {
|
||||
welcomeText = ""
|
||||
}
|
||||
} else {
|
||||
enable = false
|
||||
incognito = false
|
||||
welcomeText = ""
|
||||
}
|
||||
}
|
||||
|
||||
var autoAccept: AutoAccept? {
|
||||
if enable {
|
||||
var autoReply: MsgContent? = nil
|
||||
let s = welcomeText.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if s != "" { autoReply = .text(s) }
|
||||
return AutoAccept(acceptIncognito: incognito, autoReply: autoReply)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AcceptRequestsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AcceptRequestsView(contactLink: UserContactLink(connReqContact: ""))
|
||||
}
|
||||
}
|
||||
@@ -147,10 +147,11 @@ struct SettingsView: View {
|
||||
incognitoRow()
|
||||
|
||||
NavigationLink {
|
||||
CreateLinkView(selection: .longTerm, viaNavLink: true)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
UserAddressView(shareViaProfile: chatModel.currentUser!.addressShared)
|
||||
.navigationTitle("SimpleX address")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
settingsRow("qrcode") { Text("Your SimpleX contact address") }
|
||||
settingsRow("qrcode") { Text("Your SimpleX address") }
|
||||
}
|
||||
|
||||
NavigationLink {
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
//
|
||||
// UserAddress.swift
|
||||
// SimpleX
|
||||
//
|
||||
// Created by Evgeny Poberezkin on 31/01/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct UserAddress: View {
|
||||
@EnvironmentObject private var chatModel: ChatModel
|
||||
@State private var alert: UserAddressAlert?
|
||||
@State private var showAcceptRequests = false
|
||||
|
||||
private enum UserAddressAlert: Identifiable {
|
||||
case deleteAddress
|
||||
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .deleteAddress: return "deleteAddress"
|
||||
case let .error(title, _): return "error \(title)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack (alignment: .leading) {
|
||||
Text("You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it.")
|
||||
.padding(.bottom)
|
||||
if let userAdress = chatModel.userAddress {
|
||||
QRCode(uri: userAdress.connReqContact)
|
||||
HStack {
|
||||
Button {
|
||||
showShareSheet(items: [userAdress.connReqContact])
|
||||
} label: {
|
||||
HStack {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
Text("Share link")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
NavigationLink {
|
||||
if let contactLink = chatModel.userAddress {
|
||||
AcceptRequestsView(contactLink: contactLink)
|
||||
.navigationTitle("Contact requests")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Contact requests")
|
||||
Image(systemName: "chevron.right")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
Button(role: .destructive) { alert = .deleteAddress } label: {
|
||||
Label("Delete address", systemImage: "trash")
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
} else {
|
||||
Button {
|
||||
Task {
|
||||
do {
|
||||
let connReqContact = try await apiCreateUserAddress()
|
||||
DispatchQueue.main.async {
|
||||
chatModel.userAddress = UserContactLink(connReqContact: connReqContact)
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("UserAddress apiCreateUserAddress: \(responseError(error))")
|
||||
let a = getErrorAlert(error, "Error creating address")
|
||||
alert = .error(title: a.title, error: a.message)
|
||||
}
|
||||
}
|
||||
} label: { Label("Create address", systemImage: "qrcode") }
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||
.sheet(isPresented: $showAcceptRequests) {
|
||||
if let contactLink = chatModel.userAddress {
|
||||
AcceptRequestsView(contactLink: contactLink)
|
||||
}
|
||||
}
|
||||
.alert(item: $alert) { alert in
|
||||
switch alert {
|
||||
case .deleteAddress:
|
||||
return Alert(
|
||||
title: Text("Delete address?"),
|
||||
message: Text("All your contacts will remain connected"),
|
||||
primaryButton: .destructive(Text("Delete")) {
|
||||
Task {
|
||||
do {
|
||||
try await apiDeleteUserAddress()
|
||||
DispatchQueue.main.async {
|
||||
chatModel.userAddress = nil
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("UserAddress apiDeleteUserAddress: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
}, secondaryButton: .cancel()
|
||||
)
|
||||
case let .error(title, error):
|
||||
return Alert(title: Text(title), message: Text(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UserAddress_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let chatModel = ChatModel()
|
||||
chatModel.userAddress = UserContactLink(connReqContact: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D")
|
||||
return Group {
|
||||
UserAddress()
|
||||
.environmentObject(chatModel)
|
||||
UserAddress()
|
||||
.environmentObject(ChatModel())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// UserAddressLearnMore.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 27.04.2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct UserAddressLearnMore: View {
|
||||
var body: some View {
|
||||
List {
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
Text("You can create a long term address that can be used by other people to connect with you.")
|
||||
Text("Unlike 1-time invitation links, these addresses can be used many times, that makes them good to share online.")
|
||||
Text("When people connect to you via this address, you will receive a connection request that you can accept or reject.")
|
||||
Text("Read more in [User Guide](https://github.com/simplex-chat/simplex-chat/blob/stable/docs/guide/app-settings.md#your-simplex-contact-address).")
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UserAddressLearnMore_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
UserAddressLearnMore()
|
||||
}
|
||||
}
|
||||
396
apps/ios/Shared/Views/UserSettings/UserAddressView.swift
Normal file
396
apps/ios/Shared/Views/UserSettings/UserAddressView.swift
Normal file
@@ -0,0 +1,396 @@
|
||||
//
|
||||
// UserAddressView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 26.04.2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct UserAddressView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@EnvironmentObject private var chatModel: ChatModel
|
||||
@State var viaCreateLinkView = false
|
||||
@State var shareViaProfile = false
|
||||
@State private var aas = AutoAcceptState()
|
||||
@State private var savedAAS = AutoAcceptState()
|
||||
@State private var ignoreShareViaProfileChange = false
|
||||
@State private var alert: UserAddressAlert?
|
||||
@State private var showSaveDialogue = false
|
||||
@State private var progressIndicator = false
|
||||
@FocusState private var keyboardVisible: Bool
|
||||
|
||||
private enum UserAddressAlert: Identifiable {
|
||||
case deleteAddress
|
||||
case profileAddress(on: Bool)
|
||||
case shareOnCreate
|
||||
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .deleteAddress: return "deleteAddress"
|
||||
case let .profileAddress(on): return "profileAddress \(on)"
|
||||
case .shareOnCreate: return "shareOnCreate"
|
||||
case let .error(title, _): return "error \(title)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if viaCreateLinkView {
|
||||
userAddressView()
|
||||
} else {
|
||||
userAddressView()
|
||||
.modifier(BackButton {
|
||||
if savedAAS == aas {
|
||||
dismiss()
|
||||
} else {
|
||||
showSaveDialogue = true
|
||||
}
|
||||
})
|
||||
.confirmationDialog("Save settings?", isPresented: $showSaveDialogue) {
|
||||
Button("Save auto-accept settings") {
|
||||
saveAAS()
|
||||
dismiss()
|
||||
}
|
||||
Button("Exit without saving") { dismiss() }
|
||||
}
|
||||
}
|
||||
if progressIndicator {
|
||||
ZStack {
|
||||
if chatModel.userAddress != nil {
|
||||
Circle()
|
||||
.fill(.white)
|
||||
.opacity(0.7)
|
||||
.frame(width: 56, height: 56)
|
||||
}
|
||||
ProgressView().scaleEffect(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func userAddressView() -> some View {
|
||||
List {
|
||||
if let userAddress = chatModel.userAddress {
|
||||
existingAddressView(userAddress)
|
||||
.onAppear {
|
||||
aas = AutoAcceptState(userAddress: userAddress)
|
||||
savedAAS = aas
|
||||
}
|
||||
.onChange(of: aas.enable) { _ in
|
||||
if !aas.enable { aas = AutoAcceptState() }
|
||||
}
|
||||
} else {
|
||||
Section {
|
||||
createAddressButton()
|
||||
} footer: {
|
||||
Text("Create an address to let people connect with you.")
|
||||
}
|
||||
|
||||
Section {
|
||||
learnMoreButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(item: $alert) { alert in
|
||||
switch alert {
|
||||
case .deleteAddress:
|
||||
return Alert(
|
||||
title: Text("Delete address?"),
|
||||
message:
|
||||
shareViaProfile
|
||||
? Text("All your contacts will remain connected. Profile update will be sent to your contacts.")
|
||||
: Text("All your contacts will remain connected."),
|
||||
primaryButton: .destructive(Text("Delete")) {
|
||||
progressIndicator = true
|
||||
Task {
|
||||
do {
|
||||
if let u = try await apiDeleteUserAddress() {
|
||||
DispatchQueue.main.async {
|
||||
chatModel.userAddress = nil
|
||||
chatModel.updateUser(u)
|
||||
if shareViaProfile {
|
||||
ignoreShareViaProfileChange = true
|
||||
shareViaProfile = false
|
||||
}
|
||||
}
|
||||
}
|
||||
await MainActor.run { progressIndicator = false }
|
||||
} catch let error {
|
||||
logger.error("UserAddressView apiDeleteUserAddress: \(responseError(error))")
|
||||
await MainActor.run { progressIndicator = false }
|
||||
}
|
||||
}
|
||||
}, secondaryButton: .cancel()
|
||||
)
|
||||
case let .profileAddress(on):
|
||||
if on {
|
||||
return Alert(
|
||||
title: Text("Share address with contacts?"),
|
||||
message: Text("Profile update will be sent to your contacts."),
|
||||
primaryButton: .default(Text("Share")) {
|
||||
setProfileAddress(on)
|
||||
}, secondaryButton: .cancel() {
|
||||
ignoreShareViaProfileChange = true
|
||||
shareViaProfile = !on
|
||||
}
|
||||
)
|
||||
} else {
|
||||
return Alert(
|
||||
title: Text("Stop sharing address?"),
|
||||
message: Text("Profile update will be sent to your contacts."),
|
||||
primaryButton: .default(Text("Stop sharing")) {
|
||||
setProfileAddress(on)
|
||||
}, secondaryButton: .cancel() {
|
||||
ignoreShareViaProfileChange = true
|
||||
shareViaProfile = !on
|
||||
}
|
||||
)
|
||||
}
|
||||
case .shareOnCreate:
|
||||
return Alert(
|
||||
title: Text("Share address with contacts?"),
|
||||
message: Text("Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts."),
|
||||
primaryButton: .default(Text("Share")) {
|
||||
setProfileAddress(true)
|
||||
ignoreShareViaProfileChange = true
|
||||
shareViaProfile = true
|
||||
}, secondaryButton: .cancel()
|
||||
)
|
||||
case let .error(title, error):
|
||||
return Alert(title: Text(title), message: Text(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View {
|
||||
Section {
|
||||
QRCode(uri: userAddress.connReqContact)
|
||||
shareQRCodeButton(userAddress)
|
||||
shareWithContactsButton()
|
||||
autoAcceptToggle()
|
||||
learnMoreButton()
|
||||
} header: {
|
||||
Text("Address")
|
||||
}
|
||||
|
||||
if aas.enable {
|
||||
autoAcceptSection()
|
||||
}
|
||||
|
||||
Section {
|
||||
deleteAddressButton()
|
||||
} footer: {
|
||||
Text("Your contacts will remain connected.")
|
||||
}
|
||||
}
|
||||
|
||||
private func createAddressButton() -> some View {
|
||||
Button {
|
||||
progressIndicator = true
|
||||
Task {
|
||||
do {
|
||||
let connReqContact = try await apiCreateUserAddress()
|
||||
DispatchQueue.main.async {
|
||||
chatModel.userAddress = UserContactLink(connReqContact: connReqContact)
|
||||
alert = .shareOnCreate
|
||||
progressIndicator = false
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("UserAddressView apiCreateUserAddress: \(responseError(error))")
|
||||
let a = getErrorAlert(error, "Error creating address")
|
||||
alert = .error(title: a.title, error: a.message)
|
||||
await MainActor.run { progressIndicator = false }
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label("Create SimpleX address", systemImage: "qrcode")
|
||||
}
|
||||
}
|
||||
|
||||
private func deleteAddressButton() -> some View {
|
||||
Button(role: .destructive) {
|
||||
alert = .deleteAddress
|
||||
} label: {
|
||||
Label("Delete address", systemImage: "trash")
|
||||
.foregroundColor(Color.red)
|
||||
}
|
||||
}
|
||||
|
||||
private func shareQRCodeButton(_ userAdress: UserContactLink) -> some View {
|
||||
Button {
|
||||
showShareSheet(items: [userAdress.connReqContact])
|
||||
} label: {
|
||||
settingsRow("square.and.arrow.up") {
|
||||
Text("Share address")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func autoAcceptToggle() -> some View {
|
||||
settingsRow("checkmark") {
|
||||
Toggle("Auto-accept", isOn: $aas.enable)
|
||||
.onChange(of: aas.enable) { _ in
|
||||
saveAAS()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func learnMoreButton() -> some View {
|
||||
NavigationLink {
|
||||
UserAddressLearnMore()
|
||||
.navigationTitle("SimpleX address")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
settingsRow("info.circle") {
|
||||
Text("Learn more")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func shareWithContactsButton() -> some View {
|
||||
settingsRow("person") {
|
||||
Toggle("Share with contacts", isOn: $shareViaProfile)
|
||||
.onChange(of: shareViaProfile) { on in
|
||||
if ignoreShareViaProfileChange {
|
||||
ignoreShareViaProfileChange = false
|
||||
} else {
|
||||
alert = .profileAddress(on: on)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setProfileAddress(_ on: Bool) {
|
||||
progressIndicator = true
|
||||
Task {
|
||||
do {
|
||||
if let u = try await apiSetProfileAddress(on: on) {
|
||||
DispatchQueue.main.async {
|
||||
chatModel.updateUser(u)
|
||||
}
|
||||
}
|
||||
await MainActor.run { progressIndicator = false }
|
||||
} catch let error {
|
||||
logger.error("UserAddressView apiSetProfileAddress: \(responseError(error))")
|
||||
await MainActor.run { progressIndicator = false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AutoAcceptState: Equatable {
|
||||
var enable = false
|
||||
var incognito = false
|
||||
var welcomeText = ""
|
||||
|
||||
init(enable: Bool = false, incognito: Bool = false, welcomeText: String = "") {
|
||||
self.enable = enable
|
||||
self.incognito = incognito
|
||||
self.welcomeText = welcomeText
|
||||
}
|
||||
|
||||
init(userAddress: UserContactLink) {
|
||||
if let aa = userAddress.autoAccept {
|
||||
enable = true
|
||||
incognito = aa.acceptIncognito
|
||||
if let msg = aa.autoReply {
|
||||
welcomeText = msg.text
|
||||
} else {
|
||||
welcomeText = ""
|
||||
}
|
||||
} else {
|
||||
enable = false
|
||||
incognito = false
|
||||
welcomeText = ""
|
||||
}
|
||||
}
|
||||
|
||||
var autoAccept: AutoAccept? {
|
||||
if enable {
|
||||
var autoReply: MsgContent? = nil
|
||||
let s = welcomeText.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if s != "" { autoReply = .text(s) }
|
||||
return AutoAccept(acceptIncognito: incognito, autoReply: autoReply)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func autoAcceptSection() -> some View {
|
||||
Section {
|
||||
acceptIncognitoToggle()
|
||||
welcomeMessageEditor()
|
||||
saveAASButton()
|
||||
.disabled(aas == savedAAS)
|
||||
} header: {
|
||||
Text("Accept requests")
|
||||
}
|
||||
}
|
||||
|
||||
private func acceptIncognitoToggle() -> some View {
|
||||
settingsRow(
|
||||
aas.incognito ? "theatermasks.fill" : "theatermasks",
|
||||
color: aas.incognito ? .indigo : .secondary
|
||||
) {
|
||||
Toggle("Accept incognito", isOn: $aas.incognito)
|
||||
}
|
||||
}
|
||||
|
||||
private func welcomeMessageEditor() -> some View {
|
||||
ZStack {
|
||||
if aas.welcomeText.isEmpty {
|
||||
TextEditor(text: Binding.constant("Enter welcome message… (optional)"))
|
||||
.foregroundColor(.secondary)
|
||||
.disabled(true)
|
||||
.padding(.horizontal, -5)
|
||||
.padding(.top, -8)
|
||||
.frame(height: 90, alignment: .topLeading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
TextEditor(text: $aas.welcomeText)
|
||||
.focused($keyboardVisible)
|
||||
.padding(.horizontal, -5)
|
||||
.padding(.top, -8)
|
||||
.frame(height: 90, alignment: .topLeading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
private func saveAASButton() -> some View {
|
||||
Button {
|
||||
saveAAS()
|
||||
} label: {
|
||||
Text("Save")
|
||||
}
|
||||
}
|
||||
|
||||
private func saveAAS() {
|
||||
Task {
|
||||
do {
|
||||
if let address = try await userAddressAutoAccept(aas.autoAccept) {
|
||||
chatModel.userAddress = address
|
||||
savedAAS = aas
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("userAddressAutoAccept error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UserAddressView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let chatModel = ChatModel()
|
||||
chatModel.userAddress = UserContactLink(connReqContact: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D")
|
||||
return Group {
|
||||
UserAddressView()
|
||||
.environmentObject(chatModel)
|
||||
UserAddressView()
|
||||
.environmentObject(ChatModel())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user