commit
4f7e59e157
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"idiom" : "universal", |
||||
"filename" : "ticket_bundle_checked.pdf", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"version" : 1, |
||||
"author" : "xcode" |
||||
} |
||||
} |
Binary file not shown.
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"idiom" : "universal", |
||||
"filename" : "ticket_bundle_unchecked.pdf", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"version" : 1, |
||||
"author" : "xcode" |
||||
} |
||||
} |
Binary file not shown.
@ -0,0 +1,103 @@ |
||||
// |
||||
// Created by James Sangalli on 15/2/18. |
||||
// Sends the sales orders to and from the market queue server |
||||
// |
||||
|
||||
import Foundation |
||||
import Alamofire |
||||
import SwiftyJSON |
||||
import BigInt |
||||
|
||||
//"orders": [ |
||||
// { |
||||
// "message": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACOG8m/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWojJJwB77oK92ehmsr0RR4CkfyJhxoTjAAIAAwAE)", |
||||
// "expiry": "1518913831", |
||||
// "start": "32800312", |
||||
// "count": "3", |
||||
// "price": "10000000000000000", |
||||
// "signature": "jrzcgpsnV7IPGE3nZQeHQk5vyZdy5c8rHk0R/iG7wpiK9NT730I//DN5Dg5fHs+s4ZFgOGQnk7cXLQROBs9NvgE=" |
||||
// } |
||||
//] |
||||
|
||||
public class MarketQueueHandler { |
||||
|
||||
public let baseURL = "https://482kdh4npg.execute-api.ap-southeast-1.amazonaws.com/dev/" |
||||
public let contractAddress = "bC9a1026A4BC6F0BA8Bbe486d1D09dA5732B39e4".lowercased() |
||||
|
||||
public func getOrders(callback: @escaping (_ result : Any) -> Void) { |
||||
Alamofire.request(baseURL + "contract/" + contractAddress, method: .get).responseJSON { |
||||
response in |
||||
var orders = [SignedOrder]() |
||||
if let json = response.result.value { |
||||
let parsedJSON = try! JSON(data: response.data!) |
||||
for i in 0...parsedJSON.count - 1 { |
||||
let orderObj: JSON = parsedJSON["orders"][i] |
||||
if(orderObj == nil) |
||||
{ |
||||
callback("no orders") |
||||
return |
||||
} |
||||
orders.append(self.parseOrder(orderObj)) |
||||
} |
||||
callback(orders) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func parseOrder(_ orderObj: JSON) -> SignedOrder { |
||||
let orderString = orderObj["message"].string! |
||||
let message = MarketQueueHandler.bytesToHexa(Array(Data(base64Encoded: orderString)!)) |
||||
let price = message.substring(to: 64) |
||||
let expiry = message.substring(with: Range(uncheckedBounds: (64, 128))) |
||||
let contractAddress = "0x" + message.substring(with: Range(uncheckedBounds: (128, 168))) |
||||
let indices = message.substring(from: 168) |
||||
let order = Order( |
||||
price: BigUInt(price, radix: 16)!, |
||||
indices: indices.hexa2Bytes.map({ UInt16($0) }), |
||||
expiry: BigUInt(expiry, radix: 16)!, |
||||
contractAddress: contractAddress, |
||||
start: BigUInt(orderObj["start"].string!)!, |
||||
count: orderObj["count"].intValue |
||||
) |
||||
let signedOrder = SignedOrder( |
||||
order: order, |
||||
message: message.hexa2Bytes, |
||||
signature: "0x" + MarketQueueHandler.bytesToHexa(Array(Data(base64Encoded: orderObj["signature"].string!)!)) |
||||
) |
||||
return signedOrder |
||||
} |
||||
|
||||
//only have to give first order to server then pad the signatures |
||||
public func putOrderToServer(signedOrders: [SignedOrder], |
||||
publicKey: String, |
||||
callback: @escaping (_ result: Any) -> Void) { |
||||
//TODO get encoding for count and start |
||||
let query: String = baseURL + "public-key/" + publicKey + "?start=" + |
||||
signedOrders[0].order.start.description + ";count=" + signedOrders.count.description |
||||
var messageBytes: [UInt8] = signedOrders[0].message |
||||
print(signedOrders[0].signature.count) |
||||
|
||||
for i in 0...signedOrders.count - 1 { |
||||
for j in 0...64 { |
||||
messageBytes.append(signedOrders[i].signature.hexa2Bytes[j]) |
||||
} |
||||
} |
||||
let headers: HTTPHeaders = ["Content-Type": "application/vnd.awallet-signed-orders-v0"] |
||||
|
||||
print(query) |
||||
|
||||
Alamofire.upload(Data(bytes: messageBytes), to: query, method: .put, headers: headers).response { response in |
||||
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) { |
||||
print("Data: \(utf8Text)") // original server data as UTF8 string |
||||
callback(data) |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static func bytesToHexa(_ bytes: [UInt8]) -> String { |
||||
return bytes.map { |
||||
String(format: "%02X", $0) |
||||
}.joined() |
||||
} |
||||
|
||||
} |
@ -1,77 +0,0 @@ |
||||
// |
||||
// Created by James Sangalli on 15/2/18. |
||||
// |
||||
|
||||
import Foundation |
||||
import Alamofire |
||||
import SwiftyJSON |
||||
|
||||
//"orders": [ |
||||
// { |
||||
// "message": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACOG8m/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWojJJwB77oK92ehmsr0RR4CkfyJhxoTjAAIAAwAE)", |
||||
// "expiry": "1518913831", |
||||
// "start": "32800312", |
||||
// "count": "3", |
||||
// "price": "10000000000000000", |
||||
// "signature": "jrzcgpsnV7IPGE3nZQeHQk5vyZdy5c8rHk0R/iG7wpiK9NT730I//DN5Dg5fHs+s4ZFgOGQnk7cXLQROBs9NvgE=" |
||||
// } |
||||
//] |
||||
|
||||
public class OrdersRequest { |
||||
|
||||
public let baseURL = "https://482kdh4npg.execute-api.ap-southeast-1.amazonaws.com/dev/" |
||||
public let contractAddress = "0x007bee82bdd9e866b2bd114780a47f2261c684e3" //this is wrong as it is the deployer address, will be corrected later |
||||
|
||||
public func getOrders(callback: @escaping (_ result: Any) -> Void) { |
||||
Alamofire.request(baseURL + "/contract/" + contractAddress, method: .get).responseJSON { response in |
||||
callback(response) |
||||
} |
||||
} |
||||
|
||||
//only have to give first order to server then pad the signatures |
||||
public func putOrderToServer(signedOrders: [SignedOrder], |
||||
publicKey: String, |
||||
callback: @escaping (_ result: Any) -> Void) { |
||||
//TODO get encoding for count and start |
||||
let query: String = baseURL + "public-key/" + publicKey + "?start=" + |
||||
signedOrders[0].order.start.description + ";count=" + signedOrders[0].order.count.description |
||||
var data: [UInt8] = signedOrders[0].message |
||||
|
||||
for i in 0...signedOrders.count - 1 { |
||||
for j in 0...64 { |
||||
data.append(signedOrders[i].signature.hexa2Bytes[j]) |
||||
} |
||||
} |
||||
|
||||
let hexData: String = OrdersRequest.bytesToHexa(data) |
||||
let parameters: Parameters = ["data": hexData] |
||||
let headers: HTTPHeaders = ["Content-Type": "application/vnd.awallet-signed-orders-v0"] |
||||
|
||||
Alamofire.request(query, method: .put, |
||||
parameters: parameters, |
||||
encoding: JSONEncoding.default, |
||||
headers: headers).responseJSON { response in |
||||
print("Request: \(String(describing: response.request))") // original url request |
||||
print("Response: \(String(describing: response.response))") // http url response |
||||
print("Result: \(response.result)") // response serialization result |
||||
|
||||
if let json = response.result.value { |
||||
//print("JSON: \(json)") // serialized json response |
||||
let parsedJSON = JSON(parseJSON: json as! String) |
||||
callback(parsedJSON["orders"]["accepted"]) |
||||
} |
||||
|
||||
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) { |
||||
print("Data: \(utf8Text)") // original server data as UTF8 string |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
public static func bytesToHexa(_ bytes: [UInt8]) -> String { |
||||
return bytes.map { |
||||
String(format: "%02X", $0) |
||||
}.joined() |
||||
} |
||||
|
||||
} |
@ -0,0 +1,160 @@ |
||||
// |
||||
// Created by James Sangalli on 24/3/18. |
||||
// |
||||
|
||||
/** |
||||
* Universal link format |
||||
* |
||||
* Android requires the link to be in the form: |
||||
* |
||||
* https://www.awallet.io/[base64] |
||||
* |
||||
* The format forbids using a prefix other than 'www'. |
||||
* There needs to be text in the specific link too, in this case 'import'. |
||||
$ echo -n https://www.awallet.io/; \ |
||||
echo -n 000f42405AB5B400007bee82bdd9e866b2bd114780a47f2261c684e30102030405060708092F982B84C635967A9B6306ED5789A7C1919164171E37DCCDF4B59BE54754410530818B896B7D240F56C59EBDF209062EE54DA7A3590905739674DCFDCECF3E9B1b | xxd -r -p | base64;\ |
||||
https://app.awallet.io/AA9CQFq1tAAAe+6CvdnoZrK9EUeApH8iYcaE4wECAwQFBgcICS+YK4TGNZZ6m2MG7VeJp8GRkWQXHjfczfS1m+VHVEEFMIGLiWt9JA9WxZ698gkGLuVNp6NZCQVzlnTc/c7PPpsb |
||||
* uint32: price in Szabo 000f4240 |
||||
* uint32: expiry in Unix Time 5AB5B400 |
||||
* bytes20: contract address 007bee82bdd9e866b2bd114780a47f2261c684e3 |
||||
* Uint16[]: ticket indices 010203040506070809 |
||||
* bytes32: 2F982B84C635967A9B6306ED5789A7C1919164171E37DCCDF4B59BE547544105 |
||||
* bytes32: 30818B896B7D240F56C59EBDF209062EE54DA7A3590905739674DCFDCECF3E9B |
||||
* byte: 1b |
||||
* 1521857536, [0,1,2,3,4,5,6,7,8,9], 27, "0x2F982B84C635967A9B6306ED5789A7C1919164171E37DCCDF4B59BE547544105", "0x30818B896B7D240F56C59EBDF209062EE54DA7A3590905739674DCFDCECF3E9B" -> 0xd2bef24c7e90192426b54bf437a5eac4e220dde7 |
||||
*/ |
||||
|
||||
import Foundation |
||||
import BigInt |
||||
|
||||
|
||||
public class UniversalLinkHandler { |
||||
|
||||
public let urlPrefix = "https://app.awallet.io/" |
||||
public static let paymentServer = "http://stormbird.duckdns.org:8080/api/claimToken" |
||||
|
||||
//TODO fix encoding of this link later as it is low priority |
||||
func createUniversalLink(signedOrder: SignedOrder) -> String |
||||
{ |
||||
let message = MarketQueueHandler.bytesToHexa(signedOrder.message) |
||||
let signature = signedOrder.signature.substring(from: 2) |
||||
let link = (message + signature).hexa2Bytes |
||||
let binaryData = Data(bytes: link) |
||||
let base64String = binaryData.base64EncodedString() |
||||
|
||||
return urlPrefix + base64String |
||||
} |
||||
|
||||
func parseURL(url: String) -> SignedOrder { |
||||
let linkInfo = url.substring(from: urlPrefix.count) |
||||
let linkBytes = Data(base64Encoded: linkInfo)?.array |
||||
let price = getPriceFromLinkBytes(linkBytes: linkBytes) |
||||
let expiry = getExpiryFromLinkBytes(linkBytes: linkBytes) |
||||
let contractAddress = getContractAddressFromLinkBytes(linkBytes: linkBytes) |
||||
let ticketIndices = getTicketIndicesFromLinkBytes(linkBytes: linkBytes) |
||||
let (v, r, s) = getVRSFromLinkBytes(linkBytes: linkBytes) |
||||
let message = getMessageFromLinkBytes(linkBytes: linkBytes!) |
||||
|
||||
let order = Order( |
||||
price: price, |
||||
indices: ticketIndices, |
||||
expiry: expiry, |
||||
contractAddress: contractAddress, |
||||
start: BigUInt("0")!, |
||||
count: ticketIndices.count |
||||
) |
||||
|
||||
return SignedOrder(order: order, message: message, signature: "0x" + r + s + v) |
||||
} |
||||
|
||||
func getPriceFromLinkBytes(linkBytes: [UInt8]?) -> BigUInt { |
||||
var priceBytes = [UInt8]() |
||||
for i in 0...3 { |
||||
//price in szabo |
||||
priceBytes.append(linkBytes![i]) |
||||
} |
||||
return (BigUInt(MarketQueueHandler.bytesToHexa(priceBytes), |
||||
radix: 16)?.multiplied(by: BigUInt("1000000000000")!))! |
||||
} |
||||
|
||||
func getExpiryFromLinkBytes(linkBytes: [UInt8]?) -> BigUInt { |
||||
var expiryBytes = [UInt8]() |
||||
for i in 4...7 { |
||||
expiryBytes.append(linkBytes![i]) |
||||
} |
||||
let expiry = MarketQueueHandler.bytesToHexa(expiryBytes) |
||||
return BigUInt(expiry, radix: 16)! |
||||
} |
||||
|
||||
func getContractAddressFromLinkBytes(linkBytes: [UInt8]?) -> String { |
||||
var contractAddrBytes = [UInt8]() |
||||
for i in 8...27 { |
||||
contractAddrBytes.append(linkBytes![i]) |
||||
} |
||||
return MarketQueueHandler.bytesToHexa(contractAddrBytes) |
||||
} |
||||
|
||||
func getTicketIndicesFromLinkBytes(linkBytes: [UInt8]?) -> [UInt16] { |
||||
|
||||
let ticketLength = ((linkBytes?.count)! - (65 + 20 + 8)) - 1 |
||||
var ticketIndices = [UInt16]() |
||||
var state: Int = 1 |
||||
var currentIndex: UInt16 = 0 |
||||
let ticketStart = 28 |
||||
|
||||
for i in stride(from: ticketStart, through: ticketStart + ticketLength, by: 1) { |
||||
let byte: UInt8 = linkBytes![i] |
||||
switch(state) { |
||||
case 1: |
||||
//8th bit is equal to 128, if not set then it is only one ticket and will change the state |
||||
if(byte & (128) == 128) { //should be done with masks |
||||
currentIndex = UInt16((byte & 127)) << 8 |
||||
state = 2 |
||||
} |
||||
else { |
||||
ticketIndices.append(UInt16(byte)) |
||||
} |
||||
break; |
||||
case 2: |
||||
currentIndex += UInt16(byte) |
||||
ticketIndices.append(currentIndex) |
||||
state = 1 |
||||
break; |
||||
default: |
||||
break |
||||
} |
||||
} |
||||
|
||||
return ticketIndices |
||||
} |
||||
|
||||
func getVRSFromLinkBytes(linkBytes: [UInt8]?) -> (String, String, String) { |
||||
var signatureStart = (linkBytes?.count)! - 65 |
||||
var rBytes = [UInt8]() |
||||
for i in signatureStart...signatureStart + 31 |
||||
{ |
||||
rBytes.append(linkBytes![i]) |
||||
} |
||||
let r = MarketQueueHandler.bytesToHexa(rBytes) |
||||
signatureStart += 32 |
||||
var sBytes = [UInt8]() |
||||
for i in signatureStart...signatureStart + 31 { |
||||
sBytes.append(linkBytes![i]) |
||||
} |
||||
|
||||
let s = MarketQueueHandler.bytesToHexa(sBytes) |
||||
let v = String(format:"%2X", linkBytes![(linkBytes?.count)! - 1]).trimmed |
||||
|
||||
return (v, r, s) |
||||
} |
||||
|
||||
func getMessageFromLinkBytes(linkBytes: [UInt8]?) -> [UInt8] { |
||||
let ticketLength = (linkBytes?.count)! - (65 + 20 + 8) - 1 |
||||
var message = [UInt8]() |
||||
for i in 0...ticketLength + 84 { |
||||
message.append(linkBytes![i]) |
||||
} |
||||
return message |
||||
} |
||||
|
||||
} |
@ -1,231 +0,0 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> |
||||
<device id="retina4_7" orientation="portrait"> |
||||
<adaptation id="fullscreen"/> |
||||
</device> |
||||
<dependencies> |
||||
<deployment identifier="iOS"/> |
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/> |
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/> |
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> |
||||
</dependencies> |
||||
<scenes> |
||||
<!--Redeem Tickets--> |
||||
<scene sceneID="FVp-jv-uXu"> |
||||
<objects> |
||||
<viewController storyboardIdentifier="RedeemTicketsViewController" id="dt4-4a-Rec" userLabel="Redeem Tickets" customClass="RedeemTicketsViewController" customModule="Trust" customModuleProvider="target" sceneMemberID="viewController"> |
||||
<view key="view" contentMode="scaleToFill" id="Lhy-Sv-9I6"> |
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> |
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> |
||||
<subviews> |
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="V6W-o7-JQ1"> |
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/> |
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
<prototypes> |
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="RedeemTableViewCell" rowHeight="30" id="TFt-LR-2x5"> |
||||
<rect key="frame" x="0.0" y="28" width="375" height="30"/> |
||||
<autoresizingMask key="autoresizingMask"/> |
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="TFt-LR-2x5" id="kE7-Tf-gCn"> |
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="30"/> |
||||
<autoresizingMask key="autoresizingMask"/> |
||||
<subviews> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="SELECT TICKETS TO REDEEM" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0F1-Dz-cM8"> |
||||
<rect key="frame" x="15" y="7" width="207" height="21"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/> |
||||
<nil key="textColor"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
</subviews> |
||||
</tableViewCellContentView> |
||||
</tableViewCell> |
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="RedeemTicketCell" rowHeight="90" id="x30-SZ-UO2" customClass="RedeemTicketTableViewCell" customModule="Trust" customModuleProvider="target"> |
||||
<rect key="frame" x="0.0" y="58" width="375" height="90"/> |
||||
<autoresizingMask key="autoresizingMask"/> |
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="x30-SZ-UO2" id="0T6-c0-w1v"> |
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="90"/> |
||||
<autoresizingMask key="autoresizingMask"/> |
||||
<subviews> |
||||
<button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gtm-9O-Und" customClass="RadioButton" customModule="Trust" customModuleProvider="target"> |
||||
<rect key="frame" x="8" y="36" width="18" height="18"/> |
||||
<constraints> |
||||
<constraint firstAttribute="height" constant="18" id="ZRs-66-jgP"/> |
||||
<constraint firstAttribute="width" constant="18" id="da0-OQ-uoy"/> |
||||
</constraints> |
||||
<userDefinedRuntimeAttributes> |
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isOn" value="NO"/> |
||||
<userDefinedRuntimeAttribute type="color" keyPath="buttonColor"> |
||||
<color key="value" red="0.12941176469999999" green="0.73725490199999999" blue="0.29411764709999999" alpha="1" colorSpace="calibratedRGB"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="space"> |
||||
<real key="value" value="5"/> |
||||
</userDefinedRuntimeAttribute> |
||||
</userDefinedRuntimeAttributes> |
||||
</button> |
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Bkz-zm-vkj" customClass="TicketView" customModule="Trust" customModuleProvider="target"> |
||||
<rect key="frame" x="34" y="4" width="333" height="82"/> |
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
</view> |
||||
</subviews> |
||||
<constraints> |
||||
<constraint firstItem="gtm-9O-Und" firstAttribute="centerY" secondItem="0T6-c0-w1v" secondAttribute="centerY" id="3NW-8X-Vtp"/> |
||||
<constraint firstItem="gtm-9O-Und" firstAttribute="leading" secondItem="0T6-c0-w1v" secondAttribute="leading" constant="8" id="3fy-DO-idO"/> |
||||
<constraint firstAttribute="trailing" secondItem="Bkz-zm-vkj" secondAttribute="trailing" constant="8" id="Vah-ht-N4R"/> |
||||
<constraint firstItem="Bkz-zm-vkj" firstAttribute="top" secondItem="0T6-c0-w1v" secondAttribute="top" constant="4" id="XkM-QH-ctA"/> |
||||
<constraint firstAttribute="bottom" secondItem="Bkz-zm-vkj" secondAttribute="bottom" constant="4" id="aRd-ju-Z5k"/> |
||||
<constraint firstItem="Bkz-zm-vkj" firstAttribute="leading" secondItem="gtm-9O-Und" secondAttribute="trailing" constant="8" id="f1L-SU-5PM"/> |
||||
</constraints> |
||||
</tableViewCellContentView> |
||||
<connections> |
||||
<outlet property="radioButton" destination="gtm-9O-Und" id="tKD-f3-KBV"/> |
||||
<outlet property="ticketView" destination="Bkz-zm-vkj" id="boc-Zz-qPt"/> |
||||
</connections> |
||||
</tableViewCell> |
||||
</prototypes> |
||||
<connections> |
||||
<outlet property="dataSource" destination="dt4-4a-Rec" id="1zm-N8-TiD"/> |
||||
<outlet property="delegate" destination="dt4-4a-Rec" id="hlz-fa-mcS"/> |
||||
</connections> |
||||
</tableView> |
||||
</subviews> |
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
<constraints> |
||||
<constraint firstItem="V6W-o7-JQ1" firstAttribute="top" secondItem="TiC-xe-4Ea" secondAttribute="top" id="BQs-dt-bLR"/> |
||||
<constraint firstItem="TiC-xe-4Ea" firstAttribute="trailing" secondItem="V6W-o7-JQ1" secondAttribute="trailing" id="IYZ-d0-wXs"/> |
||||
<constraint firstItem="V6W-o7-JQ1" firstAttribute="leading" secondItem="TiC-xe-4Ea" secondAttribute="leading" id="Qq3-gt-Eje"/> |
||||
<constraint firstItem="TiC-xe-4Ea" firstAttribute="bottom" secondItem="V6W-o7-JQ1" secondAttribute="bottom" id="r8a-Xa-ozT"/> |
||||
</constraints> |
||||
<viewLayoutGuide key="safeArea" id="TiC-xe-4Ea"/> |
||||
</view> |
||||
<navigationItem key="navigationItem" title="Use Token" id="KPo-ua-2Ez"/> |
||||
<connections> |
||||
<outlet property="tableView" destination="V6W-o7-JQ1" id="PAH-CT-vni"/> |
||||
</connections> |
||||
</viewController> |
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="yVC-XP-XG2" userLabel="First Responder" sceneMemberID="firstResponder"/> |
||||
</objects> |
||||
<point key="canvasLocation" x="228" y="170.46476761619192"/> |
||||
</scene> |
||||
<!--Quantity Selection View Controller--> |
||||
<scene sceneID="IWC-WP-rP7"> |
||||
<objects> |
||||
<viewController storyboardIdentifier="QuantitySelectionViewController" id="rXu-Vx-puG" customClass="QuantitySelectionViewController" customModule="Trust" customModuleProvider="target" sceneMemberID="viewController"> |
||||
<view key="view" contentMode="scaleToFill" id="9Kh-8o-KAb"> |
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> |
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> |
||||
<subviews> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Select Quantity of Tickets" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zSr-ce-F0B"> |
||||
<rect key="frame" x="35" y="76" width="304" height="21"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="25"/> |
||||
<nil key="textColor"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oQ8-qj-Gnu" customClass="NumberStepper" customModule="Trust" customModuleProvider="target"> |
||||
<rect key="frame" x="63" y="149" width="227" height="47"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
<color key="tintColor" red="0.20000000000000001" green="0.70980392160000005" blue="0.8980392157" alpha="1" colorSpace="calibratedRGB"/> |
||||
<userDefinedRuntimeAttributes> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="value"> |
||||
<real key="value" value="0.0"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="minimumValue"> |
||||
<real key="value" value="0.0"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="maximumValue"> |
||||
<real key="value" value="1000"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="stepValue"> |
||||
<real key="value" value="1"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="color" keyPath="buttonsTextColor"> |
||||
<color key="value" red="0.20000000000000001" green="0.70980392160000005" blue="0.8980392157" alpha="1" colorSpace="calibratedRGB"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="color" keyPath="buttonsBackgroundColor"> |
||||
<color key="value" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="color" keyPath="labelTextColor"> |
||||
<color key="value" red="0.20000000000000001" green="0.70980392160000005" blue="0.8980392157" alpha="1" colorSpace="calibratedRGB"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="color" keyPath="labelBackgroundColor"> |
||||
<color key="value" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="labelCornerRadius"> |
||||
<real key="value" value="0.0"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth"> |
||||
<real key="value" value="1"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> |
||||
<real key="value" value="9"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="color" keyPath="borderColor"> |
||||
<color key="value" red="0.20000000000000001" green="0.70980392160000005" blue="0.8980392157" alpha="1" colorSpace="calibratedRGB"/> |
||||
</userDefinedRuntimeAttribute> |
||||
</userDefinedRuntimeAttributes> |
||||
</view> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="QUANTITY OF TICKETS" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="I8D-gw-m7M"> |
||||
<rect key="frame" x="95" y="119" width="163" height="30"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/> |
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jBH-Fe-D1q" customClass="TicketView" customModule="Trust" customModuleProvider="target"> |
||||
<rect key="frame" x="16" y="269" width="343" height="90"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
</view> |
||||
</subviews> |
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
<viewLayoutGuide key="safeArea" id="8by-Gd-al4"/> |
||||
</view> |
||||
<connections> |
||||
<outlet property="quantityStepper" destination="oQ8-qj-Gnu" id="5Tr-yT-e6o"/> |
||||
<outlet property="ticketView" destination="jBH-Fe-D1q" id="nIa-hz-9tm"/> |
||||
</connections> |
||||
</viewController> |
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="RA3-pW-I2b" userLabel="First Responder" sceneMemberID="firstResponder"/> |
||||
</objects> |
||||
<point key="canvasLocation" x="896.79999999999995" y="169.56521739130437"/> |
||||
</scene> |
||||
<!--Ticket Redemption View Controller--> |
||||
<scene sceneID="iaD-Yd-G0B"> |
||||
<objects> |
||||
<viewController storyboardIdentifier="TicketRedemptionViewController" id="Czc-tX-qsc" customClass="TicketRedemptionViewController" customModule="Trust" customModuleProvider="target" sceneMemberID="viewController"> |
||||
<view key="view" contentMode="scaleToFill" id="N7W-u3-8n7"> |
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> |
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> |
||||
<subviews> |
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xvi-Wz-ipm"> |
||||
<rect key="frame" x="67" y="103" width="240" height="233"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
</imageView> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Show QR Code to Redemption Booth" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mGS-KR-f9p"> |
||||
<rect key="frame" x="54" y="20" width="267" height="60"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="25"/> |
||||
<nil key="textColor"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gxE-62-C2c" customClass="TicketView" customModule="Trust" customModuleProvider="target"> |
||||
<rect key="frame" x="16" y="355" width="343" height="90"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
</view> |
||||
</subviews> |
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
<viewLayoutGuide key="safeArea" id="W9z-g1-VSi"/> |
||||
</view> |
||||
<connections> |
||||
<outlet property="imageView" destination="xvi-Wz-ipm" id="tfw-TC-adc"/> |
||||
<outlet property="ticketView" destination="gxE-62-C2c" id="D9X-zC-jGD"/> |
||||
</connections> |
||||
</viewController> |
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="sTV-lv-Ngl" userLabel="First Responder" sceneMemberID="firstResponder"/> |
||||
</objects> |
||||
<point key="canvasLocation" x="1578.4000000000001" y="169.56521739130437"/> |
||||
</scene> |
||||
</scenes> |
||||
</document> |
@ -0,0 +1,57 @@ |
||||
// Copyright © 2018 Stormbird PTE. LTD. |
||||
|
||||
import UIKit |
||||
|
||||
struct RedeemTicketTableViewCellViewModel { |
||||
private let ticketHolder: TicketHolder |
||||
|
||||
init( |
||||
ticketHolder: TicketHolder |
||||
) { |
||||
self.ticketHolder = ticketHolder |
||||
} |
||||
|
||||
var ticketCount: String { |
||||
return "x\(ticketHolder.tickets.count)" |
||||
} |
||||
|
||||
var title: String { |
||||
return ticketHolder.name |
||||
} |
||||
|
||||
var seatRange: String { |
||||
return ticketHolder.seatRange |
||||
} |
||||
|
||||
var zoneName: String { |
||||
return ticketHolder.zone |
||||
} |
||||
var venue: String { |
||||
return ticketHolder.venue |
||||
} |
||||
|
||||
var date: String { |
||||
//TODO Should format be localized? |
||||
return ticketHolder.date.format("dd MMM yyyy") |
||||
} |
||||
|
||||
var backgroundColor: UIColor { |
||||
return Colors.appWhite |
||||
} |
||||
|
||||
var status: String { |
||||
return "" |
||||
} |
||||
|
||||
var cellHeight: CGFloat { |
||||
return 113 |
||||
} |
||||
|
||||
var checkboxImage: UIImage { |
||||
if ticketHolder.status == .redeemed { |
||||
return R.image.ticket_bundle_checked()! |
||||
} else { |
||||
return R.image.ticket_bundle_unchecked()! |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,53 @@ |
||||
// Copyright © 2018 Stormbird PTE. LTD. |
||||
|
||||
import UIKit |
||||
|
||||
struct TicketRowViewModel { |
||||
var backgroundColor: UIColor { |
||||
return Colors.appWhite |
||||
} |
||||
|
||||
var contentsBackgroundColor: UIColor { |
||||
return Colors.appWhite |
||||
} |
||||
|
||||
var titleColor: UIColor { |
||||
return Colors.appText |
||||
} |
||||
|
||||
var countColor: UIColor { |
||||
return Colors.appHighlightGreen |
||||
} |
||||
|
||||
var subtitleColor: UIColor { |
||||
return UIColor(red: 112, green: 112, blue: 112) |
||||
} |
||||
|
||||
var iconsColor: UIColor { |
||||
return Colors.appBackground |
||||
} |
||||
|
||||
var ticketCountFont: UIFont { |
||||
return Fonts.bold(size: 21)! |
||||
} |
||||
|
||||
var titleFont: UIFont { |
||||
return Fonts.light(size: 21)! |
||||
} |
||||
|
||||
var venueFont: UIFont { |
||||
return Fonts.light(size: 18)! |
||||
} |
||||
|
||||
var stateBackgroundColor: UIColor { |
||||
return UIColor(red: 151, green: 151, blue: 151) |
||||
} |
||||
|
||||
var stateColor: UIColor { |
||||
return .white |
||||
} |
||||
|
||||
var subtitleFont: UIFont { |
||||
return Fonts.semibold(size: 15)! |
||||
} |
||||
} |
@ -0,0 +1,150 @@ |
||||
// Copyright © 2018 Stormbird PTE. LTD. |
||||
|
||||
import UIKit |
||||
|
||||
class TicketRowView: UIView { |
||||
let checkboxImageView = UIImageView(image: R.image.ticket_bundle_unchecked()) |
||||
let background = UIView() |
||||
let stateLabel = UILabel() |
||||
let ticketCountLabel = UILabel() |
||||
let titleLabel = UILabel() |
||||
let venueLabel = UILabel() |
||||
let dateLabel = UILabel() |
||||
let seatRangeLabel = UILabel() |
||||
let zoneNameLabel = UILabel() |
||||
let dateImageView = UIImageView() |
||||
let seatRangeImageView = UIImageView() |
||||
let zoneNameImageView = UIImageView() |
||||
let showCheckbox: Bool |
||||
|
||||
init(showCheckbox: Bool = false) { |
||||
self.showCheckbox = showCheckbox |
||||
|
||||
super.init(frame: .zero) |
||||
|
||||
checkboxImageView.translatesAutoresizingMaskIntoConstraints = false |
||||
if showCheckbox { |
||||
addSubview(checkboxImageView) |
||||
} |
||||
|
||||
background.translatesAutoresizingMaskIntoConstraints = false |
||||
addSubview(background) |
||||
|
||||
venueLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) |
||||
|
||||
//A spacer view to take up empty horizontal space so venueLabel can be right aligned while the rest is left aligned in topRowStack |
||||
let topRowStack = UIStackView(arrangedSubviews: [ |
||||
ticketCountLabel, |
||||
titleLabel, |
||||
.spacer(), |
||||
venueLabel]) |
||||
topRowStack.axis = .horizontal |
||||
topRowStack.spacing = 15 |
||||
topRowStack.distribution = .fill |
||||
topRowStack.alignment = .center |
||||
topRowStack.setContentHuggingPriority(UILayoutPriority.required, for: .horizontal) |
||||
|
||||
let bottomRowStack = UIStackView(arrangedSubviews: [ |
||||
dateImageView, |
||||
dateLabel, |
||||
.spacerWidth(7), |
||||
seatRangeImageView, |
||||
seatRangeLabel, |
||||
.spacerWidth(7), |
||||
zoneNameImageView, |
||||
zoneNameLabel |
||||
]) |
||||
bottomRowStack.axis = .horizontal |
||||
bottomRowStack.spacing = 7 |
||||
bottomRowStack.distribution = .fill |
||||
bottomRowStack.setContentHuggingPriority(UILayoutPriority.required, for: .horizontal) |
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [ |
||||
stateLabel, |
||||
topRowStack, |
||||
bottomRowStack |
||||
]) |
||||
stackView.translatesAutoresizingMaskIntoConstraints = false |
||||
stackView.axis = .vertical |
||||
stackView.alignment = .leading |
||||
stackView.spacing = 10 |
||||
stackView.distribution = .fill |
||||
stackView.setContentHuggingPriority(UILayoutPriority.required, for: .vertical) |
||||
background.addSubview(stackView) |
||||
|
||||
// TODO extract constant. Maybe StyleLayout.sideMargin |
||||
let xMargin = CGFloat(7) |
||||
let yMargin = CGFloat(5) |
||||
var checkboxRelatedConstraints = [NSLayoutConstraint]() |
||||
if showCheckbox { |
||||
checkboxRelatedConstraints.append(checkboxImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: xMargin)) |
||||
checkboxRelatedConstraints.append(checkboxImageView.centerYAnchor.constraint(equalTo: centerYAnchor)) |
||||
checkboxRelatedConstraints.append(background.leadingAnchor.constraint(equalTo: checkboxImageView.trailingAnchor, constant: xMargin)) |
||||
} else { |
||||
checkboxRelatedConstraints.append(background.leadingAnchor.constraint(equalTo: leadingAnchor, constant: xMargin)) |
||||
} |
||||
|
||||
NSLayoutConstraint.activate([ |
||||
topRowStack.trailingAnchor.constraint(equalTo: stackView.trailingAnchor), |
||||
|
||||
stackView.leadingAnchor.constraint(equalTo: background.leadingAnchor, constant: 21), |
||||
stackView.trailingAnchor.constraint(equalTo: background.trailingAnchor, constant: -21), |
||||
stackView.topAnchor.constraint(equalTo: background.topAnchor, constant: 16), |
||||
stackView.bottomAnchor.constraint(lessThanOrEqualTo: background.bottomAnchor, constant: -16), |
||||
|
||||
|
||||
background.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -xMargin), |
||||
background.topAnchor.constraint(equalTo: topAnchor, constant: yMargin), |
||||
background.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -yMargin), |
||||
|
||||
stateLabel.heightAnchor.constraint(equalToConstant: 22), |
||||
] + checkboxRelatedConstraints) |
||||
} |
||||
|
||||
required init?(coder aDecoder: NSCoder) { |
||||
fatalError("init(coder:) has not been implemented") |
||||
} |
||||
|
||||
func configure(viewModel: TicketRowViewModel) { |
||||
background.backgroundColor = viewModel.contentsBackgroundColor |
||||
background.layer.cornerRadius = 20 |
||||
background.layer.shadowRadius = 3 |
||||
background.layer.shadowColor = UIColor.black.cgColor |
||||
background.layer.shadowOffset = CGSize(width: 0, height: 0) |
||||
background.layer.shadowOpacity = 0.14 |
||||
background.layer.borderColor = UIColor.black.cgColor |
||||
|
||||
stateLabel.backgroundColor = viewModel.stateBackgroundColor |
||||
stateLabel.layer.cornerRadius = 8 |
||||
stateLabel.clipsToBounds = true |
||||
stateLabel.textColor = viewModel.stateColor |
||||
stateLabel.font = viewModel.subtitleFont |
||||
|
||||
ticketCountLabel.textColor = viewModel.countColor |
||||
ticketCountLabel.font = viewModel.ticketCountFont |
||||
|
||||
titleLabel.textColor = viewModel.titleColor |
||||
titleLabel.font = viewModel.titleFont |
||||
|
||||
venueLabel.textColor = viewModel.titleColor |
||||
venueLabel.font = viewModel.venueFont |
||||
venueLabel.textAlignment = .right |
||||
|
||||
dateLabel.textColor = viewModel.subtitleColor |
||||
dateLabel.font = viewModel.subtitleFont |
||||
|
||||
seatRangeLabel.textColor = viewModel.subtitleColor |
||||
seatRangeLabel.font = viewModel.subtitleFont |
||||
|
||||
zoneNameLabel.textColor = viewModel.subtitleColor |
||||
zoneNameLabel.font = viewModel.subtitleFont |
||||
|
||||
dateImageView.image = R.image.calendar()?.withRenderingMode(.alwaysTemplate) |
||||
seatRangeImageView.image = R.image.ticket()?.withRenderingMode(.alwaysTemplate) |
||||
zoneNameImageView.image = R.image.category()?.withRenderingMode(.alwaysTemplate) |
||||
|
||||
dateImageView.tintColor = viewModel.iconsColor |
||||
seatRangeImageView.tintColor = viewModel.iconsColor |
||||
zoneNameImageView.tintColor = viewModel.iconsColor |
||||
} |
||||
} |
@ -0,0 +1,53 @@ |
||||
// Copyright © 2018 Stormbird PTE. LTD. |
||||
|
||||
import UIKit |
||||
|
||||
class TicketsViewControllerTitleHeader: UIView { |
||||
let background = UIView() |
||||
let titleLabel = UILabel() |
||||
|
||||
override init(frame: CGRect) { |
||||
super.init(frame: frame) |
||||
|
||||
background.translatesAutoresizingMaskIntoConstraints = false |
||||
addSubview(background) |
||||
|
||||
titleLabel.textAlignment = .center |
||||
titleLabel.translatesAutoresizingMaskIntoConstraints = false |
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [titleLabel]) |
||||
stackView.translatesAutoresizingMaskIntoConstraints = false |
||||
stackView.axis = .vertical |
||||
stackView.spacing = 0 |
||||
stackView.distribution = .fill |
||||
background.addSubview(stackView) |
||||
|
||||
let backgroundWidthConstraint = background.widthAnchor.constraint(equalTo: widthAnchor) |
||||
backgroundWidthConstraint.priority = .defaultHigh |
||||
// TODO extract constant. Maybe StyleLayout.sideMargin |
||||
NSLayoutConstraint.activate([ |
||||
background.leadingAnchor.constraint(equalTo: leadingAnchor), |
||||
// background.topAnchor.constraint(equalTo: topAnchor), |
||||
background.centerYAnchor.constraint(equalTo: centerYAnchor), |
||||
backgroundWidthConstraint, |
||||
|
||||
stackView.leadingAnchor.constraint(equalTo: background.leadingAnchor, constant: 21), |
||||
stackView.trailingAnchor.constraint(equalTo: background.trailingAnchor, constant: -21), |
||||
stackView.topAnchor.constraint(equalTo: background.topAnchor, constant: 16), |
||||
stackView.bottomAnchor.constraint(lessThanOrEqualTo: background.bottomAnchor, constant: -16), |
||||
]) |
||||
} |
||||
|
||||
required init?(coder aDecoder: NSCoder) { |
||||
fatalError("init(coder:) has not been implemented") |
||||
} |
||||
|
||||
func configure(title: String) { |
||||
frame = CGRect(x: 0, y: 0, width: 300, height: 90) |
||||
backgroundColor = Colors.appWhite |
||||
|
||||
titleLabel.textColor = Colors.appText |
||||
titleLabel.font = Fonts.light(size: 25)! |
||||
titleLabel.text = title |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
// |
||||
// Created by James Sangalli on 7/3/18. |
||||
// When someone takes an order and pays for it, we call it "claim an order" |
||||
// |
||||
|
||||
import Foundation |
||||
import BigInt |
||||
import JSONRPCKit |
||||
import APIKit |
||||
import Result |
||||
import TrustKeystore |
||||
import JavaScriptKit |
||||
import Result |
||||
|
||||
class ClaimOrderCoordinator { |
||||
private let web3: Web3Swift |
||||
|
||||
init( |
||||
web3: Web3Swift |
||||
) { |
||||
self.web3 = web3 |
||||
} |
||||
|
||||
func claimOrder(indices: [UInt16], |
||||
expiry: BigUInt, |
||||
v: UInt8, |
||||
r: String, |
||||
s: String, |
||||
completion: @escaping (Result<String, AnyError>) -> Void |
||||
) { |
||||
let request = ClaimStormBirdOrder(expiry: expiry, indices: indices, v: v, r: r, s: s) |
||||
web3.request(request: request) { result in |
||||
switch result { |
||||
//TODO handle cases for UI |
||||
case .success(let res): |
||||
print(res) |
||||
completion(.success(res)) |
||||
case .failure(let err): |
||||
print(err) |
||||
completion(.failure(AnyError(err))) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// TODO: Testing purposes only. Remove this when it is fully functional |
||||
func startWeb3() { |
||||
web3.start() |
||||
} |
||||
|
||||
} |
@ -1,63 +0,0 @@ |
||||
// |
||||
// TicketView.swift |
||||
// Alpha-Wallet |
||||
// |
||||
// Created by Oguzhan Gungor on 3/5/18. |
||||
// Copyright © 2018 Alpha-Wallet. All rights reserved. |
||||
// |
||||
|
||||
import UIKit |
||||
|
||||
@IBDesignable |
||||
class TicketView: UIView { |
||||
|
||||
let nibName = "TicketView" |
||||
var contentView: UIView? |
||||
|
||||
@IBOutlet weak var ticketNumberLabel: UILabel! |
||||
@IBOutlet weak var nameLabel: UILabel! |
||||
@IBOutlet weak var venueLabel: UILabel! |
||||
@IBOutlet weak var dateLabel: UILabel! |
||||
@IBOutlet weak var seatLabel: UILabel! |
||||
@IBOutlet weak var zoneLabel: UILabel! |
||||
|
||||
func configure(ticketHolder: TicketHolder) { |
||||
ticketNumberLabel.text = ticketHolder.ticketCount |
||||
nameLabel.text = ticketHolder.name |
||||
venueLabel.text = ticketHolder.venue |
||||
dateLabel.text = ticketHolder.date.format("dd MMM yyyy") |
||||
zoneLabel.text = ticketHolder.zone |
||||
seatLabel.text = ticketHolder.seatRange |
||||
} |
||||
|
||||
} |
||||
|
||||
extension TicketView { |
||||
override |
||||
func awakeFromNib() { |
||||
super.awakeFromNib() |
||||
nibSetup() |
||||
} |
||||
|
||||
override |
||||
func prepareForInterfaceBuilder() { |
||||
super.prepareForInterfaceBuilder() |
||||
nibSetup() |
||||
contentView?.prepareForInterfaceBuilder() |
||||
} |
||||
|
||||
func nibSetup() { |
||||
guard let view = loadViewFromNib() else { return } |
||||
view.frame = bounds |
||||
view.autoresizingMask = [.flexibleWidth, .flexibleHeight] |
||||
addSubview(view) |
||||
contentView = view |
||||
} |
||||
|
||||
func loadViewFromNib() -> UIView? { |
||||
let bundle = Bundle(for: type(of: self)) |
||||
let nib = UINib(nibName: nibName, bundle: bundle) |
||||
return nib.instantiate(withOwner: self, options: nil).first as? UIView |
||||
} |
||||
|
||||
} |
@ -1,100 +0,0 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> |
||||
<device id="retina4_7" orientation="portrait"> |
||||
<adaptation id="fullscreen"/> |
||||
</device> |
||||
<dependencies> |
||||
<deployment identifier="iOS"/> |
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/> |
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/> |
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> |
||||
</dependencies> |
||||
<objects> |
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TicketView" customModule="Trust" customModuleProvider="target"> |
||||
<connections> |
||||
<outlet property="dateLabel" destination="edH-Mp-Aoq" id="SdC-7C-36B"/> |
||||
<outlet property="nameLabel" destination="Ohf-I4-SrC" id="qQj-i4-25Q"/> |
||||
<outlet property="seatLabel" destination="muF-oJ-WiE" id="pGO-iv-93P"/> |
||||
<outlet property="ticketNumberLabel" destination="k4Z-7C-cpP" id="QXS-N0-ygF"/> |
||||
<outlet property="venueLabel" destination="O4M-DA-Ryo" id="FNY-sW-Miv"/> |
||||
<outlet property="zoneLabel" destination="Etp-V9-ttP" id="r4T-s0-NtG"/> |
||||
</connections> |
||||
</placeholder> |
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> |
||||
<view contentMode="scaleToFill" id="iN0-l3-epB"> |
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="85"/> |
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> |
||||
<subviews> |
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GNV-WP-nNS" customClass="DesignableView" customModule="Trust" customModuleProvider="target"> |
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="85"/> |
||||
<subviews> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="x10" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="k4Z-7C-cpP"> |
||||
<rect key="frame" x="8" y="16" width="27" height="21"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/> |
||||
<color key="textColor" red="0.12941176469999999" green="0.73725490199999999" blue="0.29411764709999999" alpha="1" colorSpace="calibratedRGB"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="ARRANGING MORTGAGE" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="Ohf-I4-SrC"> |
||||
<rect key="frame" x="41" y="16" width="177" height="21"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/> |
||||
<nil key="textColor"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="RBS Forbidden City" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="O4M-DA-Ryo"> |
||||
<rect key="frame" x="224" y="16" width="135" height="21"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/> |
||||
<nil key="textColor"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="05 Mar 2018" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="edH-Mp-Aoq"> |
||||
<rect key="frame" x="8" y="53" width="102" height="21"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/> |
||||
<nil key="textColor"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="1-11" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="muF-oJ-WiE"> |
||||
<rect key="frame" x="118" y="53" width="102" height="21"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/> |
||||
<nil key="textColor"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="ZONE A" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Etp-V9-ttP"> |
||||
<rect key="frame" x="249" y="53" width="102" height="21"/> |
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/> |
||||
<nil key="textColor"/> |
||||
<nil key="highlightedColor"/> |
||||
</label> |
||||
</subviews> |
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
<userDefinedRuntimeAttributes> |
||||
<userDefinedRuntimeAttribute type="color" keyPath="borderColor"> |
||||
<color key="value" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth"> |
||||
<real key="value" value="1"/> |
||||
</userDefinedRuntimeAttribute> |
||||
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> |
||||
<real key="value" value="7"/> |
||||
</userDefinedRuntimeAttribute> |
||||
</userDefinedRuntimeAttributes> |
||||
</view> |
||||
</subviews> |
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> |
||||
<constraints> |
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="GNV-WP-nNS" secondAttribute="trailing" id="4xI-H5-uEd"/> |
||||
<constraint firstItem="GNV-WP-nNS" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="BJv-JU-JZs"/> |
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="GNV-WP-nNS" secondAttribute="bottom" id="XAz-dY-Mpn"/> |
||||
<constraint firstItem="GNV-WP-nNS" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" id="bQi-Kl-Hca"/> |
||||
</constraints> |
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> |
||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/> |
||||
<point key="canvasLocation" x="-96" y="91"/> |
||||
</view> |
||||
</objects> |
||||
</document> |
@ -0,0 +1,33 @@ |
||||
// |
||||
// Created by James Sangalli on 7/3/18. |
||||
// This is a struct with the capacity to convert an order to a new format: |
||||
// the data field of a transaction. |
||||
// There are 4 formats of orders: |
||||
// 1) the binary data the signature is corrisponding to. |
||||
// 2) the compressed format, which is Base64 encoded to UniversalLink |
||||
// 3) the JSON format, which is used to pass to feeMaster server. |
||||
// 4) this data format, to pass as part of an Ethereum transaction |
||||
// This class gets you the 4th format. |
||||
// |
||||
|
||||
import Foundation |
||||
import Foundation |
||||
import TrustKeystore |
||||
import BigInt |
||||
|
||||
|
||||
struct ClaimStormBirdOrder: Web3Request { |
||||
typealias Response = String |
||||
|
||||
let expiry: BigUInt |
||||
let indices: [UInt16] |
||||
let v: UInt8 |
||||
let r: String |
||||
let s: String |
||||
|
||||
var type: Web3RequestType { |
||||
let abi = "{\"constant\":false,\"inputs\":[{\"name\":\"expiry\",\"type\":\"uint256\"},{\"name\":\"ticketIndices\",\"type\":\"uint16[]\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"trade\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"}, [\"\(expiry)\", \(indices), \(v), \"\(r)\", \"\(s)\"]" |
||||
let run = "web3.eth.abi.encodeFunctionCall(" + abi + ")" |
||||
return .script(command: run) |
||||
} |
||||
} |
@ -0,0 +1,98 @@ |
||||
// |
||||
// Created by James Sangalli on 8/3/18. |
||||
// |
||||
|
||||
import Foundation |
||||
import XCTest |
||||
@testable import Trust |
||||
import BigInt |
||||
import TrustKeystore |
||||
|
||||
class ClaimOrderCoordinatorTests : XCTestCase { |
||||
|
||||
var expectations = [XCTestExpectation]() |
||||
|
||||
func testClaimOrder() { |
||||
let keystore = try! EtherKeystore() |
||||
let claimOrderCoordinator = FakeClaimOrderCoordinator() |
||||
let expectation = self.expectation(description: "wait til callback") |
||||
expectations.append(expectation) |
||||
var indices = [UInt16]() |
||||
indices.append(14) |
||||
let expiry = BigUInt("0") |
||||
let v = UInt8(27) |
||||
let r = "0x2d8e40406bf6175036ab1e1099b48590438bf48d429a8b209120fecd07894566" |
||||
let s = "0x59ccf58ca36f681976228309fdd9de7e30e860084d9d63014fa79d48a25bb93d" |
||||
|
||||
let token = TokenObject( |
||||
contract: "0xacDe9017473D7dC82ACFd0da601E4de291a7d6b0", |
||||
name: "MJ Comeback", |
||||
symbol: "MJC", |
||||
decimals: 0, |
||||
value: "0", |
||||
isCustom: true, |
||||
isDisabled: false, |
||||
isStormBird: true |
||||
) |
||||
|
||||
claimOrderCoordinator.claimOrder(indices: indices, expiry: expiry!, v: v, r: r, s: s) { result in |
||||
switch result { |
||||
case .success(let payload): |
||||
let address: Address = .makeStormBird() |
||||
let transaction = UnconfirmedTransaction( |
||||
transferType: .stormBirdOrder(token), |
||||
value: BigInt("0"), |
||||
to: address, |
||||
data: Data(bytes: payload.hexa2Bytes), |
||||
gasLimit: .none, |
||||
gasPrice: 14400, |
||||
nonce: .none, |
||||
v: v, |
||||
r: r, |
||||
s: s, |
||||
expiry: expiry, |
||||
indices: indices |
||||
) |
||||
|
||||
let session: WalletSession = .makeStormBirdSession() |
||||
|
||||
let configurator = TransactionConfigurator( |
||||
session: session, |
||||
account: .make(), |
||||
transaction: transaction |
||||
) |
||||
|
||||
let unsignedTransaction = configurator.formUnsignedTransaction() |
||||
|
||||
let account = keystore.getAccount(for: address)! |
||||
|
||||
let signedTransaction = UnsignedTransaction(value: unsignedTransaction.value, |
||||
account: account, |
||||
to: unsignedTransaction.to, |
||||
nonce: unsignedTransaction.nonce, |
||||
data: unsignedTransaction.data, |
||||
gasPrice: unsignedTransaction.gasPrice, |
||||
gasLimit: unsignedTransaction.gasLimit, |
||||
chainID: 3) |
||||
|
||||
|
||||
let sendTransactionCoordinator = SendTransactionCoordinator(session: session, |
||||
keystore: keystore, |
||||
confirmType: .signThenSend) |
||||
|
||||
sendTransactionCoordinator.send(transaction: signedTransaction) { result in |
||||
switch result { |
||||
case .success(let res): |
||||
print(res); |
||||
expectation.fulfill() |
||||
case .failure(let error): |
||||
print(error); |
||||
} |
||||
} |
||||
case .failure: break |
||||
} |
||||
} |
||||
wait(for: expectations, timeout: 10000) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,11 @@ |
||||
// Copyright SIX DAY LLC. All rights reserved. |
||||
|
||||
import Foundation |
||||
@testable import Trust |
||||
|
||||
class FakeClaimOrderCoordinator: ClaimOrderCoordinator { |
||||
convenience init() { |
||||
self.init(web3: Web3Swift()) |
||||
startWeb3() |
||||
} |
||||
} |
@ -0,0 +1,59 @@ |
||||
import Foundation |
||||
import XCTest |
||||
@testable import Trust |
||||
import TrustKeystore |
||||
import BigInt |
||||
|
||||
class MarketQueueHandlerTests: XCTestCase { |
||||
|
||||
var expectations = [XCTestExpectation]() |
||||
let keyStore = FakeEtherKeystore() |
||||
|
||||
func testGetOrders() { |
||||
let expectation = self.expectation(description: "wait til callback") |
||||
expectations.append(expectation) |
||||
MarketQueueHandler().getOrders(callback: { |
||||
callback in |
||||
print(callback) |
||||
expectation.fulfill() |
||||
}) |
||||
wait(for: expectations, timeout: 10) |
||||
} |
||||
|
||||
//TODO reuse when test account works |
||||
func testPuttingOrderToQueue() { |
||||
// let expectation = self.expectation(description: "wait til callback") |
||||
// expectations.append(expectation) |
||||
// var testOrdersList : Array<Order> = Array<Order>() |
||||
// let account = keyStore.createAccount(password: "test") |
||||
// //set up test orders |
||||
// var indices = [UInt16]() |
||||
// indices.append(1) |
||||
// indices.append(2) |
||||
// |
||||
// var timestamp = NSDate().timeIntervalSince1970.description //1521562138 |
||||
// timestamp = timestamp.substring(to: timestamp.count - 6); |
||||
// let ts = Int(timestamp)! + 300; |
||||
// let testOrder1 = Order( |
||||
// price: BigUInt("100000")!, |
||||
// indices: indices, |
||||
// expiry: BigUInt(String(ts))!, |
||||
// contractAddress: "bC9a1026A4BC6F0BA8Bbe486d1D09dA5732B39e4".lowercased(), |
||||
// start: BigUInt("500000210121213")!, |
||||
// count: 3 |
||||
// ) |
||||
// testOrdersList.append(testOrder1) |
||||
// let signedOrders = try! SignOrders().signOrders(orders: testOrdersList, account: account) |
||||
// let privateKey = keyStore.exportPrivateKey(account: account) |
||||
// let publicKey = try! Secp256k1.shared.pubKeyFromPrivateKey(from: privateKey.dematerialize()) |
||||
// |
||||
// OrdersRequest.init().putOrderToServer(signedOrders: signedOrders, publicKey: publicKey.hexString, callback: { |
||||
// callback in |
||||
// print(callback) |
||||
// expectation.fulfill() |
||||
// }) |
||||
// |
||||
// wait(for: expectations, timeout: 10) |
||||
} |
||||
} |
||||
|
@ -1,64 +0,0 @@ |
||||
import Foundation |
||||
import XCTest |
||||
@testable import Trust |
||||
import TrustKeystore |
||||
import BigInt |
||||
|
||||
class OrderRequestTest : XCTestCase { |
||||
|
||||
var expectations = [XCTestExpectation]() |
||||
|
||||
func testHttpCallToQueue() { |
||||
let expectation = self.expectation(description: "wait til callback") |
||||
expectations.append(expectation) |
||||
OrdersRequest.init().getOrders(callback: { |
||||
callback in |
||||
print(callback) |
||||
expectation.fulfill() |
||||
}) |
||||
|
||||
wait(for: expectations, timeout: 10) |
||||
} |
||||
|
||||
func testPuttingOrderToQueue() { |
||||
let expectation = self.expectation(description: "wait til callback") |
||||
expectations.append(expectation) |
||||
|
||||
var testOrdersList : Array<Order> = Array<Order>() |
||||
let keyStore = FakeEtherKeystore() |
||||
let account = keyStore.createAccount(password: "haha") |
||||
|
||||
//set up test orders |
||||
var indices = [UInt16]() |
||||
indices.append(1) |
||||
indices.append(2) |
||||
|
||||
let testOrder1 = Order(price: BigUInt("100000")!, indices: indices, |
||||
expiry: BigUInt("0")!, contractAddress: "007bee82bdd9e866b2bd114780a47f2261c684e3", |
||||
start: BigUInt("500000210121213")!, count: 3) |
||||
testOrdersList.append(testOrder1) |
||||
|
||||
let signOrders = SignOrders() |
||||
|
||||
//TODO fix signature issues |
||||
var signedOrders : Array<SignedOrder> = signOrders.signOrders(orders: testOrdersList, account: account) |
||||
|
||||
signedOrders[0].signature = "0x1cae08113567db5303fb1ed1b157fbc8c7247aa" + |
||||
"9689ee76902d731c9806ab9853d8fcded6145fc7ebe5c32e41e247b315" + |
||||
"b2b23f41dcb3acd17d01a9f6140669f1c" |
||||
|
||||
let privateKey = keyStore.exportPrivateKey(account: account) |
||||
|
||||
let publicKey = try! Secp256k1.shared.pubKeyFromPrivateKey(from: |
||||
privateKey.dematerialize()).hexString |
||||
|
||||
OrdersRequest.init().putOrderToServer(signedOrders: signedOrders, publicKey: publicKey, callback: { |
||||
callback in |
||||
print(callback) |
||||
expectation.fulfill() |
||||
}) |
||||
|
||||
wait(for: expectations, timeout: 10) |
||||
} |
||||
} |
||||
|
@ -0,0 +1,20 @@ |
||||
// |
||||
// Created by James Sangalli on 24/3/18. |
||||
// |
||||
|
||||
import Foundation |
||||
@testable import Trust |
||||
import XCTest |
||||
|
||||
class UniversalLinkHandlerTests: XCTestCase { |
||||
|
||||
func testUniversalLinkParser() { |
||||
let testUrl = "https://app.awallet.io/AAGGoFq8Ule8mhAmpLxvC6i75IbR0J2lcys55AECAwQFBgcICYvWi5I+Tl5m9XumBD5jLIm6i39kD7F40UW4BaJDEVOWLTYz3kek7wjT7Bn+2w0NCiyx7zWuvseTA8qfoIqCIxob" |
||||
let signedOrder = UniversalLinkHandler().parseURL(url: testUrl) |
||||
XCTAssertGreaterThanOrEqual(signedOrder.signature.count, 130) |
||||
let url = UniversalLinkHandler().createUniversalLink(signedOrder: signedOrder) |
||||
print(url) |
||||
XCTAssertEqual(testUrl, url) |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue