-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow automated fetching of synced bookmarks' favicons #564
Conversation
…el and MenuBookmarksViewModel
…tity unaware of display mode
…folder IDs from Constants
|
||
context.performAndWait { | ||
let bookmarks = BookmarkUtils.fetchAllBookmarks(in: context) | ||
ids = bookmarks.compactMap(\.uuid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be more efficient to use dictionary result type query to get one property?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea :) let me rework it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's done! All other comments are addressed now so ready for you to take another look.
|
||
// MARK: - Overrides | ||
|
||
override var isAsynchronous: Bool { true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this? You seem to be always starting operation through the queue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need this in order for the operation's main()
function to not report finished state after exiting, but wait until isFinished
is set by an async Task that's run there. It's the same for SyncOperation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it hurts, but I belive this value is ignored by OperationQueue - as it always starts the operation on a queue. Nonetheless we can keep it. :)
// guard let favoritesFolder = BookmarkUtils.fetchFavoritesFolder(context) else { | ||
// // Error - unable to process favorites | ||
// return | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's with this? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a remainder from pre-form-factor-specific favorites code which I forgot to remove :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great and works perfectly! Great job
I left a couple of minor comments
Also general question… are we using different icons in the iOS prompt and macOS prompt
} | ||
|
||
/** | ||
* This class manages fetching favicons for bookmarks updated by Sync. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we keep this generic and not relate it to sync specifically
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The actual API technically doesn't prevent this fetcher to be used outside of Sync. I think it's okay to be specific about it in the docs at the moment, and update the docs whenever we apply the fetcher to another domain (which may require revisiting its API anyway).
|
||
private func initStorage() throws { | ||
if !FileManager.default.fileExists(atPath: dataDirectoryURL.path) { | ||
try FileManager.default.createDirectory(at: dataDirectoryURL, withIntermediateDirectories: true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don’t want an error like BookmarksFaviconsFetcherError.failedToCreateDirectory(error)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need it because this class is not initialized in BSK, but in client apps. The sole reason for having BookmarksFaviconsFetcherError
is to be able to pass various errors to client apps via EventMapping< BookmarksFaviconsFetcherError>
. For this function we don't need EventMapping because it's initialized directly in client apps that are expected to handle the error with a pixel as appropriate.
|
||
public func storeBookmarkIDs(_ ids: Set<String>) throws { | ||
do { | ||
try ids.joined(separator: ",").data(using: .utf8)?.write(to: missingIDsFileURL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would we want to differentiate failed to store from failed to convert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't worry about conversion to Data because UUIDs is something we should have control over, and converting to data would only return nil
when a string can't be represented in UTF-8. We're even force unwrapping these calls in a bunch of places across the BSK codebase.
await runAfterOperationsFinished { | ||
let ids = (try? self.stateStore.getBookmarkIDs()) ?? [] | ||
XCTAssertTrue(ids.isEmpty) | ||
XCTAssertTrue(MockBookmarksFaviconsFetcherEventMapper.errors.isEmpty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes me think… should it be an error if empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means that there are no bookmarks to handle which is an expected scenario for users that don't have bookmarks :)
Thanks for the review @SabrinaTardio! For the prompt icons, the onboarding dialog uses the same icons. The settings option used different icons, but from what I can see, the newest designs get rid of the icons in settings altogether. |
Please review the release process for BrowserServicesKit here.
Required:
Task/Issue URL: https://app.asana.com/0/0/1205949780297088/f
iOS PR: duckduckgo/iOS#2163
macOS PR: duckduckgo/macos-browser#1857
What kind of version bump will this require?: Major
Tech Design URL: https://app.asana.com/0/481882893211075/1204986998781220/f
Description:
Add BookmarksFaviconFetcher that is used to fetch favicons for bookmarks received by Sync.
Fetcher is opt-in, controlled by a setting inside Sync settings (with an additional in-context onboarding
popup presented from client apps). Fetcher uses LinkPresentation framework to obtain a favicon
for a given domain, and in case of failure it falls back to checking hardcoded favicon URLs.
Fetcher keeps a state internally, by saving list of bookmarks IDs that need processing to a file on disk.
Fetcher plugs into clients' implementation of favicon storage by exposing FaviconStoring protocol.
Fetcher performs fetching on a serial operation queue. Each fetcher invocation cancels previously scheduled
operation and schedules a new one. Updating fetcher state is also scheduled on the operation queue -
state updates don't support cancelling and always finish before next operation is started.
Steps to test this PR:
On iOS:
On macOS:
OS Testing:
Internal references:
Software Engineering Expectations
Technical Design Template