2021-02-05 10:53:00 +01:00
//
// M a s t o d o n R e g i s t e r V i e w M o d e l . s w i f t
// M a s t o d o n
//
// C r e a t e d b y M a i n a s u K C i r n o o n 2 0 2 1 - 2 - 5 .
//
import Combine
2021-02-20 06:55:06 +01:00
import Foundation
2021-02-05 10:53:00 +01:00
import MastodonSDK
2021-02-20 06:55:06 +01:00
import UIKit
2021-02-05 10:53:00 +01:00
final class MastodonRegisterViewModel {
2021-02-20 10:13:16 +01:00
var disposeBag = Set < AnyCancellable > ( )
2021-02-05 10:53:00 +01:00
// i n p u t
let domain : String
2021-02-20 12:54:08 +01:00
let authenticateInfo : AuthenticationViewModel . AuthenticateInfo
2021-02-22 09:20:44 +01:00
let instance : Mastodon . Entity . Instance
2021-02-05 10:53:00 +01:00
let applicationToken : Mastodon . Entity . Token
2021-02-20 12:54:08 +01:00
2021-02-22 10:48:44 +01:00
let username = CurrentValueSubject < String , Never > ( " " )
let displayName = CurrentValueSubject < String , Never > ( " " )
let email = CurrentValueSubject < String , Never > ( " " )
let password = CurrentValueSubject < String , Never > ( " " )
2021-02-26 11:27:47 +01:00
let reason = CurrentValueSubject < String , Never > ( " " )
2021-02-22 10:48:44 +01:00
2021-02-05 10:53:00 +01:00
// o u t p u t
2021-02-26 11:27:47 +01:00
let approvalRequired : Bool
2021-02-05 10:53:00 +01:00
let applicationAuthorization : Mastodon . API . OAuth . Authorization
2021-02-22 10:48:44 +01:00
let usernameValidateState = CurrentValueSubject < ValidateState , Never > ( . empty )
let displayNameValidateState = CurrentValueSubject < ValidateState , Never > ( . empty )
let emailValidateState = CurrentValueSubject < ValidateState , Never > ( . empty )
let passwordValidateState = CurrentValueSubject < ValidateState , Never > ( . empty )
2021-02-26 05:52:37 +01:00
let inviteValidateState = CurrentValueSubject < ValidateState , Never > ( . empty )
2021-02-22 10:48:44 +01:00
2021-03-01 10:45:02 +01:00
let isUsernameTaken = CurrentValueSubject < Bool , Never > ( false )
2021-02-26 11:27:47 +01:00
let isRegistering = CurrentValueSubject < Bool , Never > ( false )
2021-02-22 10:48:44 +01:00
let isAllValid = CurrentValueSubject < Bool , Never > ( false )
2021-02-05 10:53:00 +01:00
let error = CurrentValueSubject < Error ? , Never > ( nil )
2021-02-20 12:54:08 +01:00
init (
domain : String ,
authenticateInfo : AuthenticationViewModel . AuthenticateInfo ,
2021-02-22 09:20:44 +01:00
instance : Mastodon . Entity . Instance ,
2021-02-20 12:54:08 +01:00
applicationToken : Mastodon . Entity . Token
) {
2021-02-05 10:53:00 +01:00
self . domain = domain
2021-02-20 12:54:08 +01:00
self . authenticateInfo = authenticateInfo
2021-02-22 09:20:44 +01:00
self . instance = instance
2021-02-05 10:53:00 +01:00
self . applicationToken = applicationToken
2021-02-26 11:27:47 +01:00
self . approvalRequired = instance . approvalRequired ? ? false
2021-02-05 10:53:00 +01:00
self . applicationAuthorization = Mastodon . API . OAuth . Authorization ( accessToken : applicationToken . accessToken )
2021-02-20 10:13:16 +01:00
username
. map { username in
2021-02-22 10:48:44 +01:00
guard ! username . isEmpty else { return . empty }
var isValid = true
// r e g e x o p t - o u t w a y t o c h e c k v a l i d a t i o n
// a l l o w e d :
// a - z ( i s A S C I I & & i s L e t t e r )
// A - Z ( i s A S C I I & & i s L e t t e r )
// 0 - 9 ( i s A S C I I & & i s N u m b e r )
// _ ( " _ " )
for char in username {
2021-02-26 05:52:37 +01:00
guard char . isASCII , char . isLetter || char . isNumber || char = = " _ " else {
2021-02-22 10:48:44 +01:00
isValid = false
break
}
2021-02-20 10:13:16 +01:00
}
2021-02-22 10:48:44 +01:00
return isValid ? . valid : . invalid
2021-02-20 10:13:16 +01:00
}
2021-02-22 10:48:44 +01:00
. assign ( to : \ . value , on : usernameValidateState )
2021-02-20 10:13:16 +01:00
. store ( in : & disposeBag )
2021-02-22 10:48:44 +01:00
displayName
2021-02-20 11:24:23 +01:00
. map { displayname in
2021-02-22 10:48:44 +01:00
guard ! displayname . isEmpty else { return . empty }
return . valid
2021-02-20 11:24:23 +01:00
}
2021-02-22 10:48:44 +01:00
. assign ( to : \ . value , on : displayNameValidateState )
2021-02-20 11:24:23 +01:00
. store ( in : & disposeBag )
email
2021-02-22 10:48:44 +01:00
. map { email in
guard ! email . isEmpty else { return . empty }
return MastodonRegisterViewModel . isValidEmail ( email ) ? . valid : . invalid
2021-02-20 11:24:23 +01:00
}
2021-02-22 10:48:44 +01:00
. assign ( to : \ . value , on : emailValidateState )
2021-02-20 11:24:23 +01:00
. store ( in : & disposeBag )
password
2021-02-22 10:48:44 +01:00
. map { password in
guard ! password . isEmpty else { return . empty }
return password . count >= 8 ? . valid : . invalid
2021-02-20 11:24:23 +01:00
}
2021-02-22 10:48:44 +01:00
. assign ( to : \ . value , on : passwordValidateState )
2021-02-20 11:24:23 +01:00
. store ( in : & disposeBag )
2021-02-26 08:39:05 +01:00
if approvalRequired {
2021-02-26 11:27:47 +01:00
reason
2021-02-26 05:52:37 +01:00
. map { invite in
guard ! invite . isEmpty else { return . empty }
return . valid
}
. assign ( to : \ . value , on : inviteValidateState )
. store ( in : & disposeBag )
}
2021-02-26 08:39:05 +01:00
let publisherOne = Publishers . CombineLatest4 (
2021-02-22 10:48:44 +01:00
usernameValidateState . eraseToAnyPublisher ( ) ,
2021-02-26 08:39:05 +01:00
displayNameValidateState . eraseToAnyPublisher ( ) ,
2021-02-22 10:48:44 +01:00
emailValidateState . eraseToAnyPublisher ( ) ,
2021-02-26 08:39:05 +01:00
passwordValidateState . eraseToAnyPublisher ( )
) . map {
$0 . 0 = = . valid && $0 . 1 = = . valid && $0 . 2 = = . valid && $0 . 3 = = . valid
}
2021-02-26 05:52:37 +01:00
Publishers . CombineLatest (
publisherOne ,
2021-02-26 08:39:05 +01:00
approvalRequired ? inviteValidateState . map { $0 = = . valid } . eraseToAnyPublisher ( ) : Just ( true ) . eraseToAnyPublisher ( )
2021-02-22 10:48:44 +01:00
)
2021-02-26 08:39:05 +01:00
. map {
return $0 && $1
2021-02-26 05:52:37 +01:00
}
2021-02-22 10:48:44 +01:00
. assign ( to : \ . value , on : isAllValid )
. store ( in : & disposeBag )
}
}
extension MastodonRegisterViewModel {
enum ValidateState {
case empty
case invalid
case valid
2021-02-05 10:53:00 +01:00
}
2021-02-20 06:55:06 +01:00
}
extension MastodonRegisterViewModel {
2021-02-22 10:48:44 +01:00
static func isValidEmail ( _ email : String ) -> Bool {
2021-02-20 06:55:06 +01:00
let emailRegEx = " [A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+ \\ .[A-Za-z]{2,64} "
2021-02-20 11:24:23 +01:00
let emailPred = NSPredicate ( format : " SELF MATCHES %@ " , emailRegEx )
2021-02-20 06:55:06 +01:00
return emailPred . evaluate ( with : email )
}
func attributeStringForUsername ( ) -> NSAttributedString {
let resultAttributeString = NSMutableAttributedString ( )
let redImage = NSTextAttachment ( )
let font = UIFont . preferredFont ( forTextStyle : . caption1 )
let configuration = UIImage . SymbolConfiguration ( font : font )
redImage . image = UIImage ( systemName : " xmark.octagon.fill " , withConfiguration : configuration ) ? . withTintColor ( Asset . Colors . lightDangerRed . color )
let imageAttribute = NSAttributedString ( attachment : redImage )
let stringAttribute = NSAttributedString ( string : " This username is taken. " , attributes : [ NSAttributedString . Key . font : font , NSAttributedString . Key . foregroundColor : Asset . Colors . lightDangerRed . color ] )
resultAttributeString . append ( imageAttribute )
resultAttributeString . append ( stringAttribute )
return resultAttributeString
}
2021-02-22 10:48:44 +01:00
func attributeStringForPassword ( eightCharacters : Bool = false ) -> NSAttributedString {
2021-02-20 06:55:06 +01:00
let font = UIFont . preferredFont ( forTextStyle : . caption1 )
let color = UIColor . black
let falseColor = UIColor . clear
let attributeString = NSMutableAttributedString ( )
let start = NSAttributedString ( string : " Your password needs at least: \n " , attributes : [ NSAttributedString . Key . font : font , NSAttributedString . Key . foregroundColor : color ] )
attributeString . append ( start )
2021-02-20 11:24:23 +01:00
attributeString . append ( checkmarkImage ( color : eightCharacters ? color : falseColor ) )
2021-02-22 10:48:44 +01:00
let eightCharactersDescription = NSAttributedString ( string : " Eight characters \n " , attributes : [ NSAttributedString . Key . font : font , NSAttributedString . Key . foregroundColor : color ] )
2021-02-20 06:55:06 +01:00
attributeString . append ( eightCharactersDescription )
return attributeString
}
2021-02-20 11:24:23 +01:00
func checkmarkImage ( color : UIColor ) -> NSAttributedString {
let checkmarkImage = NSTextAttachment ( )
2021-02-20 06:55:06 +01:00
let font = UIFont . preferredFont ( forTextStyle : . caption1 )
let configuration = UIImage . SymbolConfiguration ( font : font )
2021-02-20 11:24:23 +01:00
checkmarkImage . image = UIImage ( systemName : " checkmark.circle.fill " , withConfiguration : configuration ) ? . withTintColor ( color )
return NSAttributedString ( attachment : checkmarkImage )
2021-02-20 06:55:06 +01:00
}
2021-02-05 10:53:00 +01:00
}