Skip to content

Commit ca1e14d

Browse files
authored
Update Favicons to include 2 letters + colors (macOS parity) (duckduckgo#1927)
Task/Issue URL: https://app.asana.com/0/1142021229838617/1205285857217353/f Description: Updates the favicon generator to support multiple characters. It also makes sure we pass random colors for fake icon generation (which was not working before)
1 parent 4e77c16 commit ca1e14d

7 files changed

+53
-30
lines changed

DuckDuckGo/AutofillListItemTableViewCell.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class AutofillListItemTableViewCell: UITableViewCell {
126126
private func setupContentView(with item: AutofillLoginListItemViewModel) {
127127
titleLabel.text = item.title
128128
subtitleLabel.text = item.subtitle
129-
iconImageView.loadFavicon(forDomain: item.account.domain, usingCache: .fireproof, preferredFakeFaviconLetter: item.preferredFaviconLetter)
129+
iconImageView.loadFavicon(forDomain: item.account.domain, usingCache: .fireproof, preferredFakeFaviconLetters: item.preferredFaviconLetters)
130130
}
131131

132132
override func layoutSubviews() {

DuckDuckGo/AutofillLoginDetailsHeaderView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ struct AutofillLoginDetailsHeaderView: View {
2828
HStack(spacing: Constants.horizontalStackSpacing) {
2929
FaviconView(viewModel: FaviconViewModel(domain: viewModel.domain,
3030
cacheType: .fireproof,
31-
preferredFakeFaviconLetter: viewModel.preferredFakeFaviconLetter))
31+
preferredFakeFaviconLetters: viewModel.preferredFakeFaviconLetters))
3232
.scaledToFit()
3333
.frame(width: Constants.imageSize, height: Constants.imageSize)
3434
.accessibilityHidden(true)

DuckDuckGo/AutofillLoginDetailsViewModel.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,13 +455,18 @@ final class AutofillLoginDetailsHeaderViewModel: ObservableObject {
455455
@Published var title: String = ""
456456
@Published var subtitle: String = ""
457457
@Published var domain: String = ""
458-
@Published var preferredFakeFaviconLetter: String?
458+
@Published var preferredFakeFaviconLetters: String?
459459

460460
func updateData(with account: SecureVaultModels.WebsiteAccount, tld: TLD, autofillDomainNameUrlMatcher: AutofillDomainNameUrlMatcher, autofillDomainNameUrlSort: AutofillDomainNameUrlSort) {
461461
self.title = account.name(tld: tld, autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher)
462462
self.subtitle = UserText.autofillLoginDetailsLastUpdated(for: (dateFormatter.string(from: account.lastUpdated)))
463463
self.domain = account.domain ?? ""
464-
self.preferredFakeFaviconLetter = account.firstTLDLetter(tld: tld, autofillDomainNameUrlSort: autofillDomainNameUrlSort)
464+
465+
// Update favicon
466+
let accountName = account.name(tld: tld, autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher)
467+
let accountTitle = (account.title?.isEmpty == false) ? account.title! : "#"
468+
self.preferredFakeFaviconLetters = tld.eTLDplus1(accountName) ?? accountTitle
469+
465470
}
466471

467472
}

DuckDuckGo/AutofillLoginListItemViewModel.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@ import Common
2525
final class AutofillLoginListItemViewModel: Identifiable, Hashable {
2626
@Published var image = UIImage(systemName: "globe")!
2727

28+
var preferredFaviconLetters: String {
29+
let accountName = self.account.name(tld: tld, autofillDomainNameUrlMatcher: urlMatcher)
30+
let accountTitle = (account.title?.isEmpty == false) ? account.title! : "#"
31+
return tld.eTLDplus1(accountName) ?? accountTitle
32+
}
33+
2834
let account: SecureVaultModels.WebsiteAccount
2935
let title: String
3036
let subtitle: String
31-
let preferredFaviconLetter: String?
3237
let id = UUID()
3338
let tld: TLD
39+
let urlMatcher: AutofillDomainNameUrlMatcher
3440

3541
internal init(account: SecureVaultModels.WebsiteAccount,
3642
tld: TLD,
@@ -40,16 +46,15 @@ final class AutofillLoginListItemViewModel: Identifiable, Hashable {
4046
self.tld = tld
4147
self.title = account.name(tld: tld, autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher)
4248
self.subtitle = account.username ?? ""
43-
self.preferredFaviconLetter = account.firstTLDLetter(tld: tld, autofillDomainNameUrlSort: autofillDomainNameUrlSort)
44-
49+
self.urlMatcher = autofillDomainNameUrlMatcher
4550
fetchImage()
4651
}
4752

4853
private func fetchImage() {
4954
FaviconsHelper.loadFaviconSync(forDomain: account.domain,
5055
usingCache: .fireproof,
5156
useFakeFavicon: true,
52-
preferredFakeFaviconLetter: preferredFaviconLetter) { image, _ in
57+
preferredFakeFaviconLetters: preferredFaviconLetters) { image, _ in
5358
if let image = image {
5459
self.image = image
5560
} else {

DuckDuckGo/FaviconViewModel.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,25 @@ final class FaviconViewModel {
2626
private let domain: String
2727
private let useFakeFavicon: Bool
2828
private let cacheType: Favicons.CacheType
29-
private let preferredFaviconLetter: String?
29+
private let preferredFaviconLetters: String?
3030

3131
internal init(domain: String,
3232
useFakeFavicon: Bool = true,
3333
cacheType: Favicons.CacheType = .tabs,
34-
preferredFakeFaviconLetter: String? = nil) {
34+
preferredFakeFaviconLetters: String? = nil) {
3535

3636
self.domain = domain
3737
self.useFakeFavicon = useFakeFavicon
3838
self.cacheType = cacheType
39-
self.preferredFaviconLetter = preferredFakeFaviconLetter
39+
self.preferredFaviconLetters = preferredFakeFaviconLetters
4040
loadFavicon()
4141
}
4242

4343
private func loadFavicon() {
4444
FaviconsHelper.loadFaviconSync(forDomain: domain,
4545
usingCache: cacheType,
4646
useFakeFavicon: useFakeFavicon,
47-
preferredFakeFaviconLetter: preferredFaviconLetter) { image, _ in
47+
preferredFakeFaviconLetters: preferredFaviconLetters) { image, _ in
4848
if let image = image {
4949
self.image = image
5050
}

DuckDuckGo/FaviconsHelper.swift

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,18 @@
2020
import UIKit
2121
import Core
2222
import Kingfisher
23+
import Common
2324

2425
struct FaviconsHelper {
25-
26+
27+
private static let tld: TLD = AppDependencyProvider.shared.storageCache.tld
28+
2629
static func loadFaviconSync(forDomain domain: String?,
2730
usingCache cacheType: Favicons.CacheType,
2831
useFakeFavicon: Bool,
29-
preferredFakeFaviconLetter: String? = nil,
32+
preferredFakeFaviconLetters: String? = nil,
3033
completion: ((UIImage?, Bool) -> Void)? = nil) {
31-
34+
3235
func complete(_ image: UIImage?) {
3336
var fake = false
3437
var resultImage: UIImage?
@@ -38,7 +41,8 @@ struct FaviconsHelper {
3841
} else if useFakeFavicon, let domain = domain {
3942
fake = true
4043
resultImage = Self.createFakeFavicon(forDomain: domain,
41-
preferredFakeFaviconLetter: preferredFakeFaviconLetter)
44+
backgroundColor: UIColor.forDomain(domain),
45+
preferredFakeFaviconLetters: preferredFakeFaviconLetters)
4246
}
4347
completion?(resultImage, fake)
4448
}
@@ -92,12 +96,13 @@ struct FaviconsHelper {
9296
size: CGFloat = 192,
9397
backgroundColor: UIColor = UIColor.greyishBrown2,
9498
bold: Bool = true,
95-
preferredFakeFaviconLetter: String? = nil) -> UIImage? {
99+
preferredFakeFaviconLetters: String? = nil,
100+
letterCount: Int = 2) -> UIImage? {
96101

97102
let cornerRadius = size * 0.125
98-
let fontSize = size * 0.76
99-
100103
let imageRect = CGRect(x: 0, y: 0, width: size, height: size)
104+
let padding = size * 0.16
105+
let labelFrame = CGRect(x: padding, y: padding, width: imageRect.width - (2 * padding), height: imageRect.height - (2 * padding))
101106

102107
let renderer = UIGraphicsImageRenderer(size: imageRect.size)
103108
let icon = renderer.image { imageContext in
@@ -106,21 +111,29 @@ struct FaviconsHelper {
106111
context.setFillColor(backgroundColor.cgColor)
107112
context.addPath(CGPath(roundedRect: imageRect, cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil))
108113
context.fillPath()
109-
110-
let label = UILabel(frame: imageRect)
111-
label.font = bold ? UIFont.boldAppFont(ofSize: fontSize) : UIFont.appFont(ofSize: fontSize)
114+
115+
let label = UILabel(frame: labelFrame)
116+
label.numberOfLines = 1
117+
label.adjustsFontSizeToFitWidth = true
118+
label.minimumScaleFactor = 0.1
119+
label.baselineAdjustment = .alignCenters
120+
label.font = bold ? UIFont.boldAppFont(ofSize: size) : UIFont.appFont(ofSize: size)
112121
label.textColor = UIColor.white
113122
label.textAlignment = .center
114-
label.text = preferredFakeFaviconLetter ?? String(domain.droppingWwwPrefix().prefix(1).uppercased())
115-
label.sizeToFit()
116-
117-
context.translateBy(x: (imageRect.width - label.bounds.width) / 2.0,
118-
y: (imageRect.height - label.font.ascender) / 2.0 - (label.font.ascender - label.font.capHeight) / 2.0)
119-
123+
124+
if let prefferedPrefix = preferredFakeFaviconLetters?.prefix(letterCount).capitalized {
125+
label.text = prefferedPrefix
126+
} else {
127+
label.text = String(tld.eTLDplus1(domain)?.prefix(letterCount) ?? "#").capitalized
128+
}
129+
130+
context.translateBy(x: padding, y: padding)
131+
120132
label.layer.draw(in: context)
121133
}
122134

123135
return icon.withRenderingMode(.alwaysOriginal)
124136
}
137+
125138

126139
}

DuckDuckGo/UIImageViewExtension.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ extension UIImageView {
2727
func loadFavicon(forDomain domain: String?,
2828
usingCache cacheType: Favicons.CacheType,
2929
useFakeFavicon: Bool = true,
30-
preferredFakeFaviconLetter: String? = nil,
30+
preferredFakeFaviconLetters: String? = nil,
3131
completion: ((UIImage?, Bool) -> Void)? = nil) {
3232

3333
func load() {
3434
FaviconsHelper.loadFaviconSync(forDomain: domain,
3535
usingCache: cacheType,
3636
useFakeFavicon: useFakeFavicon,
37-
preferredFakeFaviconLetter: preferredFakeFaviconLetter) { image, fake in
37+
preferredFakeFaviconLetters: preferredFakeFaviconLetters) { image, fake in
3838
self.image = image
3939
completion?(image, fake)
4040
}

0 commit comments

Comments
 (0)