mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
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:
@@ -618,6 +618,16 @@ func apiUpdateProfile(profile: Profile) async throws -> Profile? {
|
||||
}
|
||||
}
|
||||
|
||||
func apiSetProfileAddress(on: Bool) async throws -> User? {
|
||||
let userId = try currentUserId("apiSetProfileAddress")
|
||||
let r = await chatSendCmd(.apiSetProfileAddress(userId: userId, on: on))
|
||||
switch r {
|
||||
case .userProfileNoChange: return nil
|
||||
case let .userProfileUpdated(user, _, _): return user
|
||||
default: throw r
|
||||
}
|
||||
}
|
||||
|
||||
func apiSetContactPrefs(contactId: Int64, preferences: Preferences) async throws -> Contact? {
|
||||
let r = await chatSendCmd(.apiSetContactPrefs(contactId: contactId, preferences: preferences))
|
||||
if case let .contactPrefsUpdated(_, _, toContact) = r { return toContact }
|
||||
@@ -643,10 +653,10 @@ func apiCreateUserAddress() async throws -> String {
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiDeleteUserAddress() async throws {
|
||||
func apiDeleteUserAddress() async throws -> User? {
|
||||
let userId = try currentUserId("apiDeleteUserAddress")
|
||||
let r = await chatSendCmd(.apiDeleteMyAddress(userId: userId))
|
||||
if case .userContactLinkDeleted = r { return }
|
||||
if case let .userContactLinkDeleted(user) = r { return user }
|
||||
throw r
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,21 @@ struct ChatInfoView: View {
|
||||
contactPreferencesButton()
|
||||
}
|
||||
|
||||
if let contactLink = contact.contactLink {
|
||||
Section {
|
||||
QRCode(uri: contactLink)
|
||||
Button {
|
||||
showShareSheet(items: [contactLink])
|
||||
} label: {
|
||||
Label("Share address", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
} header: {
|
||||
Text("Address")
|
||||
} footer: {
|
||||
Text("You can share this address with your contacts to let them connect with **\(contact.displayName)**.")
|
||||
}
|
||||
}
|
||||
|
||||
Section("Servers") {
|
||||
networkStatusRow()
|
||||
.onTapGesture {
|
||||
|
||||
@@ -65,6 +65,21 @@ struct GroupMemberInfoView: View {
|
||||
}
|
||||
}
|
||||
|
||||
if let contactLink = member.contactLink {
|
||||
Section {
|
||||
QRCode(uri: contactLink)
|
||||
Button {
|
||||
showShareSheet(items: [contactLink])
|
||||
} label: {
|
||||
Label("Share address", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
} header: {
|
||||
Text("Address")
|
||||
} footer: {
|
||||
Text("You can share this address with your contacts to let them connect with **\(member.displayName)**.")
|
||||
}
|
||||
}
|
||||
|
||||
Section("Member") {
|
||||
infoRow("Group", groupInfo.displayName)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ struct CreateLinkView: View {
|
||||
)
|
||||
}
|
||||
.tag(CreateLinkTab.oneTime)
|
||||
UserAddress()
|
||||
UserAddressView(viaCreateLinkView: true)
|
||||
.tabItem {
|
||||
Label("Your contact address", systemImage: "infinity.circle")
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,6 @@
|
||||
5CB634B129E5EFEA0066AD6B /* PasscodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB634B029E5EFEA0066AD6B /* PasscodeView.swift */; };
|
||||
5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; };
|
||||
5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* UserProfile.swift */; };
|
||||
5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E327A8683A00ACCCDD /* UserAddress.swift */; };
|
||||
5CB9250D27A9432000ACCCDD /* ChatListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */; };
|
||||
5CBD285A295711D700EC2CF4 /* ImageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */; };
|
||||
5CBD285C29575B8E00EC2CF4 /* WhatsNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD285B29575B8E00EC2CF4 /* WhatsNewView.swift */; };
|
||||
@@ -116,7 +115,6 @@
|
||||
5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */; };
|
||||
5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; };
|
||||
5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */; };
|
||||
5CCA7DF32905735700C8FEBA /* AcceptRequestsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCA7DF22905735700C8FEBA /* AcceptRequestsView.swift */; };
|
||||
5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */; };
|
||||
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; };
|
||||
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; };
|
||||
@@ -172,6 +170,8 @@
|
||||
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
|
||||
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
|
||||
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
|
||||
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
|
||||
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; };
|
||||
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; };
|
||||
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; };
|
||||
D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; };
|
||||
@@ -368,7 +368,6 @@
|
||||
5CB634B029E5EFEA0066AD6B /* PasscodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasscodeView.swift; sourceTree = "<group>"; };
|
||||
5CB924D627A8563F00ACCCDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
5CB924E027A867BA00ACCCDD /* UserProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfile.swift; sourceTree = "<group>"; };
|
||||
5CB924E327A8683A00ACCCDD /* UserAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddress.swift; sourceTree = "<group>"; };
|
||||
5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListNavLink.swift; sourceTree = "<group>"; };
|
||||
5CBD285529565CAE00EC2CF4 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CBD285629565CAE00EC2CF4 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
@@ -384,7 +383,6 @@
|
||||
5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRcvDecryptionError.swift; sourceTree = "<group>"; };
|
||||
5CCA7DF22905735700C8FEBA /* AcceptRequestsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcceptRequestsView.swift; sourceTree = "<group>"; };
|
||||
5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.swift; sourceTree = "<group>"; };
|
||||
5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = "<group>"; };
|
||||
5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = "<group>"; };
|
||||
@@ -439,6 +437,8 @@
|
||||
649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; };
|
||||
64AA1C6827EE10C800AC7277 /* ContextItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextItemView.swift; sourceTree = "<group>"; };
|
||||
64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = "<group>"; };
|
||||
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = "<group>"; };
|
||||
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = "<group>"; };
|
||||
64DAE1502809D9F5000DA960 /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
|
||||
64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = "<group>"; };
|
||||
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = "<group>"; };
|
||||
@@ -709,8 +709,6 @@
|
||||
5C5DB70D289ABDD200730FFF /* AppearanceSettings.swift */,
|
||||
5C05DF522840AA1D00C683F9 /* CallSettings.swift */,
|
||||
5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */,
|
||||
5CB924E327A8683A00ACCCDD /* UserAddress.swift */,
|
||||
5CCA7DF22905735700C8FEBA /* AcceptRequestsView.swift */,
|
||||
5CB924E027A867BA00ACCCDD /* UserProfile.swift */,
|
||||
5CC036DF29C488D500C0EF20 /* HiddenProfileView.swift */,
|
||||
5C577F7C27C83AA10006112D /* MarkdownHelp.swift */,
|
||||
@@ -722,6 +720,8 @@
|
||||
18415845648CA4F5A8BCA272 /* UserProfilesView.swift */,
|
||||
5C65F341297D3F3600B67AF3 /* VersionView.swift */,
|
||||
5C65DAF829D0CC20003CEE45 /* DeveloperView.swift */,
|
||||
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */,
|
||||
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */,
|
||||
);
|
||||
path = UserSettings;
|
||||
sourceTree = "<group>";
|
||||
@@ -1069,7 +1069,6 @@
|
||||
5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */,
|
||||
5C3F1D562842B68D00EC8A82 /* IntegrityErrorItemView.swift in Sources */,
|
||||
5C029EAA283942EA004A9677 /* CallController.swift in Sources */,
|
||||
5CCA7DF32905735700C8FEBA /* AcceptRequestsView.swift in Sources */,
|
||||
5CBE6C142944CC12002D9531 /* ScanCodeView.swift in Sources */,
|
||||
5CC036E029C488D500C0EF20 /* HiddenProfileView.swift in Sources */,
|
||||
5C5346A827B59A6A004DF848 /* ChatHelp.swift in Sources */,
|
||||
@@ -1089,7 +1088,6 @@
|
||||
D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */,
|
||||
5C00164428A26FBC0094D739 /* ContextMenu.swift in Sources */,
|
||||
5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */,
|
||||
5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */,
|
||||
5C65F343297D45E100B67AF3 /* VersionView.swift in Sources */,
|
||||
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */,
|
||||
5CB0BA90282713D900B3292C /* SimpleXInfo.swift in Sources */,
|
||||
@@ -1126,6 +1124,7 @@
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */,
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */,
|
||||
6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */,
|
||||
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */,
|
||||
5C971E2127AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */,
|
||||
5C55A921283CCCB700C4E99E /* IncomingCallView.swift in Sources */,
|
||||
6454036F2822A9750090DDFF /* ComposeFileView.swift in Sources */,
|
||||
@@ -1137,6 +1136,7 @@
|
||||
6407BA83295DA85D0082BA18 /* CIInvalidJSONView.swift in Sources */,
|
||||
644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */,
|
||||
5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */,
|
||||
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */,
|
||||
6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */,
|
||||
5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */,
|
||||
5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */,
|
||||
|
||||
@@ -85,6 +85,7 @@ public enum ChatCommand {
|
||||
case apiCreateMyAddress(userId: Int64)
|
||||
case apiDeleteMyAddress(userId: Int64)
|
||||
case apiShowMyAddress(userId: Int64)
|
||||
case apiSetProfileAddress(userId: Int64, on: Bool)
|
||||
case apiAddressAutoAccept(userId: Int64, autoAccept: AutoAccept?)
|
||||
case apiAcceptContact(contactReqId: Int64)
|
||||
case apiRejectContact(contactReqId: Int64)
|
||||
@@ -189,6 +190,7 @@ public enum ChatCommand {
|
||||
case let .apiCreateMyAddress(userId): return "/_address \(userId)"
|
||||
case let .apiDeleteMyAddress(userId): return "/_delete_address \(userId)"
|
||||
case let .apiShowMyAddress(userId): return "/_show_address \(userId)"
|
||||
case let .apiSetProfileAddress(userId, on): return "/_profile_address \(userId) \(onOff(on))"
|
||||
case let .apiAddressAutoAccept(userId, autoAccept): return "/_auto_accept \(userId) \(AutoAccept.cmdString(autoAccept))"
|
||||
case let .apiAcceptContact(contactReqId): return "/_accept \(contactReqId)"
|
||||
case let .apiRejectContact(contactReqId): return "/_reject \(contactReqId)"
|
||||
@@ -290,6 +292,7 @@ public enum ChatCommand {
|
||||
case .apiCreateMyAddress: return "apiCreateMyAddress"
|
||||
case .apiDeleteMyAddress: return "apiDeleteMyAddress"
|
||||
case .apiShowMyAddress: return "apiShowMyAddress"
|
||||
case .apiSetProfileAddress: return "apiSetProfileAddress"
|
||||
case .apiAddressAutoAccept: return "apiAddressAutoAccept"
|
||||
case .apiAcceptContact: return "apiAcceptContact"
|
||||
case .apiRejectContact: return "apiRejectContact"
|
||||
|
||||
@@ -33,6 +33,10 @@ public struct User: Decodable, NamedChat, Identifiable {
|
||||
activeUser || showNtfs
|
||||
}
|
||||
|
||||
public var addressShared: Bool {
|
||||
profile.contactLink != nil
|
||||
}
|
||||
|
||||
public static let sampleData = User(
|
||||
userId: 1,
|
||||
userContactId: 1,
|
||||
@@ -71,16 +75,24 @@ public typealias ContactName = String
|
||||
public typealias GroupName = String
|
||||
|
||||
public struct Profile: Codable, NamedChat {
|
||||
public init(displayName: String, fullName: String, image: String? = nil, preferences: Preferences? = nil) {
|
||||
public init(
|
||||
displayName: String,
|
||||
fullName: String,
|
||||
image: String? = nil,
|
||||
contactLink: String? = nil,
|
||||
preferences: Preferences? = nil
|
||||
) {
|
||||
self.displayName = displayName
|
||||
self.fullName = fullName
|
||||
self.image = image
|
||||
self.contactLink = contactLink
|
||||
self.preferences = preferences
|
||||
}
|
||||
|
||||
public var displayName: String
|
||||
public var fullName: String
|
||||
public var image: String?
|
||||
public var contactLink: String?
|
||||
public var preferences: Preferences?
|
||||
public var localAlias: String { get { "" } }
|
||||
|
||||
@@ -95,11 +107,20 @@ public struct Profile: Codable, NamedChat {
|
||||
}
|
||||
|
||||
public struct LocalProfile: Codable, NamedChat {
|
||||
public init(profileId: Int64, displayName: String, fullName: String, image: String? = nil, preferences: Preferences? = nil, localAlias: String) {
|
||||
public init(
|
||||
profileId: Int64,
|
||||
displayName: String,
|
||||
fullName: String,
|
||||
image: String? = nil,
|
||||
contactLink: String? = nil,
|
||||
preferences: Preferences? = nil,
|
||||
localAlias: String
|
||||
) {
|
||||
self.profileId = profileId
|
||||
self.displayName = displayName
|
||||
self.fullName = fullName
|
||||
self.image = image
|
||||
self.contactLink = contactLink
|
||||
self.preferences = preferences
|
||||
self.localAlias = localAlias
|
||||
}
|
||||
@@ -108,6 +129,7 @@ public struct LocalProfile: Codable, NamedChat {
|
||||
public var displayName: String
|
||||
public var fullName: String
|
||||
public var image: String?
|
||||
public var contactLink: String?
|
||||
public var preferences: Preferences?
|
||||
public var localAlias: String
|
||||
|
||||
@@ -127,11 +149,11 @@ public struct LocalProfile: Codable, NamedChat {
|
||||
}
|
||||
|
||||
public func toLocalProfile (_ profileId: Int64, _ profile: Profile, _ localAlias: String) -> LocalProfile {
|
||||
LocalProfile(profileId: profileId, displayName: profile.displayName, fullName: profile.fullName, image: profile.image, preferences: profile.preferences, localAlias: localAlias)
|
||||
LocalProfile(profileId: profileId, displayName: profile.displayName, fullName: profile.fullName, image: profile.image, contactLink: profile.contactLink, preferences: profile.preferences, localAlias: localAlias)
|
||||
}
|
||||
|
||||
public func fromLocalProfile (_ profile: LocalProfile) -> Profile {
|
||||
Profile(displayName: profile.displayName, fullName: profile.fullName, image: profile.image, preferences: profile.preferences)
|
||||
Profile(displayName: profile.displayName, fullName: profile.fullName, image: profile.image, contactLink: profile.contactLink, preferences: profile.preferences)
|
||||
}
|
||||
|
||||
public enum ChatType: String {
|
||||
@@ -1164,6 +1186,7 @@ public struct Contact: Identifiable, Decodable, NamedChat {
|
||||
public var displayName: String { localAlias == "" ? profile.displayName : localAlias }
|
||||
public var fullName: String { get { profile.fullName } }
|
||||
public var image: String? { get { profile.image } }
|
||||
public var contactLink: String? { get { profile.contactLink } }
|
||||
public var localAlias: String { profile.localAlias }
|
||||
public var verified: Bool { activeConn.connectionCode != nil }
|
||||
|
||||
@@ -1508,6 +1531,7 @@ public struct GroupMember: Identifiable, Decodable {
|
||||
}
|
||||
public var fullName: String { get { memberProfile.fullName } }
|
||||
public var image: String? { get { memberProfile.image } }
|
||||
public var contactLink: String? { get { memberProfile.contactLink } }
|
||||
public var verified: Bool { activeConn?.connectionCode != nil }
|
||||
|
||||
var directChatId: ChatId? {
|
||||
|
||||
Reference in New Issue
Block a user