2020-12-04 02:15:37 +01:00
//
// A d d R e a d e r A P I A c c o u n t V i e w . s w i f t
// M u l t i p l a t f o r m m a c O S
//
// C r e a t e d b y S t u a r t B r e c k e n r i d g e o n 0 3 / 1 2 / 2 0 2 0 .
// C o p y r i g h t © 2 0 2 0 R a n c h e r o S o f t w a r e . A l l r i g h t s r e s e r v e d .
//
import SwiftUI
import Account
2020-12-05 14:09:34 +01:00
import RSCore
import RSWeb
import Secrets
2020-12-04 02:15:37 +01:00
struct AddReaderAPIAccountView : View {
@ Environment ( \ . presentationMode ) var presentationMode
2020-12-05 14:09:34 +01:00
@ StateObject private var model = AddReaderAPIViewModel ( )
2020-12-04 02:15:37 +01:00
public var accountType : AccountType
2020-12-05 14:09:34 +01:00
var body : some View {
2020-12-05 15:58:11 +01:00
#if os ( macOS )
macBody
#else
2020-12-07 12:22:35 +01:00
NavigationView {
iosBody
}
2020-12-05 15:58:11 +01:00
#endif
}
#if os ( iOS )
var iosBody : some View {
2020-12-07 12:22:35 +01:00
List {
Section ( header : formHeader , content : {
TextField ( " Email " , text : $ model . username )
if model . showPassword = = false {
ZStack {
HStack {
SecureField ( " Password " , text : $ model . password )
Spacer ( )
Image ( systemName : " eye.fill " )
. foregroundColor ( . accentColor )
. onTapGesture {
model . showPassword = true
}
}
}
}
else {
ZStack {
HStack {
TextField ( " Password " , text : $ model . password )
Spacer ( )
Image ( systemName : " eye.slash.fill " )
. foregroundColor ( . accentColor )
. onTapGesture {
model . showPassword = false
}
}
}
}
if accountType = = . freshRSS {
TextField ( " API URL " , text : $ model . apiUrl )
}
} )
Section ( footer : formFooter , content : {
Button ( action : {
model . authenticateReaderAccount ( accountType )
} , label : {
HStack {
Spacer ( )
2020-12-07 12:52:35 +01:00
Text ( " Add Account " )
2020-12-07 12:22:35 +01:00
Spacer ( )
}
} ) . disabled ( createDisabled ( ) )
} )
}
. navigationBarItems ( leading :
Button ( action : {
presentationMode . wrappedValue . dismiss ( )
} , label : {
Text ( " Cancel " )
} ) )
. listStyle ( InsetGroupedListStyle ( ) )
. navigationBarTitleDisplayMode ( . inline )
. navigationTitle ( Text ( accountType . localizedAccountName ( ) ) )
. alert ( isPresented : $ model . showError , content : {
Alert ( title : Text ( " Sign In Error " ) , message : Text ( model . accountUpdateError . description ) , dismissButton : . cancel ( Text ( " Dismiss " ) ) )
} )
. onReceive ( model . $ canDismiss , perform : { value in
if value = = true {
presentationMode . wrappedValue . dismiss ( )
}
} )
2020-12-05 15:58:11 +01:00
}
#endif
#if os ( macOS )
var macBody : some View {
2020-12-04 02:15:37 +01:00
VStack {
HStack ( spacing : 16 ) {
VStack ( alignment : . leading ) {
accountType . image ( )
. resizable ( )
. frame ( width : 50 , height : 50 )
Spacer ( )
}
VStack ( alignment : . leading , spacing : 8 ) {
Text ( " Sign in to your \( accountType . localizedAccountName ( ) ) account. " )
. font ( . headline )
HStack {
if accountType = = . freshRSS {
Text ( " Don't have a \( accountType . localizedAccountName ( ) ) instance? " )
. font ( . callout )
} else {
Text ( " Don't have an \( accountType . localizedAccountName ( ) ) account? " )
. font ( . callout )
}
Button ( action : {
2020-12-06 00:58:20 +01:00
model . presentSignUpOption ( accountType )
2020-12-04 02:15:37 +01:00
} , label : {
Text ( accountType = = . freshRSS ? " Find out more. " : " Sign up here. " ) . font ( . callout )
} ) . buttonStyle ( LinkButtonStyle ( ) )
}
HStack {
VStack ( alignment : . trailing , spacing : 14 ) {
Text ( " Email " )
Text ( " Password " )
2020-12-05 14:09:34 +01:00
if accountType = = . freshRSS {
Text ( " API URL " )
}
2020-12-04 02:15:37 +01:00
}
VStack ( spacing : 8 ) {
2020-12-05 14:09:34 +01:00
TextField ( " me@email.com " , text : $ model . username )
SecureField ( " ••••••••••• " , text : $ model . password )
if accountType = = . freshRSS {
TextField ( " https://myfreshrss.rocks " , text : $ model . apiUrl )
}
2020-12-04 02:15:37 +01:00
}
}
Text ( " Your username and password will be encrypted and stored in Keychain. " )
. foregroundColor ( . secondary )
. font ( . callout )
. lineLimit ( 2 )
. padding ( . top , 4 )
Spacer ( )
HStack ( spacing : 8 ) {
Spacer ( )
2020-12-05 14:09:34 +01:00
ProgressView ( )
. scaleEffect ( CGSize ( width : 0.5 , height : 0.5 ) )
. hidden ( ! model . isAuthenticating )
2020-12-04 02:15:37 +01:00
Button ( action : {
presentationMode . wrappedValue . dismiss ( )
} , label : {
Text ( " Cancel " )
. frame ( width : 60 )
} ) . keyboardShortcut ( . cancelAction )
2020-12-05 14:09:34 +01:00
2020-12-04 02:15:37 +01:00
Button ( action : {
2020-12-05 15:18:10 +01:00
model . authenticateReaderAccount ( accountType )
2020-12-04 02:15:37 +01:00
} , label : {
2020-12-05 14:09:34 +01:00
Text ( " Sign In " )
2020-12-04 02:15:37 +01:00
. frame ( width : 60 )
} )
. keyboardShortcut ( . defaultAction )
2020-12-05 14:09:34 +01:00
. disabled ( createDisabled ( ) )
2020-12-04 02:15:37 +01:00
}
}
}
}
. padding ( )
2020-12-05 14:09:34 +01:00
. frame ( width : 400 , height : height ( ) )
2020-12-04 02:15:37 +01:00
. textFieldStyle ( RoundedBorderTextFieldStyle ( ) )
2020-12-05 14:09:34 +01:00
. alert ( isPresented : $ model . showError , content : {
Alert ( title : Text ( " Sign In Error " ) , message : Text ( model . accountUpdateError . description ) , dismissButton : . cancel ( ) )
} )
2020-12-05 15:18:10 +01:00
. onReceive ( model . $ canDismiss , perform : { value in
if value = = true {
presentationMode . wrappedValue . dismiss ( )
}
} )
2020-12-05 14:09:34 +01:00
}
2020-12-05 15:58:11 +01:00
#endif
2020-12-04 02:15:37 +01:00
2020-12-05 14:09:34 +01:00
func createDisabled ( ) -> Bool {
if accountType = = . freshRSS {
return model . username . isEmpty || model . password . isEmpty || ! model . apiUrl . mayBeURL
}
return model . username . isEmpty || model . password . isEmpty
}
func height ( ) -> CGFloat {
if accountType = = . freshRSS {
return 260
}
return 230
}
2020-12-07 12:22:35 +01:00
var formHeader : some View {
HStack {
Spacer ( )
VStack ( alignment : . center ) {
accountType . image ( )
. resizable ( )
. frame ( width : 50 , height : 50 )
}
Spacer ( )
} . padding ( . vertical )
}
var formFooter : some View {
HStack {
Spacer ( )
VStack ( spacing : 8 ) {
Text ( " Sign in to your \( accountType . localizedAccountName ( ) ) account and sync your subscriptions across your devices. Your username and password and password will be encrypted and stored in Keychain. " ) . foregroundColor ( . secondary )
Text ( " Don't have a \( accountType . localizedAccountName ( ) ) instance? " ) . foregroundColor ( . secondary )
Button ( action : {
model . presentSignUpOption ( accountType )
} , label : {
Text ( " Sign Up Here " ) . foregroundColor ( . blue ) . multilineTextAlignment ( . center )
} )
ProgressView ( ) . hidden ( ! model . isAuthenticating )
}
. multilineTextAlignment ( . center )
. font ( . caption2 )
Spacer ( )
} . padding ( . vertical )
}
2020-12-04 02:15:37 +01:00
}
struct AddReaderAPIAccountView_Previews : PreviewProvider {
2020-12-05 14:09:34 +01:00
static var previews : some View {
2020-12-04 02:15:37 +01:00
AddReaderAPIAccountView ( accountType : . freshRSS )
2020-12-07 12:22:35 +01:00
// A d d R e a d e r A P I A c c o u n t V i e w ( a c c o u n t T y p e : . i n o r e a d e r )
2020-12-05 14:09:34 +01:00
}
2020-12-04 02:15:37 +01:00
}