Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

feature: displaying a warning before opening a block explorer link #326

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Library/Coordinators/WalletCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ final class WalletCoordinator: NSObject, Coordinator {
guard let network = lightningService.infoService.network.value else { return }
do {
guard let url = try Settings.shared.blockExplorer.value.url(network: network, code: code, type: type) else { return }
presentSafariViewController(for: url)
UserDefaults.Keys.didSelectContinueToBlockExplorer.get(defaultValue: false) ? presentSafariViewController(for: url) : presentBlockExplorerWarning(for: url)
} catch BlockExplorerError.unsupportedNetwork {
Toast.presentError(L10n.Error.BlockExplorer.unsupportedNetwork(Settings.shared.blockExplorer.value.localized, network.localized))
} catch {
Expand Down Expand Up @@ -302,6 +302,14 @@ final class WalletCoordinator: NSObject, Coordinator {
safariViewController.preferredControlTintColor = UIColor.Zap.lightningOrange
(detailViewController ?? rootViewController).present(safariViewController, animated: true)
}

private func presentBlockExplorerWarning(for url: URL) {
let alertController = UIAlertController.blockExplorerWarningAlertController { [weak self] in
guard let self = self else { return }
self.presentSafariViewController(for: url)
}
(detailViewController ?? rootViewController).present(alertController, animated: true)
}

private func dismissDetailViewController() {
detailViewController?.dismiss(animated: true)
Expand Down
20 changes: 20 additions & 0 deletions Library/Extensions/UIAlertController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,24 @@ extension UIAlertController {

return alertController
}

static func blockExplorerWarningAlertController(continueAction: @escaping () -> Void) -> UIAlertController {
let title = L10n.Scene.BlockExplorer.title
let message = L10n.Scene.BlockExplorer.message
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let cancelAlertAction = UIAlertAction(title: L10n.Generic.cancel, style: .cancel, handler: nil)

let continueAlertAction = UIAlertAction(title: L10n.Generic.continueString, style: .default) { _ in
continueAction()
}
let alwaysContinueAlertAction = UIAlertAction(title: L10n.Scene.BlockExplorer.alwaysContinue, style: .default) { _ in
UserDefaults.Keys.didSelectContinueToBlockExplorer.set(true)
continueAction()
}
alertController.addAction(cancelAlertAction)
alertController.addAction(continueAlertAction)
alertController.addAction(alwaysContinueAlertAction)

return alertController
}
}
1 change: 1 addition & 0 deletions Library/Extensions/UserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Foundation
extension UserDefaults {
enum Keys {
static let didCreateWallet = DefaultKey<Bool>("didCreateWallet")
static let didSelectContinueToBlockExplorer = DefaultKey<Bool>("didSelectContinueToBlockExplorer")
static let lastSeenHistoryDate = DefaultKey<Date>("lastSeenHistoryDate")
static let walletDetailExpanded = DefaultKey<Bool>("homeScreenTileExpanded")
static let migrations = DefaultKey<[String]>("migrations")
Expand Down
10 changes: 10 additions & 0 deletions Library/Generated/strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ internal enum L10n {
internal enum Generic {
/// Cancel
internal static let cancel = L10n.tr("Localizable", "generic.cancel")
/// Continue
internal static let continueString = L10n.tr("Localizable", "generic.continue")
internal enum Memo {
/// What is this for?
internal static let placeholder = L10n.tr("Localizable", "generic.memo.placeholder")
Expand Down Expand Up @@ -168,6 +170,14 @@ internal enum L10n {
}

internal enum Scene {
internal enum BlockExplorer {
/// Always Continue
internal static let alwaysContinue = L10n.tr("Localizable", "scene.block_explorer.always_continue")
/// Warning: Privacy Leak
internal static let title = L10n.tr("Localizable", "scene.block_explorer.title")
/// Looking up your own addresses on a block explorer leaks your privacy to the site operator.
internal static let message = L10n.tr("Localizable", "scene.block_explorer.message")
}
internal enum ChannelBackup {
/// %@ channel.backup
internal static func cellTitle(_ p1: String) -> String {
Expand Down
19 changes: 9 additions & 10 deletions Library/Scenes/History/Detail/EventDetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@ final class EventDetailViewController: ModalDetailViewController {

init(event: HistoryEventType, presentBlockExplorer: @escaping (String, BlockExplorer.CodeType) -> Void) {
viewModel = EventDetailViewModel(event: event)

self.presentBlockExplorer = presentBlockExplorer

super.init(nibName: nil, bundle: nil)

viewModel.blockExplorerButtonTapped.observeNext { [weak self] blockExplorerItem in
guard let self = self else { return }
self.dismiss(animated: true) {
self.presentBlockExplorer(blockExplorerItem.code, blockExplorerItem.type)
}
}.dispose(in: reactive.bag)
}

required init?(coder aDecoder: NSCoder) {
Expand All @@ -31,15 +38,7 @@ final class EventDetailViewController: ModalDetailViewController {
}

private func setupEvent() {
let configuration = viewModel.detailConfiguration(delegate: self)
let configuration = viewModel.detailConfiguration()
contentStackView.set(elements: configuration)
}
}

extension EventDetailViewController: EventDetailViewModelDelegate {
func openBlockExplorer(code: String, type: BlockExplorer.CodeType) {
dismiss(animated: true) {
self.presentBlockExplorer(code, type)
}
}
}
23 changes: 14 additions & 9 deletions Library/Scenes/History/Detail/EventDetailViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@

import Foundation
import Lightning
import ReactiveKit
import SwiftBTC

protocol EventDetailViewModelDelegate: class {
func openBlockExplorer(code: String, type: BlockExplorer.CodeType)
struct BlockExplorerItem {
let code: String
let type: BlockExplorer.CodeType
}

final class EventDetailViewModel {
private let event: HistoryEventType

let blockExplorerButtonTapped = Subject<BlockExplorerItem, NoError>()

init(event: HistoryEventType) {
self.event = event
}

func detailConfiguration(delegate: EventDetailViewModelDelegate) -> [StackViewElement] {
func detailConfiguration() -> [StackViewElement] {
var result = [StackViewElement]()

switch event {
Expand All @@ -29,13 +33,13 @@ final class EventDetailViewModel {
result.append(contentsOf: amountLabel(title: L10n.Scene.TransactionDetail.amountLabel, amount: event.amount))
result.append(contentsOf: amountLabel(title: L10n.Scene.TransactionDetail.feeLabel, amount: event.fee, skipZero: true))
result.append(contentsOf: dateLabel(title: L10n.Scene.TransactionDetail.dateLabel, date: event.date))
result.append(contentsOf: blockExplorerButton(title: L10n.Scene.TransactionDetail.transactionIdLabel, code: event.txHash, type: .transactionId, delegate: delegate))
result.append(contentsOf: blockExplorerButton(title: L10n.Scene.TransactionDetail.addressLabel, code: event.destinationAddresses.first?.string, type: .address, delegate: delegate))
result.append(contentsOf: blockExplorerButton(title: L10n.Scene.TransactionDetail.transactionIdLabel, code: event.txHash, type: .transactionId))
result.append(contentsOf: blockExplorerButton(title: L10n.Scene.TransactionDetail.addressLabel, code: event.destinationAddresses.first?.string, type: .address))
case .channelEvent(let event):
result.append(contentsOf: headline(L10n.Scene.TransactionDetail.Title.channelEventDetail))
result.append(contentsOf: amountLabel(title: L10n.Scene.TransactionDetail.feeLabel, amount: event.fee, skipZero: true))
result.append(contentsOf: dateLabel(title: L10n.Scene.TransactionDetail.dateLabel, date: event.date))
result.append(contentsOf: blockExplorerButton(title: L10n.Scene.TransactionDetail.transactionIdLabel, code: event.txHash, type: .transactionId, delegate: delegate))
result.append(contentsOf: blockExplorerButton(title: L10n.Scene.TransactionDetail.transactionIdLabel, code: event.txHash, type: .transactionId))
case .createInvoiceEvent(let event):
return createInvoiceEvent(event: event)
case .lightningPaymentEvent(let event):
Expand Down Expand Up @@ -155,10 +159,11 @@ final class EventDetailViewModel {
return label(title: title, content: dateString)
}

private func blockExplorerButton(title: String, code: String?, type: BlockExplorer.CodeType, delegate: EventDetailViewModelDelegate?) -> [StackViewElement] {
private func blockExplorerButton(title: String, code: String?, type: BlockExplorer.CodeType) -> [StackViewElement] {
guard let code = code else { return [] }
return label(title: title, element: .button(title: code, style: Style.Button.custom(), completion: { _ in
delegate?.openBlockExplorer(code: code, type: type)
return label(title: title, element: .button(title: code, style: Style.Button.custom(), completion: { [weak self] _ in
guard let self = self else { return }
self.blockExplorerButtonTapped.send(BlockExplorerItem(code: code, type: type))
}))
}

Expand Down
8 changes: 2 additions & 6 deletions Library/Tests/EventDetailViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ import XCTest
// swiftlint:disable force_unwrapping
final class EventDetailViewModelTests: XCTestCase {

private final class MockDelegate: EventDetailViewModelDelegate {
func openBlockExplorer(code: String, type: BlockExplorer.CodeType) {}
}

func testTransactionEventZeroFees() {
let transaction = Transaction(
id: "100",
Expand All @@ -30,7 +26,7 @@ final class EventDetailViewModelTests: XCTestCase {
let event = TransactionEvent(transaction: transaction)!
let eventType = HistoryEventType.transactionEvent(event)
let viewModel = EventDetailViewModel(event: eventType)
let configuration = viewModel.detailConfiguration(delegate: MockDelegate())
let configuration = viewModel.detailConfiguration()

XCTAssertEqual(configuration.count, 5)
}
Expand All @@ -48,7 +44,7 @@ final class EventDetailViewModelTests: XCTestCase {
let event = TransactionEvent(transaction: transaction)!
let eventType = HistoryEventType.transactionEvent(event)
let viewModel = EventDetailViewModel(event: eventType)
let configuration = viewModel.detailConfiguration(delegate: MockDelegate())
let configuration = viewModel.detailConfiguration()

XCTAssertEqual(configuration.count, 6)
if
Expand Down
5 changes: 5 additions & 0 deletions Library/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"generic.pasteboard.invalid_address" = "Invalid Address";
"generic.qr_code.share_button" = "Share";
"generic.cancel" = "Cancel";
"generic.continue" = "Continue";

"link.help" = "https://docs.zaphq.io/";
"link.help.zapconnect" = "https://docs.zaphq.io/docs-ios-remote-node-setup";
Expand Down Expand Up @@ -309,6 +310,10 @@
"scene.manage_wallets.section_title.local" = "Local Wallet";
"scene.manage_wallets.section_title.remote" = "Remote Wallets";

"scene.block_explorer.always_continue" = "Always Continue";
"scene.block_explorer.title" = "Warning: Privacy Leak";
"scene.block_explorer.message" = "Looking up your own addresses on a block explorer leaks your privacy to the site operator.\n\nWould you like to view the block explorer?";

"scene.channel_backup.title" = "Channel Backup";
"scene.channel_backup.cell_title" = "%@ channel.backup";
"scene.channel_backup.not_found" = "file not found";
Expand Down