diff --git a/AlphaWallet/Assets.xcassets/token-artis.imageset/Contents.json b/AlphaWallet/Assets.xcassets/token-artis.imageset/Contents.json new file mode 100644 index 000000000..9037d5fe0 --- /dev/null +++ b/AlphaWallet/Assets.xcassets/token-artis.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "token-artis.pdf", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AlphaWallet/Assets.xcassets/token-artis.imageset/token-artis.pdf b/AlphaWallet/Assets.xcassets/token-artis.imageset/token-artis.pdf new file mode 100644 index 000000000..4d1f73f12 Binary files /dev/null and b/AlphaWallet/Assets.xcassets/token-artis.imageset/token-artis.pdf differ diff --git a/AlphaWallet/Assets.xcassets/token-callisto.imageset/Contents.json b/AlphaWallet/Assets.xcassets/token-callisto.imageset/Contents.json new file mode 100644 index 000000000..a306db89b --- /dev/null +++ b/AlphaWallet/Assets.xcassets/token-callisto.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "token-callisto.pdf", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AlphaWallet/Assets.xcassets/token-callisto.imageset/token-callisto.pdf b/AlphaWallet/Assets.xcassets/token-callisto.imageset/token-callisto.pdf new file mode 100644 index 000000000..9edcbcb09 Binary files /dev/null and b/AlphaWallet/Assets.xcassets/token-callisto.imageset/token-callisto.pdf differ diff --git a/AlphaWallet/Assets.xcassets/token-etc.imageset/Contents.json b/AlphaWallet/Assets.xcassets/token-etc.imageset/Contents.json new file mode 100644 index 000000000..efd2aace9 --- /dev/null +++ b/AlphaWallet/Assets.xcassets/token-etc.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "token-etc.pdf", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AlphaWallet/Assets.xcassets/token-etc.imageset/token-etc.pdf b/AlphaWallet/Assets.xcassets/token-etc.imageset/token-etc.pdf new file mode 100644 index 000000000..c3c415e62 Binary files /dev/null and b/AlphaWallet/Assets.xcassets/token-etc.imageset/token-etc.pdf differ diff --git a/AlphaWallet/Assets.xcassets/token-poa.imageset/Contents.json b/AlphaWallet/Assets.xcassets/token-poa.imageset/Contents.json new file mode 100644 index 000000000..05011ba6c --- /dev/null +++ b/AlphaWallet/Assets.xcassets/token-poa.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "token-poa.pdf", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AlphaWallet/Assets.xcassets/token-poa.imageset/token-poa.pdf b/AlphaWallet/Assets.xcassets/token-poa.imageset/token-poa.pdf new file mode 100644 index 000000000..fefd8c30e Binary files /dev/null and b/AlphaWallet/Assets.xcassets/token-poa.imageset/token-poa.pdf differ diff --git a/AlphaWallet/Extensions/UIView.swift b/AlphaWallet/Extensions/UIView.swift index 0494a1f67..023e5c12e 100644 --- a/AlphaWallet/Extensions/UIView.swift +++ b/AlphaWallet/Extensions/UIView.swift @@ -4,7 +4,23 @@ import Foundation import UIKit extension UIView { - + static var tokenSymbolBackgroundImageCache: [UIColor: UIImage] = .init() + static func tokenSymbolBackgroundImage(backgroundColor: UIColor) -> UIImage { + if let cachedValue = tokenSymbolBackgroundImageCache[backgroundColor] { + return cachedValue + } + let size = CGSize(width: 40, height: 40) + let rect = CGRect(origin: .zero, size: size) + let renderer = UIGraphicsImageRenderer(size: size) + let image = renderer.image { ctx in + ctx.cgContext.setFillColor(backgroundColor.cgColor) + ctx.cgContext.addEllipse(in: rect) + ctx.cgContext.drawPath(using: .fill) + } + tokenSymbolBackgroundImageCache[backgroundColor] = image + return image + } + func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize = .zero, radius: CGFloat = 1, scale: Bool = true, shouldRasterize: Bool = true) { layer.masksToBounds = false layer.shadowColor = color.cgColor @@ -16,7 +32,7 @@ extension UIView { layer.shouldRasterize = shouldRasterize layer.rasterizationScale = scale ? UIScreen.main.scale : 1 } - + func anchorsConstraint(to view: UIView, edgeInsets: UIEdgeInsets = .zero) -> [NSLayoutConstraint] { return [ leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: edgeInsets.left), diff --git a/AlphaWallet/Settings/Types/RPCServers.swift b/AlphaWallet/Settings/Types/RPCServers.swift index ee8b6b867..48b9f211a 100644 --- a/AlphaWallet/Settings/Types/RPCServers.swift +++ b/AlphaWallet/Settings/Types/RPCServers.swift @@ -342,6 +342,25 @@ enum RPCServer: Hashable, CaseIterable { } } + var iconImage: UIImage? { + switch self { + case .main: + return R.image.eth()! + case .xDai: + return R.image.xDai()! + case .poa: + return R.image.tokenPoa()! + case .classic: + return R.image.tokenEtc()! + case .callisto: + return R.image.tokenCallisto()! + case .artis_sigma1: + return R.image.tokenArtis()! + case .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_tau1, .custom: + return nil + } + } + init(name: String) { self = { switch name { diff --git a/AlphaWallet/Tokens/Types/TokenObject.swift b/AlphaWallet/Tokens/Types/TokenObject.swift index 4b67e869a..a7a323de1 100644 --- a/AlphaWallet/Tokens/Types/TokenObject.swift +++ b/AlphaWallet/Tokens/Types/TokenObject.swift @@ -122,6 +122,31 @@ class TokenObject: Object { var server: RPCServer { return .init(chainID: chainId) } + + var iconImage: UIImage? { + switch type { + case .nativeCryptocurrency: + return server.iconImage + case .erc20, .erc875, .erc721, .erc721ForTickets: + return nil + } + } + + var symbolBackgroundColor: UIColor { + if contractAddress.sameContract(as: Constants.nativeCryptoAddressInDatabase) { + return server.blockChainNameColor + } else { + let colors = [R.color.radical()!, R.color.cerulean()!, R.color.emerald()!, R.color.indigo()!, R.color.azure()!, R.color.pumpkin()!] + let index: Int + //We just need a random number from the contract. The LSBs are more random than the MSBs + if let i = Int(contractAddress.eip55String.substring(from: 37), radix: 16) { + index = i % colors.count + } else { + index = 0 + } + return colors[index] + } + } } func isNonZeroBalance(_ balance: String) -> Bool { diff --git a/AlphaWallet/Tokens/ViewModels/EthTokenViewCellViewModel.swift b/AlphaWallet/Tokens/ViewModels/EthTokenViewCellViewModel.swift index d1cb2860c..38607f16f 100644 --- a/AlphaWallet/Tokens/ViewModels/EthTokenViewCellViewModel.swift +++ b/AlphaWallet/Tokens/ViewModels/EthTokenViewCellViewModel.swift @@ -5,6 +5,8 @@ import UIKit import BigInt struct EthTokenViewCellViewModel { + static let numberOfCharactersOfSymbolToShow = 4 + private let shortFormatter = EtherNumberFormatter.short private let token: TokenObject private let currencyAmount: String? @@ -37,7 +39,7 @@ struct EthTokenViewCellViewModel { var amount: String { return shortFormatter.string(from: BigInt(token.value) ?? BigInt(), decimals: token.decimals) - } + } var backgroundColor: UIColor { return Screen.TokenCard.Color.background @@ -97,14 +99,14 @@ struct EthTokenViewCellViewModel { return "-" } } - + var valueChange: String? { return EthCurrencyHelper(ticker: ticker).valueChanged24h(currencyAmountWithoutSymbol: currencyAmountWithoutSymbol) } var value: String? { return currencyAmount - } + } var blockChainLabelHidden: Bool { return currencyAmount != nil @@ -113,4 +115,26 @@ struct EthTokenViewCellViewModel { var alpha: CGFloat { return isVisible ? 1.0 : 0.4 } + + var iconImage: UIImage? { + if let image = token.iconImage { + return image + } else { + return UIView.tokenSymbolBackgroundImage(backgroundColor: token.symbolBackgroundColor) + } + } + + var symbolInIcon: String { + guard token.iconImage == nil else { return "" } + let i = [EthTokenViewCellViewModel.numberOfCharactersOfSymbolToShow, token.symbol.count].min()! + return token.symbol.substring(to: i) + } + + var symbolColor: UIColor { + Colors.appWhite + } + + var symbolFont: UIFont { + UIFont.systemFont(ofSize: 13) + } } diff --git a/AlphaWallet/Tokens/ViewModels/FungibleTokenViewCellViewModel.swift b/AlphaWallet/Tokens/ViewModels/FungibleTokenViewCellViewModel.swift index 38ad3fc10..d6fc9be81 100644 --- a/AlphaWallet/Tokens/ViewModels/FungibleTokenViewCellViewModel.swift +++ b/AlphaWallet/Tokens/ViewModels/FungibleTokenViewCellViewModel.swift @@ -36,7 +36,7 @@ struct FungibleTokenViewCellViewModel { var contentsBackgroundColor: UIColor { return Screen.TokenCard.Color.background - } + } var titleColor: UIColor { return Screen.TokenCard.Color.title @@ -57,4 +57,26 @@ struct FungibleTokenViewCellViewModel { var alpha: CGFloat { return isVisible ? 1.0 : 0.4 } + + var iconImage: UIImage? { + if let image = token.iconImage { + return image + } else { + return UIView.tokenSymbolBackgroundImage(backgroundColor: token.symbolBackgroundColor) + } + } + + var symbolInIcon: String { + guard token.iconImage == nil else { return "" } + let i = [EthTokenViewCellViewModel.numberOfCharactersOfSymbolToShow, token.symbol.count].min()! + return token.symbol.substring(to: i) + } + + var symbolColor: UIColor { + Colors.appWhite + } + + var symbolFont: UIFont { + UIFont.systemFont(ofSize: 13) + } } diff --git a/AlphaWallet/Tokens/ViewModels/NonFungibleTokenViewCellViewModel.swift b/AlphaWallet/Tokens/ViewModels/NonFungibleTokenViewCellViewModel.swift index 6048de206..2b59a8556 100644 --- a/AlphaWallet/Tokens/ViewModels/NonFungibleTokenViewCellViewModel.swift +++ b/AlphaWallet/Tokens/ViewModels/NonFungibleTokenViewCellViewModel.swift @@ -85,4 +85,26 @@ struct NonFungibleTokenViewCellViewModel { var alpha: CGFloat { return isVisible ? 1.0 : 0.4 } + + var iconImage: UIImage? { + if let image = token.iconImage { + return image + } else { + return UIView.tokenSymbolBackgroundImage(backgroundColor: token.symbolBackgroundColor) + } + } + + var symbolInIcon: String { + guard token.iconImage == nil else { return "" } + let i = [EthTokenViewCellViewModel.numberOfCharactersOfSymbolToShow, token.symbol.count].min()! + return token.symbol.substring(to: i) + } + + var symbolColor: UIColor { + Colors.appWhite + } + + var symbolFont: UIFont { + UIFont.systemFont(ofSize: 13) + } } diff --git a/AlphaWallet/Tokens/Views/EthTokenViewCell.swift b/AlphaWallet/Tokens/Views/EthTokenViewCell.swift index 43f861f01..eea061382 100644 --- a/AlphaWallet/Tokens/Views/EthTokenViewCell.swift +++ b/AlphaWallet/Tokens/Views/EthTokenViewCell.swift @@ -9,6 +9,7 @@ class EthTokenViewCell: UITableViewCell { private let background = UIView() private let titleLabel = UILabel() + private let symbolLabel = UILabel() private let valuePercentageChangeValueLabel = UILabel() private let valuePercentageChangePeriodLabel = UILabel() private let valueChangeLabel = UILabel() @@ -17,6 +18,12 @@ class EthTokenViewCell: UITableViewCell { private var viewsWithContent: [UIView] { [titleLabel, valuePercentageChangeValueLabel, valuePercentageChangePeriodLabel, valueChangeLabel] } + private var tokenIconImageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFit + imageView.translatesAutoresizingMaskIntoConstraints = false + return imageView + }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) @@ -28,14 +35,23 @@ class EthTokenViewCell: UITableViewCell { valueChangeLabel.textAlignment = .center valueLabel.textAlignment = .center - let stackView = [ + let col0 = tokenIconImageView + let col1 = [ titleLabel, [blockchainLabel, valueLabel, UIView.spacerWidth(flexible: true), valueChangeLabel, valuePercentageChangeValueLabel].asStackView(spacing: 5) ].asStackView(axis: .vertical) + let stackView = [col0, col1].asStackView(spacing: 12) stackView.translatesAutoresizingMaskIntoConstraints = false background.addSubview(stackView) + symbolLabel.translatesAutoresizingMaskIntoConstraints = false + background.addSubview(symbolLabel) + NSLayoutConstraint.activate([ + symbolLabel.anchorsConstraint(to: tokenIconImageView), + + tokenIconImageView.heightAnchor.constraint(equalToConstant: 40), + tokenIconImageView.widthAnchor.constraint(equalToConstant: 40), stackView.anchorsConstraint(to: background, edgeInsets: .init(top: 16, left: 20, bottom: 16, right: 16)), background.anchorsConstraint(to: contentView) ]) @@ -56,7 +72,13 @@ class EthTokenViewCell: UITableViewCell { titleLabel.textColor = viewModel.titleColor titleLabel.font = viewModel.titleFont titleLabel.text = "\(viewModel.amount) \(viewModel.title)" - titleLabel.baselineAdjustment = .alignCenters + titleLabel.baselineAdjustment = .alignCenters + + symbolLabel.textColor = viewModel.symbolColor + symbolLabel.font = viewModel.symbolFont + symbolLabel.textAlignment = .center + symbolLabel.adjustsFontSizeToFitWidth = true + symbolLabel.text = viewModel.symbolInIcon valuePercentageChangeValueLabel.textColor = viewModel.valuePercentageChangeColor valuePercentageChangeValueLabel.font = viewModel.textValueFont @@ -74,9 +96,10 @@ class EthTokenViewCell: UITableViewCell { blockchainLabel.font = viewModel.subtitleFont blockchainLabel.text = viewModel.blockChainName blockchainLabel.isHidden = viewModel.blockChainLabelHidden - + viewsWithContent.forEach { $0.alpha = viewModel.alpha } + tokenIconImageView.image = viewModel.iconImage } } diff --git a/AlphaWallet/Tokens/Views/FungibleTokenViewCell.swift b/AlphaWallet/Tokens/Views/FungibleTokenViewCell.swift index cd659e82b..502f9bcad 100644 --- a/AlphaWallet/Tokens/Views/FungibleTokenViewCell.swift +++ b/AlphaWallet/Tokens/Views/FungibleTokenViewCell.swift @@ -9,25 +9,41 @@ class FungibleTokenViewCell: UITableViewCell { private let background = UIView() private let titleLabel = UILabel() - private let blockchainLabel = UILabel() + private let symbolLabel = UILabel() + private let blockchainLabel = UILabel() private var viewsWithContent: [UIView] { [self.titleLabel, blockchainLabel] } + private var tokenIconImageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFit + imageView.translatesAutoresizingMaskIntoConstraints = false + return imageView + }() + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(background) background.translatesAutoresizingMaskIntoConstraints = false - let bottomRowStack = [blockchainLabel, UIView.spacerWidth(flexible: true)].asStackView(spacing: 15) - let stackView = [ + let col0 = tokenIconImageView + let col1 = [ titleLabel, - bottomRowStack, + [blockchainLabel, UIView.spacerWidth(flexible: true)].asStackView(spacing: 15) ].asStackView(axis: .vertical) + let stackView = [col0, col1].asStackView(spacing: 12) stackView.translatesAutoresizingMaskIntoConstraints = false background.addSubview(stackView) + symbolLabel.translatesAutoresizingMaskIntoConstraints = false + background.addSubview(symbolLabel) + NSLayoutConstraint.activate([ + symbolLabel.anchorsConstraint(to: tokenIconImageView), + + tokenIconImageView.heightAnchor.constraint(equalToConstant: 40), + tokenIconImageView.widthAnchor.constraint(equalToConstant: 40), stackView.anchorsConstraint(to: background, edgeInsets: .init(top: 16, left: 20, bottom: 16, right: 16)), background.anchorsConstraint(to: contentView) ]) @@ -50,6 +66,12 @@ class FungibleTokenViewCell: UITableViewCell { titleLabel.text = "\(viewModel.amount) \(viewModel.title)" titleLabel.baselineAdjustment = .alignCenters + symbolLabel.textColor = viewModel.symbolColor + symbolLabel.font = viewModel.symbolFont + symbolLabel.textAlignment = .center + symbolLabel.adjustsFontSizeToFitWidth = true + symbolLabel.text = viewModel.symbolInIcon + blockchainLabel.textColor = viewModel.subtitleColor blockchainLabel.font = viewModel.subtitleFont blockchainLabel.text = viewModel.blockChainName @@ -57,5 +79,7 @@ class FungibleTokenViewCell: UITableViewCell { viewsWithContent.forEach { $0.alpha = viewModel.alpha } + + tokenIconImageView.image = viewModel.iconImage } } diff --git a/AlphaWallet/Tokens/Views/NonFungibleTokenViewCell.swift b/AlphaWallet/Tokens/Views/NonFungibleTokenViewCell.swift index 099352690..061bcaf46 100644 --- a/AlphaWallet/Tokens/Views/NonFungibleTokenViewCell.swift +++ b/AlphaWallet/Tokens/Views/NonFungibleTokenViewCell.swift @@ -9,24 +9,41 @@ class NonFungibleTokenViewCell: UITableViewCell { private let background = UIView() private let titleLabel = UILabel() + private let symbolLabel = UILabel() private let blockchainLabel = UILabel() private var viewsWithContent: [UIView] { [self.titleLabel, self.blockchainLabel] } + private var tokenIconImageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFit + imageView.translatesAutoresizingMaskIntoConstraints = false + return imageView + }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(background) background.translatesAutoresizingMaskIntoConstraints = false - let stackView = [ + + let col0 = tokenIconImageView + let col1 = [ titleLabel, - [blockchainLabel, UIView.spacerWidth(flexible: true)].asStackView(spacing: 15), + [blockchainLabel, UIView.spacerWidth(flexible: true)].asStackView(spacing: 15) ].asStackView(axis: .vertical) + let stackView = [col0, col1].asStackView(spacing: 12) stackView.translatesAutoresizingMaskIntoConstraints = false background.addSubview(stackView) + symbolLabel.translatesAutoresizingMaskIntoConstraints = false + background.addSubview(symbolLabel) + NSLayoutConstraint.activate([ + symbolLabel.anchorsConstraint(to: tokenIconImageView), + + tokenIconImageView.heightAnchor.constraint(equalToConstant: 40), + tokenIconImageView.widthAnchor.constraint(equalToConstant: 40), stackView.anchorsConstraint(to: background, edgeInsets: .init(top: 16, left: 20, bottom: 16, right: 16)), background.anchorsConstraint(to: contentView) ]) @@ -49,6 +66,12 @@ class NonFungibleTokenViewCell: UITableViewCell { titleLabel.text = "\(viewModel.amount) \(viewModel.title)" titleLabel.baselineAdjustment = .alignCenters + symbolLabel.textColor = viewModel.symbolColor + symbolLabel.font = viewModel.symbolFont + symbolLabel.textAlignment = .center + symbolLabel.adjustsFontSizeToFitWidth = true + symbolLabel.text = viewModel.symbolInIcon + blockchainLabel.textColor = viewModel.subtitleColor blockchainLabel.font = viewModel.subtitleFont blockchainLabel.text = viewModel.blockChainName @@ -56,5 +79,6 @@ class NonFungibleTokenViewCell: UITableViewCell { viewsWithContent.forEach { $0.alpha = viewModel.alpha } + tokenIconImageView.image = viewModel.iconImage } }