diff --git a/Kukai Mobile.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Kukai Mobile.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index de385198..64b3ca4e 100644 --- a/Kukai Mobile.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Kukai Mobile.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -63,22 +63,13 @@ "version" : "20.0.0" } }, - { - "identity" : "kingfisher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/simonmcl/Kingfisher.git", - "state" : { - "revision" : "292ab48438344320e7e9bcf3da46f766c30f7070", - "version" : "1.0.0" - } - }, { "identity" : "kukai-core-swift", "kind" : "remoteSourceControl", "location" : "https://github.com/kukai-wallet/kukai-core-swift", "state" : { "branch" : "develop", - "revision" : "3c5e492f50d062cdc199622d63e826e640baec25" + "revision" : "55c19abeb503e4e69c7a811d85e77d8199e74b9d" } }, { @@ -99,6 +90,15 @@ "version" : "14.3.1" } }, + { + "identity" : "sdwebimage", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SDWebImage/SDWebImage.git", + "state" : { + "revision" : "59730af512c06fb569c119d737df4c1c499e001d", + "version" : "5.18.10" + } + }, { "identity" : "secp256k1.swift", "kind" : "remoteSourceControl", @@ -135,15 +135,6 @@ "version" : "3.1.2" } }, - { - "identity" : "svgkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/simonmcl/SVGKit", - "state" : { - "revision" : "3ca70eae8573b876e403be8a9bdbe784fd560c0b", - "version" : "3.0.3" - } - }, { "identity" : "swift-qrcode-generator", "kind" : "remoteSourceControl", diff --git a/Kukai Mobile/AppDelegate.swift b/Kukai Mobile/AppDelegate.swift index 7d339a1b..4b356272 100644 --- a/Kukai Mobile/AppDelegate.swift +++ b/Kukai Mobile/AppDelegate.swift @@ -45,7 +45,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Setup any necessary settings, such as RAM limits - MediaProxyService.setupKingfisher() + MediaProxyService.setupImageLibrary() // process special arguments coming from XCUITest to do things like show keyboard and reset app data diff --git a/Kukai Mobile/Assets.xcassets/Icons/Masters/template/FavCorner.imageset/Contents.json b/Kukai Mobile/Assets.xcassets/Icons/Masters/template/FavCorner.imageset/Contents.json new file mode 100644 index 00000000..265a38dd --- /dev/null +++ b/Kukai Mobile/Assets.xcassets/Icons/Masters/template/FavCorner.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "FavCorner.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/Kukai Mobile/Assets.xcassets/Icons/Masters/template/FavCorner.imageset/FavCorner.svg b/Kukai Mobile/Assets.xcassets/Icons/Masters/template/FavCorner.imageset/FavCorner.svg new file mode 100644 index 00000000..9cb42895 --- /dev/null +++ b/Kukai Mobile/Assets.xcassets/Icons/Masters/template/FavCorner.imageset/FavCorner.svg @@ -0,0 +1,3 @@ + + + diff --git a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Email_Outlined.imageset/Contents.json b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Email_Outlined.imageset/Contents.json index d0f1557e..7839d569 100644 --- a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Email_Outlined.imageset/Contents.json +++ b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Email_Outlined.imageset/Contents.json @@ -11,6 +11,6 @@ }, "properties" : { "preserves-vector-representation" : true, - "template-rendering-intent" : "template" + "template-rendering-intent" : "original" } } diff --git a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Contents.json b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Contents.json index 2cbddd6b..7f38b1ef 100644 --- a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Contents.json +++ b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Social_Twitter_1color.svg", + "filename" : "Social_Twitter_color.svg", "idiom" : "universal" } ], diff --git a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Social_Twitter_1color.svg b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Social_Twitter_1color.svg deleted file mode 100644 index 3b5c3c9e..00000000 --- a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Social_Twitter_1color.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Social_Twitter_color.svg b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Social_Twitter_color.svg new file mode 100644 index 00000000..dc8ef14b --- /dev/null +++ b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_1color.imageset/Social_Twitter_color.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Contents.json b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Contents.json index 4c060d45..e4cbd805 100644 --- a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Contents.json +++ b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Social_Twitter_color.svg", + "filename" : "Social_Twitter_1color.svg", "idiom" : "universal" } ], diff --git a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Social_Twitter_1color.svg b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Social_Twitter_1color.svg new file mode 100644 index 00000000..3e840c96 --- /dev/null +++ b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Social_Twitter_1color.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Social_Twitter_color.svg b/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Social_Twitter_color.svg deleted file mode 100644 index 976489e9..00000000 --- a/Kukai Mobile/Assets.xcassets/Icons/social/Social_Twitter_color.imageset/Social_Twitter_color.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/Kukai Mobile/Controls/ScanViewController.swift b/Kukai Mobile/Controls/ScanViewController.swift index 330cd69c..6fbf5fcf 100644 --- a/Kukai Mobile/Controls/ScanViewController.swift +++ b/Kukai Mobile/Controls/ScanViewController.swift @@ -70,7 +70,7 @@ class ScanViewController: UIViewController, AVCaptureMetadataOutputObjectsDelega } @objc func textFieldDone() { - found(code: textfield.text ?? "") + checkForBeaconAndReport(stringToCheck: textfield.text ?? "") } func setupNav() { @@ -329,13 +329,23 @@ class ScanViewController: UIViewController, AVCaptureMetadataOutputObjectsDelega } func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { - captureSession.stopRunning() if let metadataObject = metadataObjects.first { guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return } guard let stringValue = readableObject.stringValue else { return } AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate)) - found(code: stringValue) + checkForBeaconAndReport(stringToCheck: stringValue) + } + } + + private func checkForBeaconAndReport(stringToCheck: String) { + if let data = stringToCheck.base58CheckDecodedData, let json = try? JSONSerialization.jsonObject(with: data) as? [String: String], let _ = json["relayServer"], let _ = json["publicKey"] { + self.windowError(withTitle: "error".localized(), description: "error-beacon-not-supported".localized()) + self.textfield.text = "" + + } else { + captureSession.stopRunning() + found(code: stringToCheck) } } diff --git a/Kukai Mobile/Controls/ThemeManager.swift b/Kukai Mobile/Controls/ThemeManager.swift index 26d62984..639cd5ff 100644 --- a/Kukai Mobile/Controls/ThemeManager.swift +++ b/Kukai Mobile/Controls/ThemeManager.swift @@ -6,6 +6,7 @@ // import UIKit +import KukaiCoreSwift import os.log /** @@ -507,7 +508,10 @@ public class ThemeManager { } public func updateSystemInterfaceStyle() { - UIApplication.shared.currentWindow?.overrideUserInterfaceStyle = currentInterfaceStyle() + let current = currentInterfaceStyle() + + UIApplication.shared.currentWindow?.overrideUserInterfaceStyle = current + MediaProxyService.isDarkMode = (current == .dark) } public func currentInterfaceStyle() -> UIUserInterfaceStyle { diff --git a/Kukai Mobile/Extensions/UIImageView+extensions.swift b/Kukai Mobile/Extensions/UIImageView+extensions.swift index c98c0871..0375b912 100644 --- a/Kukai Mobile/Extensions/UIImageView+extensions.swift +++ b/Kukai Mobile/Extensions/UIImageView+extensions.swift @@ -6,15 +6,10 @@ // import UIKit -import Kingfisher import KukaiCoreSwift extension UIImageView { - func setImageToCurrentSize(url: URL?) { - self.kf.setImage(with: url, options: [.processor( DownsamplingImageProcessor(size: CGSize(width: self.frame.width, height: self.frame.height)) )]) - } - func tint(color: UIColor) { self.image = self.image?.withRenderingMode(.alwaysTemplate) self.tintColor = color diff --git a/Kukai Mobile/Extensions/UIView+extensions.swift b/Kukai Mobile/Extensions/UIView+extensions.swift index c8d08eb3..f5cc7fff 100644 --- a/Kukai Mobile/Extensions/UIView+extensions.swift +++ b/Kukai Mobile/Extensions/UIView+extensions.swift @@ -100,7 +100,7 @@ extension UIView { rotateAnimation.toValue = CGFloat(Double.pi * 2) rotateAnimation.isRemovedOnCompletion = false rotateAnimation.duration = duration - rotateAnimation.repeatCount=Float.infinity + rotateAnimation.repeatCount = Float.infinity self.layer.add(rotateAnimation, forKey: nil) } diff --git a/Kukai Mobile/Localization/en.lproj/Localizable.strings b/Kukai Mobile/Localization/en.lproj/Localizable.strings index d2a54a2a..af7d7b97 100644 --- a/Kukai Mobile/Localization/en.lproj/Localizable.strings +++ b/Kukai Mobile/Localization/en.lproj/Localizable.strings @@ -50,3 +50,4 @@ "error-unsupported-sign"="Unsupported signature request"; "error-wc2-unrecoverable"="An unknown error occured with the connection. This operation can't continue. Please check the other application and try agian"; "error-wc2-cant-continue"="Unable to continue with this request due to system error"; +"error-beacon-not-supported"="Beacon QRCodes are not supported, only Wallet Connect. Please make sure you are using the kukai option. If you are, please contact the dApp support team and ask them to update their beacon version"; diff --git a/Kukai Mobile/Modules/Account/Account.storyboard b/Kukai Mobile/Modules/Account/Account.storyboard index d84d3014..3ac0d113 100644 --- a/Kukai Mobile/Modules/Account/Account.storyboard +++ b/Kukai Mobile/Modules/Account/Account.storyboard @@ -1,9 +1,9 @@ - + - + @@ -60,13 +60,13 @@ - + @@ -453,200 +453,7 @@ for your Digital Assets - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + - - + - - - + - @@ -830,22 +596,14 @@ for your Digital Assets - - - - - - - - @@ -2211,9 +1969,6 @@ Passcode must have at least 3 unique digits, and no more than 3 sequential digit - - - @@ -2223,15 +1978,9 @@ Passcode must have at least 3 unique digits, and no more than 3 sequential digit - - - - - - @@ -2250,21 +1999,12 @@ Passcode must have at least 3 unique digits, and no more than 3 sequential digit - - - - - - - - - @@ -2300,14 +2040,10 @@ Passcode must have at least 3 unique digits, and no more than 3 sequential digit - - - - - + diff --git a/Kukai Mobile/Modules/Onboarding/CreateWithSocialViewController.swift b/Kukai Mobile/Modules/Onboarding/CreateWithSocialViewController.swift index 047345b7..cc58c74b 100644 --- a/Kukai Mobile/Modules/Onboarding/CreateWithSocialViewController.swift +++ b/Kukai Mobile/Modules/Onboarding/CreateWithSocialViewController.swift @@ -22,18 +22,7 @@ class CreateWithSocialViewController: UIViewController { @IBOutlet var twitterButton: CustomisableButton! @IBOutlet var redditButton: CustomisableButton! - @IBOutlet var socialOptions2: UIStackView! - @IBOutlet var discordButton: CustomisableButton! - @IBOutlet var twitchButton: CustomisableButton! - @IBOutlet var lineButton: CustomisableButton! - - @IBOutlet var socialOptions3: UIStackView! - @IBOutlet var githubButton: CustomisableButton! - - @IBOutlet var emailTextField: ValidatorTextField! @IBOutlet var continueWIthEmailButton: CustomisableButton! - @IBOutlet var viewMoreOptionsButton: CustomisableButton! - private let cloudKitService = CloudKitService() private var appleGradient = CAGradientLayer() @@ -45,11 +34,6 @@ class CreateWithSocialViewController: UIViewController { super.viewDidLoad() let _ = self.view.addGradientBackgroundFull() - scrollView.addGestureRecognizer(UITapGestureRecognizer(target: emailTextField, action: #selector(resignFirstResponder))) - - socialOptions2.isHidden = true - socialOptions3.isHidden = true - appleButton.customButtonType = .primary appleButton.configuration?.imagePadding = 8 googleButton.customButtonType = .tertiary @@ -60,19 +44,8 @@ class CreateWithSocialViewController: UIViewController { twitterButton.customButtonType = .secondary redditButton.customButtonType = .secondary - discordButton.customButtonType = .secondary - twitchButton.customButtonType = .secondary - lineButton.customButtonType = .secondary - - githubButton.customButtonType = .secondary - learnMoreButton.configuration?.imagePlacement = .trailing learnMoreButton.configuration?.imagePadding = 8 - viewMoreOptionsButton.configuration?.imagePlacement = .trailing - viewMoreOptionsButton.configuration?.imagePadding = 8 - - emailTextField.validator = EmailValidator() - emailTextField.validatorTextFieldDelegate = self // Can't detect certain events from Torus presented modals (e.g. when a user clicks cancel). Adding a second listener to the notification they use so I can trigger a loading modal @@ -123,70 +96,40 @@ class CreateWithSocialViewController: UIViewController { return } - self.showLoadingView() // uses differetn callback structure to rest, need to pop loading here + self.showLoadingView() // uses different callback structure to rest, need to pop loading here DependencyManager.shared.torusAuthService.createWallet(from: .apple, displayOver: self.presentedViewController) { [weak self] result in self?.handleResult(result: result) } } @IBAction func googleTapped(_ sender: Any) { - guard DependencyManager.shared.torusVerifiers[.google] != nil else { - self.windowError(withTitle: "error".localized(), description: "error-missing-verifier".localized()) - return - } - - DependencyManager.shared.torusAuthService.createWallet(from: .google, displayOver: self) { [weak self] result in - self?.handleResult(result: result) - } + createWallet(withVerifier: .google) } @IBAction func facebookTapped(_ sender: Any) { - self.windowError(withTitle: "Not yet supported", description: "This feature is not yet enabled. Please wait for another release") + createWallet(withVerifier: .facebook) } @IBAction func twitterTapped(_ sender: Any) { - self.windowError(withTitle: "Not yet supported", description: "This feature is not yet enabled. Please wait for another release") + createWallet(withVerifier: .twitter) } @IBAction func redditTapped(_ sender: Any) { - self.windowError(withTitle: "Not yet supported", description: "This feature is not yet enabled. Please wait for another release") - } - - @IBAction func discordTapped(_ sender: Any) { - self.windowError(withTitle: "Not yet supported", description: "This feature is not yet enabled. Please wait for another release") - } - - @IBAction func twitchTapped(_ sender: Any) { - self.windowError(withTitle: "Not yet supported", description: "This feature is not yet enabled. Please wait for another release") - } - - @IBAction func lineTapped(_ sender: Any) { - self.windowError(withTitle: "Not yet supported", description: "This feature is not yet enabled. Please wait for another release") - } - - @IBAction func githubTapped(_ sender: Any) { - self.windowError(withTitle: "Not yet supported", description: "This feature is not yet enabled. Please wait for another release") + createWallet(withVerifier: .reddit) } @IBAction func continueWithEmailTapped(_ sender: Any) { - self.windowError(withTitle: "Not yet supported", description: "This feature is not yet enabled. Please wait for another release") + createWallet(withVerifier: .email) } - @IBAction func viewMoreOptionsTapped(_ sender: Any) { - - if socialOptions2.isHidden { - socialOptions2.isHidden = false - socialOptions3.isHidden = false - viewMoreOptionsButton.imageView?.rotate(degrees: 180, duration: 0.3) - - } else { - socialOptions2.isHidden = true - socialOptions3.isHidden = true - viewMoreOptionsButton.imageView?.rotateBack(duration: 0.3) + private func createWallet(withVerifier verifier: TorusAuthProvider) { + guard DependencyManager.shared.torusVerifiers[verifier] != nil else { + self.windowError(withTitle: "error".localized(), description: "error-missing-verifier".localized()) + return } - UIView.animate(withDuration: 0.3) { [weak self] in - self?.view.layoutIfNeeded() + DependencyManager.shared.torusAuthService.createWallet(from: verifier, displayOver: self) { [weak self] result in + self?.handleResult(result: result) } } @@ -241,26 +184,3 @@ extension CreateWithSocialViewController: AutoScrollViewDelegate { self.topSectionContainer.isUserInteractionEnabled = true } } - -extension CreateWithSocialViewController: ValidatorTextFieldDelegate { - - func textFieldDidBeginEditing(_ textField: UITextField) { - - } - - func textFieldDidEndEditing(_ textField: UITextField) { - - } - - func textFieldShouldClear(_ textField: UITextField) -> Bool { - return true - } - - func validated(_ validated: Bool, textfield: ValidatorTextField, forText text: String) { - continueWIthEmailButton.isEnabled = validated - } - - func doneOrReturnTapped(isValid: Bool, textfield: ValidatorTextField, forText text: String?) { - - } -} diff --git a/Kukai Mobile/Modules/Send/Base.lproj/Send.storyboard b/Kukai Mobile/Modules/Send/Base.lproj/Send.storyboard index b63e569a..a5283d46 100644 --- a/Kukai Mobile/Modules/Send/Base.lproj/Send.storyboard +++ b/Kukai Mobile/Modules/Send/Base.lproj/Send.storyboard @@ -3555,7 +3555,7 @@ - + @@ -3740,172 +3740,36 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - + - - - - + + + + + + - + @@ -3916,7 +3780,7 @@ - + @@ -4200,21 +4064,12 @@ - - - - - - - - - @@ -4322,12 +4177,16 @@ + + + + diff --git a/Kukai Mobile/Modules/Send/SendCollectibleAmountViewController.swift b/Kukai Mobile/Modules/Send/SendCollectibleAmountViewController.swift index 48df09a7..52a120cf 100644 --- a/Kukai Mobile/Modules/Send/SendCollectibleAmountViewController.swift +++ b/Kukai Mobile/Modules/Send/SendCollectibleAmountViewController.swift @@ -65,7 +65,7 @@ class SendCollectibleAmountViewController: UIViewController { maxButton.setTitle("Max \(amountDisplay)", for: .normal) } - MediaProxyService.load(url: MediaProxyService.url(fromUri: selectedToken?.displayURI, ofFormat: .small), to: collectibleImage, withCacheType: .temporary, fallback: UIImage()) + MediaProxyService.load(url: MediaProxyService.url(fromUri: selectedToken?.displayURI, ofFormat: MediaProxyService.Format.small.rawFormat()), to: collectibleImage, withCacheType: .temporary, fallback: UIImage()) collectibleName.text = selectedToken?.name ?? "" diff --git a/Kukai Mobile/Modules/Send/SendCollectibleConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendCollectibleConfirmViewController.swift index 33f5651f..e0bd028a 100644 --- a/Kukai Mobile/Modules/Send/SendCollectibleConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendCollectibleConfirmViewController.swift @@ -85,7 +85,7 @@ class SendCollectibleConfirmViewController: SendAbstractConfirmViewController, S self.connectedAppNameLabel.text = session.peer.name if let iconString = session.peer.icons.first, let iconUrl = URL(string: iconString) { - let smallIconURL = MediaProxyService.url(fromUri: iconUrl, ofFormat: .icon) + let smallIconURL = MediaProxyService.url(fromUri: iconUrl, ofFormat: MediaProxyService.Format.icon.rawFormat()) connectedAppURL = smallIconURL } @@ -170,7 +170,7 @@ class SendCollectibleConfirmViewController: SendAbstractConfirmViewController, S } feeValueLabel?.text = "0 tez" - MediaProxyService.load(url: MediaProxyService.url(fromUri: token.displayURI, ofFormat: .small), to: collectibleImage, withCacheType: .temporary, fallback: UIImage()) + MediaProxyService.load(url: MediaProxyService.url(fromUri: token.displayURI, ofFormat: MediaProxyService.Format.small.rawFormat()), to: collectibleImage, withCacheType: .temporary, fallback: UIImage()) } override func viewDidAppear(_ animated: Bool) { @@ -252,7 +252,7 @@ class SendCollectibleConfirmViewController: SendAbstractConfirmViewController, S let destinationAlias = currentSendData.destinationAlias let amount = currentSendData.chosenAmount ?? .zero() - let mediaURL = MediaProxyService.thumbnailURL(forNFT: nft) + let mediaURL = MediaProxyService.smallURL(forNFT: nft) let token = Token.placeholder(fromNFT: nft, amount: amount, thumbnailURL: mediaURL) let currentOps = selectedOperationsAndFees() diff --git a/Kukai Mobile/Modules/Send/SendContractConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendContractConfirmViewController.swift index d1bf30a1..f40fdb38 100644 --- a/Kukai Mobile/Modules/Send/SendContractConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendContractConfirmViewController.swift @@ -93,7 +93,7 @@ class SendContractConfirmViewController: SendAbstractConfirmViewController, Slid self.connectedAppNameLabel.text = session.peer.name if let iconString = session.peer.icons.first, let iconUrl = URL(string: iconString) { - let smallIconURL = MediaProxyService.url(fromUri: iconUrl, ofFormat: .icon) + let smallIconURL = MediaProxyService.url(fromUri: iconUrl, ofFormat: MediaProxyService.Format.icon.rawFormat()) connectedAppURL = smallIconURL } diff --git a/Kukai Mobile/Modules/Send/SendGenericConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendGenericConfirmViewController.swift index f46dcc44..a95f40a7 100644 --- a/Kukai Mobile/Modules/Send/SendGenericConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendGenericConfirmViewController.swift @@ -86,7 +86,7 @@ class SendGenericConfirmViewController: SendAbstractConfirmViewController, Slide self.connectedAppNameLabel.text = session.peer.name if let iconString = session.peer.icons.first, let iconUrl = URL(string: iconString) { - let smallIconURL = MediaProxyService.url(fromUri: iconUrl, ofFormat: .icon) + let smallIconURL = MediaProxyService.url(fromUri: iconUrl, ofFormat: MediaProxyService.Format.icon.rawFormat()) connectedAppURL = smallIconURL } @@ -113,7 +113,6 @@ class SendGenericConfirmViewController: SendAbstractConfirmViewController, Slide // Display JSON - updateAmountDisplay(withValue: currentSendData.chosenAmount ?? .zero()) updateOperationDisplay() @@ -201,33 +200,6 @@ class SendGenericConfirmViewController: SendAbstractConfirmViewController, Slide } } - func updateAmountDisplay(withValue value: TokenAmount) { - guard let token = currentSendData.chosenToken else { - largeDisplayStackView.isHidden = true - smallDisplayIcon.image = UIImage.unknownToken() - smallDisplayAmount.text = "0" - smallDisplayFiat.text = DependencyManager.shared.balanceService.fiatAmountDisplayString(forToken: Token.xtz(), ofAmount: TokenAmount.zero()) - return - } - - let amountText = value.normalisedRepresentation - if amountText.count > Int(UIScreen.main.bounds.width / 4) { - // small display - largeDisplayStackView.isHidden = true - smallDisplayIcon.addTokenIcon(token: token) - smallDisplayAmount.text = amountText + token.symbol - smallDisplayFiat.text = DependencyManager.shared.balanceService.fiatAmountDisplayString(forToken: token, ofAmount: value) - - } else { - // large display - smallDisplayStackView.isHidden = true - largeDisplayIcon.addTokenIcon(token: token) - largeDisplayAmount.text = amountText - largeDisplaySymbol.text = token.symbol - largeDisplayFiat.text = DependencyManager.shared.balanceService.fiatAmountDisplayString(forToken: token, ofAmount: value) - } - } - func updateOperationDisplay() { let ops = selectedOperationsAndFees() @@ -245,6 +217,7 @@ class SendGenericConfirmViewController: SendAbstractConfirmViewController, Slide feeValueLabel.text = fee.normalisedRepresentation + " tez" feeButton.setTitle(feesAndData.type.displayName(), for: .normal) + updateOperationDisplay() } @IBAction func closeTapped(_ sender: Any) { diff --git a/Kukai Mobile/Modules/Send/SendTokenConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendTokenConfirmViewController.swift index 04eb4128..56e81e7d 100644 --- a/Kukai Mobile/Modules/Send/SendTokenConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendTokenConfirmViewController.swift @@ -93,7 +93,7 @@ class SendTokenConfirmViewController: SendAbstractConfirmViewController, SlideBu self.connectedAppNameLabel.text = session.peer.name if let iconString = session.peer.icons.first, let iconUrl = URL(string: iconString) { - let smallIconURL = MediaProxyService.url(fromUri: iconUrl, ofFormat: .icon) + let smallIconURL = MediaProxyService.url(fromUri: iconUrl, ofFormat: MediaProxyService.Format.icon.rawFormat()) connectedAppURL = smallIconURL } diff --git a/Kukai Mobile/Modules/Side Menu/SideMenuResetViewController.swift b/Kukai Mobile/Modules/Side Menu/SideMenuResetViewController.swift index 76ed4a23..73a7ea35 100644 --- a/Kukai Mobile/Modules/Side Menu/SideMenuResetViewController.swift +++ b/Kukai Mobile/Modules/Side Menu/SideMenuResetViewController.swift @@ -7,7 +7,6 @@ import UIKit import KukaiCoreSwift -import Kingfisher class SideMenuResetViewController: UIViewController { @@ -59,9 +58,7 @@ class SideMenuResetViewController: UIViewController { DependencyManager.shared.setDefaultMainnetURLs(supressUpdateNotification: true) - MediaProxyService.imageCache(forType: .temporary).clearCache { - MediaProxyService.imageCache(forType: .permanent).clearCache(completion: completion) - } + MediaProxyService.removeAllImages(completion: completion) } @IBAction func noButtonTapped(_ sender: Any) { diff --git a/Kukai Mobile/Modules/Side Menu/WalletConnectViewModel.swift b/Kukai Mobile/Modules/Side Menu/WalletConnectViewModel.swift index 2fec91d9..42453382 100644 --- a/Kukai Mobile/Modules/Side Menu/WalletConnectViewModel.swift +++ b/Kukai Mobile/Modules/Side Menu/WalletConnectViewModel.swift @@ -36,7 +36,7 @@ class WalletConnectViewModel: ViewModel, UITableViewDiffableDataSourceHandler { return tableView.dequeueReusableCell(withIdentifier: "empty", for: indexPath) } else if let obj = item as? PairObj, let cell = tableView.dequeueReusableCell(withIdentifier: "ConnectedApp", for: indexPath) as? ConnectedAppCell { - let iconURL = MediaProxyService.url(fromUri: obj.icon, ofFormat: .icon) + let iconURL = MediaProxyService.url(fromUri: obj.icon, ofFormat: MediaProxyService.Format.icon.rawFormat()) MediaProxyService.load(url: iconURL, to: cell.iconView, withCacheType: .temporary, fallback: UIImage.unknownToken()) cell.siteLabel.text = obj.site cell.networkLabel.text = obj.network diff --git a/Kukai Mobile/Services/BalanceService.swift b/Kukai Mobile/Services/BalanceService.swift index 114b8dc6..17a1a70d 100644 --- a/Kukai Mobile/Services/BalanceService.swift +++ b/Kukai Mobile/Services/BalanceService.swift @@ -540,7 +540,7 @@ public class BalanceService { if let address = token.tokenContractAddress, let objktData = DependencyManager.shared.objktClient.collections[address] { var url: URL? = nil if let logo = objktData.logo { - url = MediaProxyService.url(fromUri: URL(string: logo), ofFormat: .icon) + url = MediaProxyService.url(fromUri: URL(string: logo), ofFormat: MediaProxyService.Format.icon.rawFormat()) } let token = Token(name: objktData.name, symbol: "", tokenType: .nonfungible, faVersion: .fa2, balance: token.balance, thumbnailURL: url, tokenContractAddress: address, tokenId: token.tokenId, nfts: token.nfts, mintingTool: token.mintingTool) diff --git a/Kukai Mobile/Services/CloudKitService.swift b/Kukai Mobile/Services/CloudKitService.swift index 98fc4f2f..41b3d901 100644 --- a/Kukai Mobile/Services/CloudKitService.swift +++ b/Kukai Mobile/Services/CloudKitService.swift @@ -45,53 +45,66 @@ public class CloudKitService { for record in configItemRecords where record.stringForKey("serviceId") == "torus" { - guard let networkStr = record.stringForKey("network"), - let network = TezosNodeClientConfig.NetworkType(rawValue: networkStr), - let config = record.doubleStringArrayToDict(key1: "keys", key2: "values"), - let provider = config["loginProvider"], - let type = config["loginType"], - let authProvider = TorusAuthProvider(rawValue: provider) else { - Logger.app.error("Skipping invalid torus config item: \(record.description)") - continue - } + // record.stringForKey returns nil, haven't got the slightest clue why. Can only get the `json` key value by fetching all values and parsing + var jsonString = (record.value(forKey: "values") as? [Any])?[0] as? String + jsonString = jsonString?.replacingOccurrences(of: "\n", with: "") + jsonString = jsonString?.replacingOccurrences(of: "\t", with: "") - // Option 1: - // Required: loginType, loginProvider, verifierName, redirectURL - // Optional: aggregateVerifierName, clientId - if let loginType = SubVerifierType(rawValue: type), let loginProvider = LoginProviders(rawValue: provider), let verifierName = config["verifierName"], let redirectURL = config["redirectURL"] { - let details = SubVerifierDetails(loginType: loginType, loginProvider: loginProvider, clientId: config["clientId"] ?? "", verifier: verifierName, redirectURL: redirectURL) - let wrapper = SubverifierWrapper(aggregateVerifierName: config["aggregateVerifierName"], networkType: network, subverifier: details) - verifiers[authProvider] = wrapper - + guard let jsonData = jsonString?.data(using: .utf8), + let jsonObject = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any], + let verifierTypeString = jsonObject["verifierType"] as? String, + let verifierType = verifierTypes(rawValue: verifierTypeString), + let networkString = record.stringForKey("network"), + let network = TezosNodeClientConfig.NetworkType(rawValue: networkString), + let loginProviderString = jsonObject["loginProvider"] as? String, + let loginProvider = LoginProviders(rawValue: loginProviderString), + let authProviderString = jsonObject["authProvider"] as? String, + let authProvider = TorusAuthProvider(rawValue: authProviderString), + let loginTypeString = jsonObject["loginType"] as? String, + let loginType = SubVerifierType(rawValue: loginTypeString), + let clientId = jsonObject["clientId"] as? String, + let verifierName = jsonObject["verifierName"] as? String, + let redirectURL = jsonObject["redirectURL"] as? String + else { + Logger.app.error("Skipping invalid torus config item: \(record.description)") + continue + } + + var subVerifier: SubVerifierDetails? = nil + var wrapper: SubverifierWrapper? = nil + + + // Check if we have a jwt dictionary, to decide which subVerfier to create + if let jwtDict = jsonObject["jwt"] as? [String: String] { + subVerifier = SubVerifierDetails(loginType: loginType, + loginProvider: loginProvider, + clientId: clientId, + verifier: verifierName, + redirectURL: redirectURL, + browserRedirectURL: jsonObject["browserRedirectURL"] as? String, // Think this is unused, just adding in case its needed in the future + jwtParams: jwtDict) + } else { + subVerifier = SubVerifierDetails(loginType: loginType, + loginProvider: loginProvider, + clientId: clientId, + verifier: verifierName, + redirectURL: redirectURL) } - // Option 2: - // Required: loginType, loginProvider, verifierName, redirectURL - // Optional: aggregateVerifierName, clientId, browserRedirectURL, jwtDomain - else if let loginType = SubVerifierType(rawValue: type), let loginProvider = LoginProviders(rawValue: provider), let verifierName = config["verifierName"], let redirectURL = config["redirectURL"] { + + // Check what type of login is being requested, so we can decide what to do with the required aggregate param, that might not be needed + if let sub = subVerifier, verifierType == .singleLogin { + wrapper = SubverifierWrapper(aggregateVerifierName: verifierName, verifierType: verifierType, networkType: network, subverifier: sub) // Non-aggregate - if let jwtDomain = config["jwtDomain"] { - let details = SubVerifierDetails(loginType: loginType, - loginProvider: loginProvider, - clientId: config["clientId"] ?? "", - verifier: verifierName, - redirectURL: redirectURL, - browserRedirectURL: config["browserRedirectURL"], - jwtParams: ["domain": jwtDomain]) - let wrapper = SubverifierWrapper(aggregateVerifierName: config["aggregateVerifierName"], networkType: network, subverifier: details) - verifiers[authProvider] = wrapper - - } else { - let details = SubVerifierDetails(loginType: loginType, - loginProvider: loginProvider, - clientId: config["clientId"] ?? "", - verifier: verifierName, - redirectURL: redirectURL, - browserRedirectURL: config["browserRedirectURL"]) - let wrapper = SubverifierWrapper(aggregateVerifierName: config["aggregateVerifierName"], networkType: network, subverifier: details) - verifiers[authProvider] = wrapper - } + } else if let sub = subVerifier, let aggregateVerfifier = jsonObject["aggregateVerifierName"] as? String { + wrapper = SubverifierWrapper(aggregateVerifierName: aggregateVerfifier, verifierType: verifierType, networkType: network, subverifier: sub) // Aggregate + + } else { + Logger.app.error("Skipping invalid torus config item: \(record.description)") + continue } + + verifiers[authProvider] = wrapper } return verifiers diff --git a/Kukai Mobile/Services/TokenStateService.swift b/Kukai Mobile/Services/TokenStateService.swift index ff727687..62e8d4f7 100644 --- a/Kukai Mobile/Services/TokenStateService.swift +++ b/Kukai Mobile/Services/TokenStateService.swift @@ -137,11 +137,19 @@ public class TokenStateService { } public func removeFavourite(forAddress address: String, token: Token) -> Bool { - favouriteBalances[address]?.removeValue(forKey: balanceId(from: token)) + let balanceId = balanceId(from: token) + let currentSortIndex = favouriteBalances[address]?[balanceId] ?? -1 + reduceSortIndex(afterIndex: currentSortIndex, inDict: &favouriteBalances, withAddress: address) + + favouriteBalances[address]?.removeValue(forKey: balanceId) return writeFavouriteBalances() } public func removeFavourite(forAddress address: String, nft: NFT) -> Bool { + let balanceId = nftId(from: nft) + let currentSortIndex = favouriteCollectibles[address]?[balanceId] ?? -1 + reduceSortIndex(afterIndex: currentSortIndex, inDict: &favouriteCollectibles, withAddress: address) + favouriteCollectibles[address]?.removeValue(forKey: nftId(from: nft)) return writeFavouriteCollectibles() } @@ -151,32 +159,35 @@ public class TokenStateService { // MARK: Reorder public func moveFavouriteBalance(forAddress address: String, forToken token: Token, toIndex: Int) -> Bool { - var account = favouriteBalances[address] ?? [:] - let tokenId = balanceId(from: token) - account[tokenId] = toIndex + let balanceId = balanceId(from: token) + let currentSortIndex = favouriteBalances[address]?[balanceId] ?? -1 - for key in account.keys { - if key != tokenId, (account[key] ?? 1) >= toIndex { - account[key] = ((account[key] ?? 1) + 1) - } + if currentSortIndex < toIndex { + // moving down, decrease others + reduceSortIndex(afterIndex: currentSortIndex, inDict: &favouriteBalances, withAddress: address) + } else { + // moving up, increase others + increaseSortIndex(beforeIndex: currentSortIndex, inDict: &favouriteBalances, withAddress: address) } - favouriteBalances[address] = account + favouriteBalances[address]?[balanceId] = toIndex return writeFavouriteBalances() } public func moveFavouriteCollectible(forAddress address: String, forNft nft: NFT, toIndex: Int) -> Bool { - var account = favouriteCollectibles[address] ?? [:] - account[nftId(from: nft)] = toIndex + let balanceId = nftId(from: nft) + let currentSortIndex = favouriteCollectibles[address]?[balanceId] ?? -1 - for key in account.keys { - if (account[key] ?? 1) >= toIndex { - account[key] = ((account[key] ?? 1) + 1) - } + if currentSortIndex < toIndex { + // moving down, decrease others + reduceSortIndex(afterIndex: currentSortIndex, inDict: &favouriteCollectibles, withAddress: address) + } else { + // moving up, increase others + increaseSortIndex(beforeIndex: currentSortIndex, inDict: &favouriteCollectibles, withAddress: address) } - favouriteCollectibles[address] = account - return writeHiddenCollectibles() + favouriteCollectibles[address]?[balanceId] = toIndex + return writeFavouriteCollectibles() } @@ -205,6 +216,30 @@ public class TokenStateService { return DiskService.write(encodable: favouriteCollectibles, toFileName: TokenStateService.favouriteCollectiblesFilename) } + private func reduceSortIndex(afterIndex: Int, inDict dict: inout [String: [String: Int]], withAddress address: String) { + var tempDict: [String: Int] = (dict[address] ?? [:]) + + for key in tempDict.keys { + if let val = tempDict[key], val > afterIndex { + tempDict[key] = val-1 + } + } + + dict[address] = tempDict + } + + private func increaseSortIndex(beforeIndex: Int, inDict dict: inout [String: [String: Int]], withAddress address: String) { + var tempDict: [String: Int] = (dict[address] ?? [:]) + + for key in tempDict.keys { + if let val = tempDict[key], val < beforeIndex { + tempDict[key] = val+1 + } + } + + dict[address] = tempDict + } + // MARK: Delete diff --git a/Kukai Mobile/Services/TransactionService.swift b/Kukai Mobile/Services/TransactionService.swift index e7f8e24c..a16cb49b 100644 --- a/Kukai Mobile/Services/TransactionService.swift +++ b/Kukai Mobile/Services/TransactionService.swift @@ -367,11 +367,11 @@ public class TransactionService { case .twitter: let image = UIImage(named: "Social_Twitter_color")?.resizedImage(size: imageSize) ?? UIImage() - return (image: image, title: metadata.socialUsername ?? "", subtitle: metadata.address.truncateTezosAddress()) + return (image: image, title: metadata.socialUsername ?? metadata.socialUserId ?? "", subtitle: metadata.address.truncateTezosAddress()) case .google: let image = UIImage(named: "Social_Google_color")?.resizedImage(size: imageSize) ?? UIImage() - return (image: image, title: metadata.socialUsername ?? "", subtitle: metadata.address.truncateTezosAddress()) + return (image: image, title: metadata.socialUserId ?? metadata.socialUsername ?? "", subtitle: metadata.address.truncateTezosAddress()) case .reddit: let image = UIImage(named: "Social_Reddit_Color")?.resizedImage(size: imageSize) ?? UIImage() @@ -394,7 +394,11 @@ public class TransactionService { return (image: image, title: metadata.socialUsername ?? "", subtitle: metadata.address.truncateTezosAddress()) case .github: - let image = UIImage(named: "Social_Github_color")?.resizedImage(size: imageSize) ?? UIImage() + let image = UIImage(named: "Social_Github_Color")?.resizedImage(size: imageSize) ?? UIImage() + return (image: image, title: metadata.socialUsername ?? "", subtitle: metadata.address.truncateTezosAddress()) + + case .email: + let image = UIImage(named: "Social_Email_Outlined")?.resizedImage(size: imageSize) ?? UIImage() return (image: image, title: metadata.socialUsername ?? "", subtitle: metadata.address.truncateTezosAddress()) case .none: