Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email Login (WIP) #49

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sample/Example/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct ContentView: View {
},
alignment: .top
)
.onReceive(Web3Modal.instance.socketConnectionStatusPublisher, perform: { status in
.onReceive(Web3Modal.instance.socketConnectionStatusPublisher.receive(on: DispatchQueue.main), perform: { status in
socketConnected = status == .connected
print("🧦 \(status)")
})
Expand Down
5 changes: 1 addition & 4 deletions Sources/Web3Modal/Components/AccountButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,7 @@ public struct AccountButtonPreviewView: View {
let store = Store()
store.balance = balance
store.session = .stub
store.account = W3MAccount(
address: Session.stub.accounts.first!.address,
chain: Session.stub.accounts.first!.blockchain
)
store.account = .stub

return store
}
Expand Down
27 changes: 27 additions & 0 deletions Sources/Web3Modal/Core/MagicMessage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation

struct MagicMessage: Decodable {
enum MessageType: String, Decodable {
case syncThemeSuccess = "@w3m-frame/SYNC_THEME_SUCCESS"
case syncDataSuccess = "@w3m-frame/SYNC_DAPP_DATA_SUCCESS"
case connectEmailSuccess = "@w3m-frame/CONNECT_EMAIL_SUCCESS"
case connectEmailError = "@w3m-frame/CONNECT_EMAIL_ERROR"
case isConnectSuccess = "@w3m-frame/IS_CONNECTED_SUCCESS"
case isConnectError = "@w3m-frame/IS_CONNECTED_ERROR"
case connectOtpSuccess = "@w3m-frame/CONNECT_OTP_SUCCESS"
case connectOtpError = "@w3m-frame/CONNECT_OTP_ERROR"
case getUserSuccess = "@w3m-frame/GET_USER_SUCCESS"
case getUserError = "@w3m-frame/GET_USER_ERROR"
case sessionUpdate = "@w3m-frame/SESSION_UPDATE"
case switchNetworkSuccess = "@w3m-frame/SWITCH_NETWORK_SUCCESS"
case switchNetworkError = "@w3m-frame/SWITCH_NETWORK_ERROR"
case rpcRequestSuccess = "@w3m-frame/RPC_REQUEST_SUCCESS"
case rpcRequestError = "@w3m-frame/RPC_REQUEST_ERROR"
}

var type: String
var payload: AnyCodable?
var rt: String?
var jwt: String?
var action: String?
}
133 changes: 133 additions & 0 deletions Sources/Web3Modal/Core/MagicMessageHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import Foundation
import WebKit

class MagicMessageHandler: NSObject, WKScriptMessageHandler {

private let router: Router
private let store: Store

init(router: Router, store: Store = .shared) {
self.router = router
self.store = store
}

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {


guard
let bodyString = message.body as? String,
bodyString.contains("@w3m-frame") || bodyString.contains("@w3m-app"),
let data = bodyString.data(using: .utf8)
else {
return
}

print(message.body)

do {
let response = try JSONDecoder().decode(MagicMessage.self, from: data)
handleMagicResponse(response)
} catch {
print("Failed decoding Magic response: ", error)
}
}


func handleMagicResponse(_ response: MagicMessage) {


switch MagicMessage.MessageType(rawValue: response.type) {
case .syncThemeSuccess:
break
case .syncDataSuccess:
break
case .connectEmailSuccess:

struct Payload: Codable {
let action: String?
}

let payload = try? response.payload?.get(Payload.self)

switch payload?.action {
case "VERIFY_OTP":
router.setRoute(Router.ConnectingSubpage.otpInput)
case "VERIFY_DEVICE":
router.setRoute(Router.ConnectingSubpage.verifyDevice)
default:
break
}
case .connectEmailError:
break
case .isConnectSuccess:

struct Payload: Codable {
let isConnected: Bool?
}

let payload = try? response.payload?.get(Payload.self)

if payload?.isConnected == true {
Web3Modal.magicService.getUser(chainId: store.selectedChain?.id)
}
case .isConnectError:

print("No magic connection")
case .connectOtpSuccess:
store.otpState = .success

Web3Modal.magicService.getUser(chainId: store.selectedChain?.id)
case .connectOtpError:
store.otpState = .error
case .getUserSuccess:

struct Payload: Codable {
let chainId: Int?
let address: String?
}

guard
let payload = try? response.payload?.get(Payload.self),
let address = payload.address,
let chainId = payload.chainId,
let blockChain = Blockchain("eip155:\(chainId)")
else {
return
}

store.account = .init(address: address, chain: blockChain)
store.connectedWith = .magic
router.setRoute(Router.AccountSubpage.profile)
case .getUserError:
print("getUserError")
case .sessionUpdate:
break
case .switchNetworkSuccess:

struct Payload: Codable {
let chainId: Int?
let address: String?
}

guard
let payload = try? response.payload?.get(Payload.self),
let chainId = payload.chainId,
let blockChain = Blockchain("eip155:\(chainId)")
else {
return
}

store.selectedChain = ChainPresets.ethChains.first(where: { $0.id == blockChain.absoluteString })
router.setRoute(Router.AccountSubpage.profile)
case .switchNetworkError:
break
case .rpcRequestSuccess:
break
case .rpcRequestError:
break
case .none:
break
}
}

}
193 changes: 193 additions & 0 deletions Sources/Web3Modal/Core/MagicRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import Foundation
import WalletConnectSign

protocol MagicMessageRequest {
var type: MagicRequest.MessageType { get }
var toString: String { get }
}

extension MagicMessageRequest {
var toString: String {
return #" { "type": "\#(type.rawValue)"} "#
}
}

enum MagicRequest {

enum MessageType: String {
case syncTheme = "@w3m-app/SYNC_THEME"
case syncData = "@w3m-app/SYNC_DAPP_DATA"
case connectEmail = "@w3m-app/CONNECT_EMAIL"
case connectDevice = "@w3m-app/CONNECT_DEVICE"
case isConnected = "@w3m-app/IS_CONNECTED"
case connectOtp = "@w3m-app/CONNECT_OTP"
case switchNetwork = "@w3m-app/SWITCH_NETWORK"
case rpcRequest = "@w3m-app/RPC_REQUEST"
case signOut = "@w3m-app/SIGN_OUT"
case getUser = "@w3m-app/GET_USER"
case getChainId = "@w3m-app/GET_CHAIN_ID"
case updateEmail = "@w3m-app/UPDATE_EMAIL"
}

struct IsConnected: MagicMessageRequest {
let type: MessageType

init() {
self.type = MessageType.isConnected
}
}

struct SwitchNetwork: MagicMessageRequest {
let chainId: String
let type: MessageType

init(chainId: String) {
self.chainId = chainId
self.type = MessageType.switchNetwork
}

var toString: String {
"{type:'\(type.rawValue)',payload:{chainId:\(chainId)}}"
}
}

struct ConnectEmail: MagicMessageRequest {
let type: MessageType
let email: String

init(email: String) {
self.email = email
self.type = MessageType.connectEmail
}

var toString: String {
"{type:'\(type.rawValue)',payload:{email:'\(email)'}}"
}
}

struct ConnectDevice: MagicMessageRequest {
let type: MessageType

init() {
self.type = MessageType.connectDevice
}
}

struct ConnectOtp: MagicMessageRequest {
let otp: String
let type: MessageType

init(otp: String) {
self.otp = otp
self.type = MessageType.connectOtp
}

var toString: String {
"{type:'\(type.rawValue)',payload:{otp:'\(otp)'}}"
}
}

struct GetUser: MagicMessageRequest {
let type: MessageType
let chainId: String?

init(chainId: String?) {
self.chainId = chainId
self.type = MessageType.getUser
}

var toString: String {
if let chainId {
return "{type:'\(type.rawValue)',payload:{chainId:\(chainId)}}"
} else {
return "{type:'\(type.rawValue)'}"
}
}
}

struct SignOut: MagicMessageRequest {
let type: MessageType

init() {
self.type = MessageType.signOut
}
}

struct GetChainId: MagicMessageRequest {
let type: MessageType

init() {
self.type = MessageType.getChainId
}
}

struct RpcRequest: MagicMessageRequest {
let method: String
let params: AnyCodable
let type: MessageType

init(method: String, params: AnyCodable) {
self.method = method
self.params = params
self.type = MessageType.rpcRequest
}

// TODO: Properly convert params to string
var toString: String {
let m = "method:'\(method)'"
let p = "" // params.map { "'\($0)'" }.joined(separator: ",")
return "{type:'\(type.rawValue)',payload:{\(m),params:[\(p)]}}"
}
}

// readonly APP_AWAIT_UPDATE_EMAIL: "@w3m-app/AWAIT_UPDATE_EMAIL";
struct UpdateEmail: MagicMessageRequest {
let type: MessageType

init() {
self.type = MessageType.updateEmail
}
}

struct SyncTheme: MagicMessageRequest {
var mode: String
let type: MessageType

init(mode: String = "light") {
self.mode = mode
self.type = MessageType.syncTheme
}

var toString: String {
let tm = "themeMode:'\(mode)'"
return "{type:'\(type.rawValue)',payload:{\(tm)}}"
}
}

struct SyncAppData: MagicMessageRequest {
let metadata: AppMetadata
let sdkVersion: String
let projectId: String
let type: MessageType

init(metadata: AppMetadata, sdkVersion: String, projectId: String) {
self.metadata = metadata
self.sdkVersion = sdkVersion
self.projectId = projectId
self.type = MessageType.syncData
}

var toString: String {
let v = "verified: true"
let p1 = "projectId:'\(projectId)'"
let p2 = "sdkVersion:'\(sdkVersion)'"
let m1 = "name:'\(metadata.name)'"
let m2 = "description:'\(metadata.description)'"
let m3 = "url:'\(metadata.url)'"
let m4 = "icons:[\"\(metadata.icons.first ?? "")\"]"
let p3 = "metadata:{\(m1),\(m2),\(m3),\(m4)}"
let p = "payload:{\(v),\(p1),\(p2),\(p3)}"
return "{type:'\(type.rawValue)',\(p)}"
}
}
}
Loading
Loading