mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-28 20:29:53 +00:00
ios: create address during onboarding (#2362)
* ios: create address during onboarding * contact picker * email wip * send email w/t leaving app * fomatting * layout, texts * remove contact picker, add email button to address page * refactor * refactor --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
parent
6f11913359
commit
551ed202be
9 changed files with 329 additions and 10 deletions
|
@ -1057,7 +1057,7 @@ func startChat(refreshInvitations: Bool = true) throws {
|
|||
}
|
||||
withAnimation {
|
||||
m.onboardingStage = m.onboardingStage == .step2_CreateProfile && m.users.count == 1
|
||||
? .step3_SetNotificationsMode
|
||||
? .step3_CreateSimpleXAddress
|
||||
: .onboardingComplete
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ struct MigrateToAppGroupView: View {
|
|||
do {
|
||||
resetChatCtrl()
|
||||
try initializeChat(start: true)
|
||||
chatModel.onboardingStage = .step3_SetNotificationsMode
|
||||
chatModel.onboardingStage = .step4_SetNotificationsMode
|
||||
setV3DBMigration(.ready)
|
||||
} catch let error {
|
||||
dbContainerGroupDefault.set(.documents)
|
||||
|
|
61
apps/ios/Shared/Views/Helpers/MailView.swift
Normal file
61
apps/ios/Shared/Views/Helpers/MailView.swift
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// MailView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 01.05.2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import MessageUI
|
||||
|
||||
struct MailView: UIViewControllerRepresentable {
|
||||
@Binding var isShowing: Bool
|
||||
@Binding var result: Result<MFMailComposeResult, Error>?
|
||||
var subject = ""
|
||||
var messageBody = ""
|
||||
|
||||
class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
|
||||
@Binding var isShowing: Bool
|
||||
@Binding var result: Result<MFMailComposeResult, Error>?
|
||||
|
||||
init(isShowing: Binding<Bool>,
|
||||
result: Binding<Result<MFMailComposeResult, Error>?>) {
|
||||
_isShowing = isShowing
|
||||
_result = result
|
||||
}
|
||||
|
||||
func mailComposeController(
|
||||
_ controller: MFMailComposeViewController,
|
||||
didFinishWith result: MFMailComposeResult,
|
||||
error: Error?
|
||||
) {
|
||||
defer {
|
||||
isShowing = false
|
||||
}
|
||||
if let error = error {
|
||||
self.result = .failure(error)
|
||||
return
|
||||
}
|
||||
self.result = .success(result)
|
||||
}
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
return Coordinator(isShowing: $isShowing, result: $result)
|
||||
}
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController {
|
||||
let vc = MFMailComposeViewController()
|
||||
vc.setSubject(subject)
|
||||
vc.setMessageBody(messageBody, isHTML: true)
|
||||
vc.mailComposeDelegate = context.coordinator
|
||||
return vc
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: MFMailComposeViewController,
|
||||
context: UIViewControllerRepresentableContext<MailView>) {
|
||||
|
||||
}
|
||||
}
|
|
@ -34,7 +34,9 @@ struct CreateProfile: View {
|
|||
VStack(alignment: .leading) {
|
||||
Text("Create your profile")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.padding(.bottom, 4)
|
||||
.frame(maxWidth: .infinity)
|
||||
Text("Your profile, contacts and delivered messages are stored on your device.")
|
||||
.padding(.bottom, 4)
|
||||
Text("The profile is only shared with your contacts.")
|
||||
|
@ -122,7 +124,7 @@ struct CreateProfile: View {
|
|||
m.currentUser = try apiCreateActiveUser(profile)
|
||||
if m.users.isEmpty {
|
||||
try startChat()
|
||||
withAnimation { m.onboardingStage = .step3_SetNotificationsMode }
|
||||
withAnimation { m.onboardingStage = .step3_CreateSimpleXAddress }
|
||||
} else {
|
||||
dismiss()
|
||||
m.users = try listUsers()
|
||||
|
|
208
apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift
Normal file
208
apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift
Normal file
|
@ -0,0 +1,208 @@
|
|||
//
|
||||
// CreateSimpleXAddress.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 28.04.2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Contacts
|
||||
import ContactsUI
|
||||
import MessageUI
|
||||
import SimpleXChat
|
||||
|
||||
struct CreateSimpleXAddress: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@State private var progressIndicator = false
|
||||
@State private var showMailView = false
|
||||
@State private var mailViewResult: Result<MFMailComposeResult, Error>? = nil
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { g in
|
||||
ScrollView {
|
||||
ZStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text("SimpleX Address")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Spacer()
|
||||
|
||||
if let userAddress = m.userAddress {
|
||||
QRCode(uri: userAddress.connReqContact)
|
||||
.frame(maxHeight: g.size.width)
|
||||
shareQRCodeButton(userAddress)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Spacer()
|
||||
|
||||
shareViaEmailButton(userAddress)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Spacer()
|
||||
|
||||
continueButton()
|
||||
.padding(.bottom, 8)
|
||||
.frame(maxWidth: .infinity)
|
||||
} else {
|
||||
createAddressButton()
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Spacer()
|
||||
|
||||
skipButton()
|
||||
.padding(.bottom, 56)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
.frame(minHeight: g.size.height)
|
||||
|
||||
if progressIndicator {
|
||||
ProgressView().scaleEffect(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
.padding()
|
||||
}
|
||||
|
||||
private func createAddressButton() -> some View {
|
||||
VStack(spacing: 8) {
|
||||
Button {
|
||||
progressIndicator = true
|
||||
Task {
|
||||
do {
|
||||
let connReqContact = try await apiCreateUserAddress()
|
||||
DispatchQueue.main.async {
|
||||
m.userAddress = UserContactLink(connReqContact: connReqContact)
|
||||
}
|
||||
if let u = try await apiSetProfileAddress(on: true) {
|
||||
DispatchQueue.main.async {
|
||||
m.updateUser(u)
|
||||
}
|
||||
}
|
||||
await MainActor.run { progressIndicator = false }
|
||||
} catch let error {
|
||||
logger.error("CreateSimpleXAddress create address: \(responseError(error))")
|
||||
await MainActor.run { progressIndicator = false }
|
||||
let a = getErrorAlert(error, "Error creating address")
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: a.title,
|
||||
message: a.message
|
||||
)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text("Create SimpleX address").font(.title)
|
||||
}
|
||||
Group {
|
||||
Text("Your contacts in SimpleX will see it.\nYou can change it in Settings.")
|
||||
}
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.footnote)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
}
|
||||
|
||||
private func skipButton() -> some View {
|
||||
VStack(spacing: 8) {
|
||||
Button {
|
||||
withAnimation {
|
||||
m.onboardingStage = .step4_SetNotificationsMode
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Don't create address")
|
||||
Image(systemName: "chevron.right")
|
||||
}
|
||||
}
|
||||
Text("You can create it later").font(.footnote)
|
||||
}
|
||||
}
|
||||
|
||||
private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View {
|
||||
Button {
|
||||
showShareSheet(items: [userAddress.connReqContact])
|
||||
} label: {
|
||||
Label("Share", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
}
|
||||
|
||||
private func shareViaEmailButton(_ userAddress: UserContactLink) -> some View {
|
||||
Button {
|
||||
showMailView = true
|
||||
} label: {
|
||||
Label("Invite friends", systemImage: "envelope")
|
||||
.font(.title2)
|
||||
}
|
||||
.sheet(isPresented: $showMailView) {
|
||||
SendAddressMailView(
|
||||
showMailView: $showMailView,
|
||||
mailViewResult: $mailViewResult,
|
||||
userAddress: userAddress
|
||||
)
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
}
|
||||
.onChange(of: mailViewResult == nil) { _ in
|
||||
if let r = mailViewResult {
|
||||
switch r {
|
||||
case let .success(composeResult):
|
||||
switch composeResult {
|
||||
case .sent:
|
||||
m.onboardingStage = .step4_SetNotificationsMode
|
||||
default: ()
|
||||
}
|
||||
case let .failure(error):
|
||||
logger.error("CreateSimpleXAddress share via email: \(responseError(error))")
|
||||
let a = getErrorAlert(error, "Error sending email")
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: a.title,
|
||||
message: a.message
|
||||
)
|
||||
}
|
||||
mailViewResult = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func continueButton() -> some View {
|
||||
Button {
|
||||
withAnimation {
|
||||
m.onboardingStage = .step4_SetNotificationsMode
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Continue")
|
||||
Image(systemName: "greaterthan")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SendAddressMailView: View {
|
||||
@Binding var showMailView: Bool
|
||||
@Binding var mailViewResult: Result<MFMailComposeResult, Error>?
|
||||
var userAddress: UserContactLink
|
||||
|
||||
var body: some View {
|
||||
let messageBody = """
|
||||
<p>Hi!</p>
|
||||
<p><a href="\(userAddress.connReqContact)">Connect to me via SimpleX Chat</a></p>
|
||||
"""
|
||||
MailView(
|
||||
isShowing: self.$showMailView,
|
||||
result: $mailViewResult,
|
||||
subject: "Let's talk in SimpleX Chat",
|
||||
messageBody: messageBody
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateSimpleXAddress_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CreateSimpleXAddress()
|
||||
}
|
||||
}
|
|
@ -15,7 +15,8 @@ struct OnboardingView: View {
|
|||
switch onboarding {
|
||||
case .step1_SimpleXInfo: SimpleXInfo(onboarding: true)
|
||||
case .step2_CreateProfile: CreateProfile()
|
||||
case .step3_SetNotificationsMode: SetNotificationsMode()
|
||||
case .step3_CreateSimpleXAddress: CreateSimpleXAddress()
|
||||
case .step4_SetNotificationsMode: SetNotificationsMode()
|
||||
case .onboardingComplete: EmptyView()
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +25,8 @@ struct OnboardingView: View {
|
|||
enum OnboardingStage {
|
||||
case step1_SimpleXInfo
|
||||
case step2_CreateProfile
|
||||
case step3_SetNotificationsMode
|
||||
case step3_CreateSimpleXAddress
|
||||
case step4_SetNotificationsMode
|
||||
case onboardingComplete
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,10 @@ struct SetNotificationsMode: View {
|
|||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
Text("Push notifications").font(.largeTitle)
|
||||
Text("Push notifications")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Text("Send notifications:")
|
||||
ForEach(NotificationsMode.values) { mode in
|
||||
|
@ -62,9 +65,10 @@ struct SetNotificationsMode: View {
|
|||
m.notificationMode = mode
|
||||
}
|
||||
} catch let error {
|
||||
let a = getErrorAlert(error, "Error enabling notifications")
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Error enabling notifications",
|
||||
message: "\(responseError(error))"
|
||||
title: a.title,
|
||||
message: a.message
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import MessageUI
|
||||
import SimpleXChat
|
||||
|
||||
struct UserAddressView: View {
|
||||
|
@ -17,6 +18,8 @@ struct UserAddressView: View {
|
|||
@State private var aas = AutoAcceptState()
|
||||
@State private var savedAAS = AutoAcceptState()
|
||||
@State private var ignoreShareViaProfileChange = false
|
||||
@State private var showMailView = false
|
||||
@State private var mailViewResult: Result<MFMailComposeResult, Error>? = nil
|
||||
@State private var alert: UserAddressAlert?
|
||||
@State private var showSaveDialogue = false
|
||||
@State private var progressIndicator = false
|
||||
|
@ -189,6 +192,7 @@ struct UserAddressView: View {
|
|||
Section {
|
||||
QRCode(uri: userAddress.connReqContact)
|
||||
shareQRCodeButton(userAddress)
|
||||
shareViaEmailButton(userAddress)
|
||||
shareWithContactsButton()
|
||||
autoAcceptToggle()
|
||||
learnMoreButton()
|
||||
|
@ -240,9 +244,9 @@ struct UserAddressView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private func shareQRCodeButton(_ userAdress: UserContactLink) -> some View {
|
||||
private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View {
|
||||
Button {
|
||||
showShareSheet(items: [userAdress.connReqContact])
|
||||
showShareSheet(items: [userAddress.connReqContact])
|
||||
} label: {
|
||||
settingsRow("square.and.arrow.up") {
|
||||
Text("Share address")
|
||||
|
@ -250,6 +254,36 @@ struct UserAddressView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private func shareViaEmailButton(_ userAddress: UserContactLink) -> some View {
|
||||
Button {
|
||||
showMailView = true
|
||||
} label: {
|
||||
settingsRow("envelope") {
|
||||
Text("Invite friends")
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showMailView) {
|
||||
SendAddressMailView(
|
||||
showMailView: $showMailView,
|
||||
mailViewResult: $mailViewResult,
|
||||
userAddress: userAddress
|
||||
)
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
}
|
||||
.onChange(of: mailViewResult == nil) { _ in
|
||||
if let r = mailViewResult {
|
||||
switch r {
|
||||
case .success: ()
|
||||
case let .failure(error):
|
||||
logger.error("UserAddressView share via email: \(responseError(error))")
|
||||
let a = getErrorAlert(error, "Error sending email")
|
||||
alert = .error(title: a.title, error: a.message)
|
||||
}
|
||||
mailViewResult = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func autoAcceptToggle() -> some View {
|
||||
settingsRow("checkmark") {
|
||||
Toggle("Auto-accept", isOn: $aas.enable)
|
||||
|
|
|
@ -151,6 +151,8 @@
|
|||
6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */; };
|
||||
6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */; };
|
||||
6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6442E0BD2880182D00CEC0F9 /* GroupChatInfoView.swift */; };
|
||||
64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */; };
|
||||
64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DCB29FFE3E800E3D48D /* MailView.swift */; };
|
||||
6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */; };
|
||||
644E72A629F18C00003534BE /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644E72A129F18C00003534BE /* libgmpxx.a */; };
|
||||
644E72A729F18C00003534BE /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644E72A229F18C00003534BE /* libffi.a */; };
|
||||
|
@ -418,6 +420,8 @@
|
|||
6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = "<group>"; };
|
||||
6442E0B9287F169300CEC0F9 /* AddGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupView.swift; sourceTree = "<group>"; };
|
||||
6442E0BD2880182D00CEC0F9 /* GroupChatInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatInfoView.swift; sourceTree = "<group>"; };
|
||||
64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSimpleXAddress.swift; sourceTree = "<group>"; };
|
||||
64466DCB29FFE3E800E3D48D /* MailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailView.swift; sourceTree = "<group>"; };
|
||||
6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupLinkView.swift; sourceTree = "<group>"; };
|
||||
644E72A129F18C00003534BE /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
644E72A229F18C00003534BE /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
|
@ -604,6 +608,7 @@
|
|||
18415A7F0F189D87DEFEABCA /* PressedButtonStyle.swift */,
|
||||
5CCB939B297EFCB100399E78 /* NavStackCompat.swift */,
|
||||
18415DAAAD1ADBEDB0EDA852 /* VideoPlayerView.swift */,
|
||||
64466DCB29FFE3E800E3D48D /* MailView.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
@ -668,6 +673,7 @@
|
|||
5CB0BA8F282713D900B3292C /* SimpleXInfo.swift */,
|
||||
5CB0BA992827FD8800B3292C /* HowItWorks.swift */,
|
||||
5CB0BA91282713FD00B3292C /* CreateProfile.swift */,
|
||||
64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */,
|
||||
5C9A5BDA2871E05400A5B906 /* SetNotificationsMode.swift */,
|
||||
5CBD285B29575B8E00EC2CF4 /* WhatsNewView.swift */,
|
||||
);
|
||||
|
@ -1085,6 +1091,7 @@
|
|||
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */,
|
||||
5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */,
|
||||
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */,
|
||||
64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */,
|
||||
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */,
|
||||
5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */,
|
||||
5C10D88A28F187F300E58BF0 /* FullScreenMediaView.swift in Sources */,
|
||||
|
@ -1129,6 +1136,7 @@
|
|||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */,
|
||||
6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */,
|
||||
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */,
|
||||
64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */,
|
||||
5C971E2127AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */,
|
||||
5C55A921283CCCB700C4E99E /* IncomingCallView.swift in Sources */,
|
||||
6454036F2822A9750090DDFF /* ComposeFileView.swift in Sources */,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue