Skip to content

Commit

Permalink
Merge pull request #128 from Automattic/etoledom/split-gravatarurl
Browse files Browse the repository at this point in the history
Split GravatarURL into AvatarURL and ProfileURL
  • Loading branch information
etoledom authored Mar 26, 2024
2 parents 4858685 + 2e96700 commit 8c98bbe
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 242 deletions.
87 changes: 87 additions & 0 deletions Sources/Gravatar/AvatarURL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import Foundation

public struct AvatarURL {
public let canonicalUrl: URL
public let hash: String
public let url: URL

let options: AvatarQueryOptions
let components: URLComponents

public init?(url: URL, options: AvatarQueryOptions = AvatarQueryOptions()) {
guard
Self.isAvatarUrl(url),
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)?.sanitizingComponents(),
let sanitizedURL = components.url,
let url = sanitizedURL.addQueryItems(from: options)
else {
return nil
}

self.canonicalUrl = sanitizedURL
self.components = components
self.hash = sanitizedURL.lastPathComponent
self.options = options
self.url = url
}

public init?(email: String, options: AvatarQueryOptions = AvatarQueryOptions()) {
self.init(hash: email.sanitized.sha256(), options: options)
}

public init?(hash: String, options: AvatarQueryOptions = AvatarQueryOptions()) {
guard let url = URL(string: .baseURL + hash) else { return nil }
self.init(url: url, options: options)
}

public static func isAvatarUrl(_ url: URL) -> Bool {
guard
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let host = components.host
else {
return false
}

return (host.hasSuffix(".gravatar.com") || host == "gravatar.com")
&& components.path.hasPrefix("/avatar/")
}

public func replacing(options: AvatarQueryOptions) -> AvatarURL? {
AvatarURL(hash: hash, options: options)
}
}

extension AvatarURL: Equatable {
public static func == (lhs: AvatarURL, rhs: AvatarURL) -> Bool {
lhs.url.absoluteString == rhs.url.absoluteString
}
}

extension String {
fileprivate static let scheme = "https"
fileprivate static let baseURL = "https://gravatar.com/avatar/"
}

extension URL {
fileprivate func addQueryItems(from options: AvatarQueryOptions) -> URL? {
guard var components = URLComponents(url: self, resolvingAgainstBaseURL: false) else {
return nil
}
components.queryItems = options.queryItems

if components.queryItems?.isEmpty == true {
components.queryItems = nil
}

return components.url
}
}

extension URLComponents {
fileprivate func sanitizingComponents() -> URLComponents {
var copy = self
copy.scheme = .scheme
copy.query = nil
return copy
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@ extension String {
return hashString
}
}

extension String {
var sanitized: String {
self.lowercased()
.trimmingCharacters(in: .whitespaces)
}
}
12 changes: 12 additions & 0 deletions Sources/Gravatar/Extensions/URL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation

extension URL {
@available(swift, deprecated: 16.0, message: "Use URL.appending(path:) instead")
func appending(pathComponent path: String) -> URL {
if #available(iOS 16.0, *) {
self.appending(path: path)
} else {
self.appendingPathComponent(path)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ extension GravatarWrapper where Component: UIImageView {
defaultAvatarOption: defaultAvatarOption
)

let gravatarURL = GravatarURL.gravatarUrl(with: email, options: downloadOptions.avatarQueryOptions)
let gravatarURL = AvatarURL(email: email, options: downloadOptions.avatarQueryOptions)?.url
return setImage(with: gravatarURL, placeholder: placeholder, options: options, completionHandler: completionHandler)
}

Expand Down
119 changes: 0 additions & 119 deletions Sources/Gravatar/GravatarURL.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Sources/Gravatar/Network/Services/AvatarService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public struct AvatarService {
with email: String,
options: ImageDownloadOptions = ImageDownloadOptions()
) async throws -> ImageDownloadResult {
guard let gravatarURL = GravatarURL.gravatarUrl(with: email, options: options.avatarQueryOptions) else {
guard let gravatarURL = AvatarURL(email: email, options: options.avatarQueryOptions)?.url else {
throw ImageFetchingError.requestError(reason: .urlInitializationFailed)
}

Expand Down
36 changes: 36 additions & 0 deletions Sources/Gravatar/ProfileURL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Foundation

public struct ProfileURL {
public let url: URL
public let hash: String
public var avatarURL: AvatarURL? {
AvatarURL(hash: hash)
}

static let baseURL: URL? = {
guard
let baseURL = URL(string: .baseURL),
let components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)
else {
return nil
}
return components.url
}()

public init?(email: String) {
let hash = email.sanitized.sha256()
self.init(hash: hash)
}

public init?(hash: String) {
guard let url = Self.baseURL?.appending(pathComponent: hash) else {
return nil
}
self.url = url
self.hash = hash
}
}

extension String {
fileprivate static let baseURL = "https://gravatar.com/"
}
Loading

0 comments on commit 8c98bbe

Please sign in to comment.