-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Show post types on home screens #25233
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
Changes from all commits
c09ac35
2385277
8a0c9a4
4995246
dfeb69e
0f415f1
4e3066d
71f572e
a3ba5d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,22 +4,16 @@ import WordPressAPIInternal | |
| import WordPressApiCache | ||
|
|
||
| extension WordPressApiCache { | ||
| static func bootstrap() -> WordPressApiCache? { | ||
| let instance: WordPressApiCache? = .onDiskCache() ?? .memoryCache() | ||
| instance?.startListeningForUpdates() | ||
| static func bootstrap() -> WordPressApiCache { | ||
| let instance: WordPressApiCache = .onDiskCache() ?? .memoryCache() | ||
| instance.startListeningForUpdates() | ||
| return instance | ||
| } | ||
|
|
||
| // TODO: | ||
| // - Log errors to sentry: https://github.com/wordpress-mobile/WordPress-iOS/pull/25157#discussion_r2785458461 | ||
| private static func onDiskCache() -> WordPressApiCache? { | ||
| let cacheURL: URL | ||
| do { | ||
| cacheURL = try FileManager.default | ||
| .url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: true) | ||
| .appending(path: "app.sqlite") | ||
| } catch { | ||
| NSLog("Failed to create api cache file: \(error)") | ||
| return nil | ||
| } | ||
| let cacheURL = URL.libraryDirectory.appending(path: "app.sqlite") | ||
|
|
||
| if let cache = WordPressApiCache.onDiskCache(file: cacheURL) { | ||
| return cache | ||
|
|
@@ -56,17 +50,22 @@ extension WordPressApiCache { | |
| return nil | ||
| } | ||
|
|
||
| return cache | ||
| } | ||
|
|
||
| private static func memoryCache() -> WordPressApiCache? { | ||
| do { | ||
| let cache = try WordPressApiCache() | ||
| _ = try cache.performMigrations() | ||
| return cache | ||
| var url = file | ||
| var values = URLResourceValues() | ||
| values.isExcludedFromBackup = true | ||
| try url.setResourceValues(values) | ||
| } catch { | ||
| NSLog("Failed to create memory cache: \(error)") | ||
| return nil | ||
| NSLog("Failed exclude the database file from iCloud backup: \(error)") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (nit) I'd suggest using |
||
| } | ||
|
|
||
| return cache | ||
| } | ||
|
|
||
| private static func memoryCache() -> WordPressApiCache { | ||
| // Creating an in-memory database should always succeed. | ||
| let cache = try! WordPressApiCache() | ||
| _ = try! cache.performMigrations() | ||
| return cache | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,26 +80,26 @@ public actor WordPressClient { | |
| public let api: any WordPressClientAPI | ||
|
|
||
| private var _cache: WordPressApiCache? | ||
| public var cache: WordPressApiCache? { | ||
| public var cache: WordPressApiCache { | ||
| get { | ||
| if _cache == nil { | ||
| _cache = WordPressApiCache.bootstrap() | ||
| if let _cache { | ||
| return _cache | ||
| } | ||
| return _cache | ||
| let cache = WordPressApiCache.bootstrap() | ||
| _cache = cache | ||
| return cache | ||
| } | ||
| } | ||
|
|
||
| private var _service: WpSelfHostedService? | ||
| public var service: WpSelfHostedService? { | ||
| get { | ||
| if _service == nil, let cache { | ||
| do { | ||
| _service = try api.createSelfHostedService(cache: cache) | ||
| } catch { | ||
| NSLog("Failed to create service: \(error)") | ||
| } | ||
| public var service: WpSelfHostedService { | ||
| get throws { | ||
| if let _service { | ||
| return _service | ||
| } | ||
| return _service | ||
| let service = try api.createSelfHostedService(cache: cache) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (nit) ideally, this will be created once and you'd need to handle errors in one place. By making a method |
||
| _service = service | ||
| return service | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,7 +53,7 @@ public final class AdaptiveTabBarController<Item: AdaptiveTabBarItem> { | |
| private func setupFilterBar() { | ||
| // filterBarContainer.backgroundColor = .systemGroupedBackground // .secondarySystemGroupedBackground | ||
| filterBarContainer.contentView.addSubview(filterBar) | ||
| filterBar.pinEdges(.top, to: filterBarContainer.safeAreaLayoutGuide, insets: UIEdgeInsets(.top, -filterBar.tabBarHeight)) | ||
| filterBar.pinEdges(.top, to: filterBarContainer.safeAreaLayoutGuide, insets: UIEdgeInsets(.top, -AdaptiveTabBar.tabBarHeight)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like it includes some unrelated changes. Does it need a rebase?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This changes are related (see #25233 (comment)). I extracted the value to a constant, to be reused in the CPT list view. |
||
| filterBar.pinEdges([.horizontal, .bottom]) | ||
|
|
||
| filterBar.addTarget(self, action: #selector(selectedFilterDidChange), for: .valueChanged) | ||
|
|
@@ -98,7 +98,7 @@ public final class AdaptiveTabBarController<Item: AdaptiveTabBarItem> { | |
| viewController.navigationItem.titleView = nil | ||
| viewController.view.addSubview(filterBarContainer) | ||
| filterBarContainer.pinEdges([.top, .horizontal]) | ||
| viewController.additionalSafeAreaInsets = UIEdgeInsets(.top, filterBar.tabBarHeight) | ||
| viewController.additionalSafeAreaInsets = UIEdgeInsets(.top, AdaptiveTabBar.tabBarHeight) | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import SwiftUI | ||
| import WordPressData | ||
|
|
||
| @propertyWrapper | ||
| struct SiteStorage<Value: Codable>: DynamicProperty { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is clever. Does it work as an observable property?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added a SwiftUI preview to test this property wrapper. The preview code does not go through the |
||
| @AppStorage private var data: Data | ||
| private let defaultValue: Value | ||
|
|
||
| var wrappedValue: Value { | ||
| get { | ||
| (try? JSONDecoder().decode(Value.self, from: data)) ?? defaultValue | ||
| } | ||
| nonmutating set { | ||
| data = (try? JSONEncoder().encode(newValue)) ?? Data() | ||
| } | ||
| } | ||
|
|
||
| var projectedValue: Binding<Value> { | ||
| Binding(get: { wrappedValue }, set: { wrappedValue = $0 }) | ||
| } | ||
|
|
||
| init(wrappedValue: Value, _ key: String, blog: TaggedManagedObjectID<Blog>, | ||
| store: UserDefaults? = nil) { | ||
| self.defaultValue = wrappedValue | ||
| let scopedKey = SiteStorageAccess.scopedKey(key, blog: blog) | ||
| _data = AppStorage(wrappedValue: Data(), scopedKey, store: store) | ||
| } | ||
|
|
||
| fileprivate init(wrappedValue: Value, _ key: String, scope: String, | ||
| store: UserDefaults? = nil) { | ||
| self.defaultValue = wrappedValue | ||
| let scopedKey = SiteStorageAccess.scopedKey(key, scope: scope) | ||
| _data = AppStorage(wrappedValue: Data(), scopedKey, store: store) | ||
| } | ||
| } | ||
|
|
||
| enum SiteStorageAccess { | ||
| static func read<T: Decodable>(_ type: T.Type, key: String, blog: TaggedManagedObjectID<Blog>) -> T? { | ||
| let scopedKey = scopedKey(key, blog: blog) | ||
| guard let data = UserDefaults.standard.data(forKey: scopedKey) else { return nil } | ||
| return try? JSONDecoder().decode(T.self, from: data) | ||
| } | ||
|
|
||
| static func write<T: Encodable>(_ value: T, key: String, blog: TaggedManagedObjectID<Blog>) { | ||
| let scopedKey = scopedKey(key, blog: blog) | ||
| let data = (try? JSONEncoder().encode(value)) ?? Data() | ||
| UserDefaults.standard.set(data, forKey: scopedKey) | ||
| } | ||
|
|
||
| static func exists(key: String, blog: TaggedManagedObjectID<Blog>) -> Bool { | ||
| let scopedKey = scopedKey(key, blog: blog) | ||
| return UserDefaults.standard.object(forKey: scopedKey) != nil | ||
| } | ||
|
|
||
| fileprivate static var prefix: String { "site-storage" } | ||
| fileprivate static var separator: String { "|" } | ||
|
|
||
| fileprivate static func scopedKey( | ||
| _ key: String, | ||
| blog: TaggedManagedObjectID<Blog> | ||
| ) -> String { | ||
| [prefix, blog.objectID.uriRepresentation().absoluteString, key] | ||
| .joined(separator: separator) | ||
| } | ||
|
|
||
| fileprivate static func scopedKey( | ||
| _ key: String, | ||
| scope: String | ||
| ) -> String { | ||
| [prefix, scope, key] | ||
| .joined(separator: separator) | ||
| } | ||
| } | ||
|
|
||
| #if DEBUG | ||
|
|
||
| private struct SiteStoragePreviewContent: View { | ||
| @SiteStorage("counter", scope: "tests") private var counter = 0 | ||
|
|
||
| var body: some View { | ||
| VStack(spacing: 20) { | ||
| Text("Counter: \(counter)") | ||
| .font(.headline) | ||
| Button("Increment") { | ||
| let key = SiteStorageAccess.scopedKey("counter", scope: "tests") | ||
|
|
||
| let newValue = counter + 1 | ||
| let encoded = (try? JSONEncoder().encode(newValue)) ?? Data() | ||
| UserDefaults.standard.set(encoded, forKey: key) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #Preview { | ||
| SiteStoragePreviewContent() | ||
| } | ||
|
|
||
| #endif | ||
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 pretty easy to do with
wpAssertionFailureand error included in user info. I did use these to monitor sync errors for posts.