mirror of https://github.com/oxen-io/session-ios
ui: refactor QR code screen with SwiftUI and update QR code style
parent
5eca31aaa2
commit
9faf835617
@ -1,13 +0,0 @@
|
||||
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct QRCodeScreen: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
QRCodeScreen()
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
import SessionUIKit
|
||||
import SignalUtilitiesKit
|
||||
import SessionUtilitiesKit
|
||||
import AVFoundation
|
||||
|
||||
struct QRCodeScreen: View {
|
||||
@EnvironmentObject var host: HostWrapper
|
||||
|
||||
@State var tabIndex = 0
|
||||
@State private var accountId: String = ""
|
||||
@State private var errorString: String? = nil
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .topLeading) {
|
||||
if #available(iOS 14.0, *) {
|
||||
ThemeManager.currentTheme.colorSwiftUI(for: .backgroundPrimary).ignoresSafeArea()
|
||||
} else {
|
||||
ThemeManager.currentTheme.colorSwiftUI(for: .backgroundPrimary)
|
||||
}
|
||||
VStack(
|
||||
spacing: 0
|
||||
){
|
||||
CustomTopTabBar(
|
||||
tabIndex: $tabIndex,
|
||||
tabTitles: [
|
||||
"settings_view_qr_code_tab_title".localized(),
|
||||
"settings_scan_qr_code_tab_title".localized()
|
||||
]
|
||||
).frame(maxWidth: .infinity)
|
||||
|
||||
if tabIndex == 0 {
|
||||
MyQRCodeScreen()
|
||||
}
|
||||
else {
|
||||
ScanQRCodeScreen(
|
||||
$accountId,
|
||||
error: $errorString,
|
||||
continueAction: continueWithAccountId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func startNewPrivateChatIfPossible(with hexEncodedPublicKey: String, onError: (() -> ())?) {
|
||||
if !KeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) {
|
||||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "invalid_session_id".localized(),
|
||||
body: .text("INVALID_SESSION_ID_MESSAGE".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text,
|
||||
afterClosed: onError
|
||||
)
|
||||
)
|
||||
self.host.controller?.present(modal, animated: true)
|
||||
}
|
||||
else {
|
||||
SessionApp.presentConversationCreatingIfNeeded(
|
||||
for: hexEncodedPublicKey,
|
||||
variant: .contact,
|
||||
dismissing: self.host.controller,
|
||||
animated: false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func continueWithAccountId(onError: (() -> ())?) {
|
||||
let hexEncodedPublicKey = accountId
|
||||
startNewPrivateChatIfPossible(with: hexEncodedPublicKey, onError: onError)
|
||||
}
|
||||
}
|
||||
|
||||
struct MyQRCodeScreen: View {
|
||||
var body: some View{
|
||||
VStack(
|
||||
spacing: Values.mediumSpacing
|
||||
) {
|
||||
QRCodeView(
|
||||
string: getUserHexEncodedPublicKey(),
|
||||
hasBackground: false,
|
||||
logo: "SessionWhite40",
|
||||
themeStyle: ThemeManager.currentTheme.interfaceStyle
|
||||
)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
|
||||
Text("settings_view_my_qr_code_explanation".localized())
|
||||
.font(.system(size: Values.verySmallFontSize))
|
||||
.foregroundColor(themeColor: .textSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.padding(.horizontal, Values.mediumSpacing)
|
||||
.padding(.all, Values.veryLargeSpacing)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
QRCodeScreen()
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
import SessionUIKit
|
||||
|
||||
struct ScanQRCodeScreen: View {
|
||||
@Binding var result: String
|
||||
@Binding var error: String?
|
||||
@State var hasCameraAccess: Bool = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized)
|
||||
|
||||
var continueAction: (((() -> ())?) -> Void)?
|
||||
|
||||
init(
|
||||
_ result: Binding<String>,
|
||||
error: Binding<String?>,
|
||||
continueAction: (((() -> ())?) -> Void)?
|
||||
) {
|
||||
self._result = result
|
||||
self._error = error
|
||||
self.continueAction = continueAction
|
||||
}
|
||||
|
||||
var body: some View{
|
||||
ZStack{
|
||||
if hasCameraAccess {
|
||||
VStack {
|
||||
QRCodeScanningVC_SwiftUI { result, onError in
|
||||
self.result = result
|
||||
continueAction?(onError)
|
||||
}
|
||||
}
|
||||
.frame(
|
||||
maxWidth: .infinity,
|
||||
maxHeight: .infinity
|
||||
)
|
||||
} else {
|
||||
VStack(
|
||||
alignment: .center,
|
||||
spacing: Values.mediumSpacing
|
||||
) {
|
||||
Spacer()
|
||||
|
||||
Text("vc_scan_qr_code_camera_access_explanation".localized())
|
||||
.font(.system(size: Values.smallFontSize))
|
||||
.foregroundColor(themeColor: .textPrimary)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Button {
|
||||
requestCameraAccess()
|
||||
} label: {
|
||||
Text("continue_2".localized())
|
||||
.bold()
|
||||
.font(.system(size: Values.mediumFontSize))
|
||||
.foregroundColor(themeColor: .primary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, Values.massiveSpacing)
|
||||
.padding(.bottom, Values.massiveSpacing)
|
||||
}
|
||||
}
|
||||
.toastView(message: $error)
|
||||
}
|
||||
|
||||
private func requestCameraAccess() {
|
||||
Permissions.requestCameraPermissionIfNeeded {
|
||||
hasCameraAccess.toggle()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
import SessionUIKit
|
||||
|
||||
struct TabBarButton: View {
|
||||
@Binding var isSelected: Bool
|
||||
|
||||
let text: String
|
||||
|
||||
var body: some View {
|
||||
ZStack(
|
||||
alignment: .bottom
|
||||
) {
|
||||
Text(text)
|
||||
.bold()
|
||||
.font(.system(size: Values.mediumFontSize))
|
||||
.foregroundColor(themeColor: .textPrimary)
|
||||
.padding(.bottom, 5)
|
||||
.frame(
|
||||
maxWidth: .infinity,
|
||||
maxHeight: .infinity
|
||||
)
|
||||
if isSelected {
|
||||
Rectangle()
|
||||
.foregroundColor(themeColor: .primary)
|
||||
.frame(
|
||||
maxWidth: .infinity,
|
||||
maxHeight: 5
|
||||
)
|
||||
.padding(.horizontal, Values.verySmallSpacing)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CustomTopTabBar: View {
|
||||
@Binding var tabIndex: Int
|
||||
let tabTitles: [String]
|
||||
|
||||
private static let height = isIPhone5OrSmaller ? CGFloat(32) : CGFloat(48)
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 0) {
|
||||
ForEach(0..<tabTitles.count, id: \.self) { index in
|
||||
TabBarButton(
|
||||
isSelected: .constant(tabIndex == index),
|
||||
text: tabTitles[index]
|
||||
)
|
||||
.onTapGesture { onButtonTapped(index: index) }
|
||||
}
|
||||
}
|
||||
.frame(
|
||||
maxWidth: .infinity,
|
||||
maxHeight: Self.height
|
||||
)
|
||||
.border(width: 1, edges: [.bottom], color: .borderSeparator)
|
||||
}
|
||||
|
||||
private func onButtonTapped(index: Int) {
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
tabIndex = index
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue