Invite screen and liberated invite code and tests

This commit is contained in:
Francisco Gindre 2020-07-03 21:19:45 -03:00
parent c870646780
commit 1e336b256b
7 changed files with 270 additions and 1 deletions

View File

@ -53,6 +53,10 @@
0DB5331024A623AF0090D722 /* sapling-output.params in Resources */ = {isa = PBXBuildFile; fileRef = 0DB5330E24A623AF0090D722 /* sapling-output.params */; };
0DB5331224A6413D0090D722 /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB5331124A6413D0090D722 /* LazyView.swift */; };
0DB5331424A6B8B60090D722 /* memo.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB5331324A6B8B60090D722 /* memo.pb.swift */; };
0DB9EF5624AFA94300FE89E6 /* LiberatedInviteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB9EF5524AFA94300FE89E6 /* LiberatedInviteTests.swift */; };
0DB9EF5824AFAA6100FE89E6 /* InviteHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB9EF5724AFAA6100FE89E6 /* InviteHandler.swift */; };
0DB9EF5A24AFF03400FE89E6 /* InvitePeople.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB9EF5924AFF03400FE89E6 /* InvitePeople.swift */; };
0DB9EF5C24B001A000FE89E6 /* ShareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB9EF5B24B001A000FE89E6 /* ShareView.swift */; };
0DC1B47B24AAA44B00AEF3D0 /* ZircleService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC1B47A24AAA44B00AEF3D0 /* ZircleService.swift */; };
0DC4B44A24ACB62400B3CBA2 /* darkside.grpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC4B43D24ACB62400B3CBA2 /* darkside.grpc.swift */; };
0DC4B44B24ACB62400B3CBA2 /* darkside.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC4B43E24ACB62400B3CBA2 /* darkside.pb.swift */; };
@ -145,6 +149,10 @@
0DB5330E24A623AF0090D722 /* sapling-output.params */ = {isa = PBXFileReference; lastKnownFileType = file; path = "sapling-output.params"; sourceTree = "<group>"; };
0DB5331124A6413D0090D722 /* LazyView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
0DB5331324A6B8B60090D722 /* memo.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = memo.pb.swift; sourceTree = "<group>"; };
0DB9EF5524AFA94300FE89E6 /* LiberatedInviteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LiberatedInviteTests.swift; path = ZirclesTests/LiberatedInviteTests.swift; sourceTree = SOURCE_ROOT; };
0DB9EF5724AFAA6100FE89E6 /* InviteHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteHandler.swift; sourceTree = "<group>"; };
0DB9EF5924AFF03400FE89E6 /* InvitePeople.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitePeople.swift; sourceTree = "<group>"; };
0DB9EF5B24B001A000FE89E6 /* ShareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareView.swift; sourceTree = "<group>"; };
0DC1B47A24AAA44B00AEF3D0 /* ZircleService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZircleService.swift; sourceTree = "<group>"; };
0DC4B43D24ACB62400B3CBA2 /* darkside.grpc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = darkside.grpc.swift; sourceTree = "<group>"; };
0DC4B43E24ACB62400B3CBA2 /* darkside.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = darkside.pb.swift; sourceTree = "<group>"; };
@ -230,6 +238,7 @@
0D64CEC024A3F2580080AA4F /* FirstScreen.swift */,
0D64CEC224A4137D0080AA4F /* ZircleData.swift */,
0D3FB6FD24A4CD4E0034A52A /* MockEnvironment.swift */,
0DB9EF5B24B001A000FE89E6 /* ShareView.swift */,
);
path = Utils;
sourceTree = "<group>";
@ -292,6 +301,7 @@
0D1366C124991A6200F0EB54 /* ZirclesTests */ = {
isa = PBXGroup;
children = (
0DB9EF5524AFA94300FE89E6 /* LiberatedInviteTests.swift */,
0DC4B43C24ACB62400B3CBA2 /* proto */,
0DC4B44024ACB62400B3CBA2 /* utils */,
0D1366C224991A6200F0EB54 /* ZirclesTests.swift */,
@ -319,6 +329,7 @@
0DEE59A924A2B90F00447C15 /* CreateNewZircleDescription.swift */,
0D64CEC424A41AF30080AA4F /* AppDetails.swift */,
0DD7278824A5532300C36D27 /* AllZirclesView.swift */,
0DB9EF5924AFF03400FE89E6 /* InvitePeople.swift */,
);
path = Views;
sourceTree = "<group>";
@ -361,6 +372,7 @@
isa = PBXGroup;
children = (
0DC1B47A24AAA44B00AEF3D0 /* ZircleService.swift */,
0DB9EF5724AFAA6100FE89E6 /* InviteHandler.swift */,
);
path = ZircleService;
sourceTree = "<group>";
@ -663,6 +675,7 @@
0D11D3D4249D05D800223146 /* Wedge.swift in Sources */,
0D1366AE24991A6000F0EB54 /* SceneDelegate.swift in Sources */,
0D11D3D0249C3AE400223146 /* Card.swift in Sources */,
0DB9EF5C24B001A000FE89E6 /* ShareView.swift in Sources */,
0D64CEB724A3CDA90080AA4F /* QRCodeGenerator.swift in Sources */,
0D64CEBF24A3D3A30080AA4F /* CombineSynchronizer.swift in Sources */,
0DD7278B24A564AF00C36D27 /* ZircleListCard.swift in Sources */,
@ -677,10 +690,12 @@
0D64CEBA24A3CDA90080AA4F /* zECC+SwiftUI.swift in Sources */,
0D11D3D2249CE6C800223146 /* GlowEffect.swift in Sources */,
0D64CEB524A3CDA90080AA4F /* String+Zcash.swift in Sources */,
0DB9EF5A24AFF03400FE89E6 /* InvitePeople.swift in Sources */,
0DB5331224A6413D0090D722 /* LazyView.swift in Sources */,
0D11D3DB249D5F1600223146 /* NeumorphicProgressBar.swift in Sources */,
0D64CEBD24A3CDA90080AA4F /* SimpleLogger.swift in Sources */,
0D64CEBC24A3CDA90080AA4F /* BalanceUtils.swift in Sources */,
0DB9EF5824AFAA6100FE89E6 /* InviteHandler.swift in Sources */,
0D5142C824A16F9600F9AE2E /* CreateNewTypeOfZircle.swift in Sources */,
0DB5331424A6B8B60090D722 /* memo.pb.swift in Sources */,
0D64CEBB24A3CDA90080AA4F /* CameraAccessHelper.swift in Sources */,
@ -706,6 +721,7 @@
0DC4B44B24ACB62400B3CBA2 /* darkside.pb.swift in Sources */,
0DC4B45324ACB62400B3CBA2 /* Stubs.swift in Sources */,
0DC4B45124ACB62400B3CBA2 /* TestDbBuilder.swift in Sources */,
0DB9EF5624AFA94300FE89E6 /* LiberatedInviteTests.swift in Sources */,
0DC4B45224ACB62400B3CBA2 /* FakeChainBuilder.swift in Sources */,
0DC4B44E24ACB62400B3CBA2 /* SampleLogger.swift in Sources */,
0DC4B45724ACF8EE00B3CBA2 /* TestCoordinator.swift in Sources */,

View File

@ -128,7 +128,7 @@ extension SeedManager {
guard keychain.get(spendingKeyKey(key)) == nil else {
throw SeedManagerError.alreadyImported
}
keychain.set(String(phrase), forKey: spendingKeyKey(key))
keychain.set(String(spendingKey), forKey: spendingKeyKey(key))
}
func getZircleKeys(_ key: String) throws -> (height: BlockHeight, phrase: String, spendingKey: String)? {

View File

@ -0,0 +1,30 @@
//
// ShareView.swift
// Zircles
//
// Created by Francisco Gindre on 7/3/20.
// Copyright © 2020 Electric Coin Company. All rights reserved.
//
import SwiftUI
struct ShareSheet: UIViewControllerRepresentable {
typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
let activityItems: [Any]
let applicationActivities: [UIActivity]? = nil
let excludedActivityTypes: [UIActivity.ActivityType]? = nil
let callback: Callback? = nil
func makeUIViewController(context: Context) -> UIActivityViewController {
let controller = UIActivityViewController(
activityItems: activityItems,
applicationActivities: applicationActivities)
controller.excludedActivityTypes = excludedActivityTypes
controller.completionWithItemsHandler = callback
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
// nothing to do here
}
}

View File

@ -0,0 +1,104 @@
//
// InvitePeople.swift
// Zircles
//
// Created by Francisco Gindre on 7/3/20.
// Copyright © 2020 Electric Coin Company. All rights reserved.
//
import SwiftUI
struct InvitePeople: View {
let liberatedZircle: String
@State var isSharing = false
var body: some View {
ZStack {
VStack(spacing: 0) {
Image("Invite").edgesIgnoringSafeArea(.all)
.frame(alignment: .top)
Spacer()
}
VStack(spacing:20) {
Spacer()
HStack() {
Text("""
Invite
Some Peeps
""")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(.white)
.multilineTextAlignment(.leading)
Spacer()
}
.padding(.horizontal, 30)
ZStack {
Color.background
VStack(spacing: 20) {
Text("This is your Sharing Link")
.font(.title)
Text(liberatedZircle)
.font(.body)
.lineLimit(3)
.truncationMode(.middle)
.foregroundColor(.textLightGray)
Button(action:{}){
Text("Copy To clipboard")
.font(.system(size: 20, weight: .bold, design: .default))
.shadow(color:Color(red: 0.2, green: 0.2, blue: 0.2).opacity(0.2), radius: 1, x: 0, y: 2)
.foregroundColor(Color.textDarkGray)
.modifier(ZcashButtonBackground(buttonShape: .roundedCorners(fillStyle: .solid(color: Color.buttonGray))))
.shadow(color: Color(red: 0.2, green: 0.2, blue: 0.2).opacity(0.5), radius: 25, x: 10, y: 10)
.frame(height: 50)
}
Button(action: {
self.isSharing = true
}, label: {
Text("Invite People")
.font(.system(size: 20, weight: .bold, design: .default))
.shadow(color:Color(red: 0.2, green: 0.2, blue: 0.2).opacity(0.2), radius: 1, x: 0, y: 2)
.foregroundColor(Color.background)
.modifier(ZcashButtonBackground(buttonShape: .roundedCorners(fillStyle: .solid(color: Color.buttonBlue))))
.shadow(color: Color(red: 0.2, green: 0.2, blue: 0.2).opacity(0.5), radius: 25, x: 10, y: 10)
.frame(height: 50)
})
}
.padding(.all, 30)
}.cornerRadius(25, corners: [.topLeft,.topRight])
.frame(height: 300,
alignment: .bottom)
}.background(Color.clear)
}
.navigationBarTitle("", displayMode: .inline)
.sheet(isPresented: $isSharing) {
ShareSheet(activityItems: [liberatedZircle])
}
}
}
struct RoundedCorner: Shape {
var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}
}
extension View {
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
clipShape( RoundedCorner(radius: radius, corners: corners) )
}
}
struct InvitePeople_Previews: PreviewProvider {
static var previews: some View {
InvitePeople(liberatedZircle: LiberatedInviteHandler.inviteWith(name: "Hackathon Drinks", viewingkey: "zxviews1qvhluf0pqqqqpq8ywsuq89wulufzghltkt76vz33rrhpaekeakr68at2lglxwfuxkth58vw8cp9s8z0qguqzxc0wfaz6f4mp7vfs47hex6n38tczds7uakalf3a25qwth56v8tz95p4qfyjquk7thwzr3uq6hwgwu4emxm3wrf3yspgatcvup83pl96jrvaymznxa5vdlh6dfgfuzja3egv96hg5eyelrlgkf29su9hucds8zjt2lnsqlcaajq7klkjxc40v80d6sfqkvwjet", height: 663190).absoluteString)
}
}

View File

@ -0,0 +1,79 @@
//
// InviteHandler.swift
// Zircles
//
// Created by Francisco Gindre on 7/3/20.
// Copyright © 2020 Electric Coin Company. All rights reserved.
//
import Foundation
/**
Liberated Invite
3 parameters
https://invite.zircles.co?vk=xxxxxxxxxx&height=xxxxxxxxx&name=xxxxxxx
*/
struct LiberatedInvite {
let name: String
let viewingKey: String
let height: Int
}
class LiberatedInviteHandler {
enum InviteComponents: String {
case scheme = "https"
case host = "invite.zircles.co"
case viewingKey = "vk"
case height
case name
}
static func inviteWith(name: String,
viewingkey: String,
height: Int) -> URL {
let nameItem = URLQueryItem(name: InviteComponents.name.rawValue, value: name)
let viewingkeyItem = URLQueryItem(name: InviteComponents.viewingKey.rawValue, value: viewingkey)
let heightItem = URLQueryItem(name: InviteComponents.height.rawValue, value: String(height))
var components = URLComponents()
components.scheme = InviteComponents.scheme.rawValue
components.host = InviteComponents.host.rawValue
components.queryItems = [ nameItem, viewingkeyItem, heightItem]
return components.url!
}
static func parseInvite(_ url: URL) -> LiberatedInvite? {
guard url.host == InviteComponents.host.rawValue else {
return nil
}
guard let parsedScheme = url.scheme,
parsedScheme == InviteComponents.scheme.rawValue else {
return nil
}
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return nil
}
guard let vk = components.queryItems?.first(where: { $0.name == InviteComponents.viewingKey.rawValue})?.value else {
return nil
}
guard let name = components.queryItems?.first(where: {$0.name == InviteComponents.name.rawValue})?.value else {
return nil
}
guard let heightParam = components.queryItems?.first(where: { $0.name == InviteComponents.height.rawValue })?.value,
let height = Int(heightParam) else {
return nil
}
return LiberatedInvite(name: name,
viewingKey: vk,
height: height)
}
}

View File

@ -72,6 +72,7 @@ struct ConcreteZircle: ZircleEntity {
import MnemonicSwift
extension CombineSynchronizer: ZircleService {
func closeZircle(name: String) throws {
}

View File

@ -0,0 +1,39 @@
//
// LiberatedInviteTests.swift
// ZirclesTests
//
// Created by Francisco Gindre on 7/3/20.
// Copyright © 2020 Electric Coin Company. All rights reserved.
//
import XCTest
@testable import Zircles
class LiberatedInviteTests: XCTestCase {
let extendedViewingKey: String = "zxviews1qvhluf0pqqqqpq8ywsuq89wulufzghltkt76vz33rrhpaekeakr68at2lglxwfuxkth58vw8cp9s8z0qguqzxc0wfaz6f4mp7vfs47hex6n38tczds7uakalf3a25qwth56v8tz95p4qfyjquk7thwzr3uq6hwgwu4emxm3wrf3yspgatcvup83pl96jrvaymznxa5vdlh6dfgfuzja3egv96hg5eyelrlgkf29su9hucds8zjt2lnsqlcaajq7klkjxc40v80d6sfqkvwjet"
let zircleName = "Hackathon Drinks"
let height: Int = 663190
func expectedUrlString() -> String {
LiberatedInviteHandler.InviteComponents.scheme.rawValue +
"://" +
LiberatedInviteHandler.InviteComponents.host.rawValue + "?name=\(zircleName.replacingOccurrences(of: " ", with: "%20"))&vk=\(extendedViewingKey)&height=\(height)"
}
func testHappyPath() throws {
guard let parsedInvite = LiberatedInviteHandler.parseInvite(URL(string: expectedUrlString())!) else {
XCTFail("parsed invite nil")
return
}
XCTAssertEqual(parsedInvite.name, zircleName)
XCTAssertEqual(parsedInvite.height, height)
XCTAssertEqual(parsedInvite.viewingKey, extendedViewingKey)
}
func testLiberatedPaymentGeneration() {
let invite = LiberatedInviteHandler.inviteWith(name: zircleName, viewingkey: extendedViewingKey, height: height)
XCTAssertEqual(invite, URL(string: expectedUrlString())!)
}
}