Skip to content

Commit d99b984

Browse files
committed
Show plugin updates after webview dismissed
We had to remove the protocol for the viewmodel, or use type erasure. It was simpler to use a non-specific viewmodel which could be configured with the plugin name, and just set the properties in the Previews. This is a bit of a shame because the protocol approach was nicer, but Swift didn’t let us use it with ObservableObject, and type erasure has quite a lot of overhead.
1 parent d99708e commit d99b984

File tree

3 files changed

+124
-71
lines changed

3 files changed

+124
-71
lines changed

WooCommerce/Classes/ViewRelated/Dashboard/Settings/PluginDetailsRowView.swift

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import SwiftUI
22
import Yosemite
33

44
struct PluginDetailsRowView: View {
5-
let viewModel: PluginDetailsViewModel
5+
@ObservedObject var viewModel: PluginDetailsViewModel
66

77
@State var webViewPresented = false
88

@@ -24,7 +24,7 @@ struct PluginDetailsRowView: View {
2424
}
2525

2626
struct PluginDetailsRowContent: View {
27-
let viewModel: PluginDetailsViewModel
27+
@ObservedObject var viewModel: PluginDetailsViewModel
2828

2929
var body: some View {
3030
HStack {
@@ -36,9 +36,12 @@ struct PluginDetailsRowContent: View {
3636
Text(viewModel.version)
3737
.secondaryBodyStyle()
3838
}
39-
if viewModel.updateURL != nil {
39+
.padding([.bottom], 2)
40+
41+
if viewModel.updateAvailable {
4042
PluginDetailsRowUpdateAvailable(versionLatest: viewModel.versionLatest)
41-
.padding([.top], 2)
43+
} else {
44+
PluginDetailsRowUpToDate()
4245
}
4346
}
4447
}
@@ -63,34 +66,62 @@ struct PluginDetailsRowUpdateAvailable: View {
6366
}
6467
}
6568

69+
struct PluginDetailsRowUpToDate: View {
70+
var body: some View {
71+
HStack {
72+
Image(systemName: Constants.upToDateSymbolName)
73+
Text(Localization.upToDateTitle)
74+
Spacer()
75+
}
76+
.font(.footnote)
77+
.foregroundColor(Color(UIColor.systemGreen))
78+
}
79+
}
80+
6681
private enum Constants {
6782
static let softwareUpdateSymbolName = "exclamationmark.arrow.triangle.2.circlepath"
83+
static let upToDateSymbolName = "checkmark.circle"
6884
}
6985

7086
private enum Localization {
7187
static let updateAvailableTitle = NSLocalizedString(
72-
"Latest version",
88+
"Update available",
89+
comment: "String shown to indicate the latest version of a plugin when an " +
90+
"update is available and highlighted to the user")
91+
static let upToDateTitle = NSLocalizedString(
92+
"Up to date",
7393
comment: "String shown to indicate the latest version of a plugin when an " +
7494
"update is available and highlighted to the user")
7595
}
7696

97+
7798
struct PluginDetailsRowView_Previews: PreviewProvider {
78-
static var previews: some View {
79-
PluginDetailsRowView(viewModel: PreviewsPluginDetailsRowViewModel())
80-
.previewLayout(.fixed(width: 375, height: 100))
99+
private static func viewModel(
100+
version: String,
101+
versionLatest: String) -> PluginDetailsViewModel {
102+
let viewModel = PluginDetailsViewModel(
103+
siteID: 0,
104+
pluginName: "WooCommerce")
105+
viewModel.plugin = SystemPlugin(siteID: 0,
106+
plugin: "",
107+
name: "",
108+
version: version,
109+
versionLatest: versionLatest,
110+
url: "",
111+
authorName: "",
112+
authorUrl: "",
113+
networkActivated: false,
114+
active: true)
115+
viewModel.updateURL = URL(string: "https://woocommerce.com")!
116+
return viewModel
81117
}
82-
}
83-
84-
private struct PreviewsPluginDetailsRowViewModel: PluginDetailsViewModel {
85-
var updateURL: URL? = URL(string: "https://woocommerce.com/plugins/update")!
86118

87-
var title = "WooCommerce Version"
88-
89-
var version = "5.9.0"
90-
91-
var versionLatest: String? = "6.9.0"
92-
93-
func refreshPlugin() {
94-
// no-op
119+
static var previews: some View {
120+
Group {
121+
PluginDetailsRowView(viewModel: viewModel(version: "6.8.0", versionLatest: "6.11.0"))
122+
.previewLayout(.fixed(width: 375, height: 100))
123+
PluginDetailsRowView(viewModel: viewModel(version: "6.11.0", versionLatest: "6.11.0"))
124+
.previewLayout(.fixed(width: 375, height: 100))
125+
}
95126
}
96127
}

WooCommerce/Classes/ViewRelated/Dashboard/Settings/Plugins/WooCommercePluginViewModel.swift

Lines changed: 70 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,15 @@ import Foundation
22
import Yosemite
33
import protocol Storage.StorageManagerType
44

5-
protocol PluginDetailsViewModel {
6-
var version: String { get }
7-
var versionLatest: String? { get }
8-
var title: String { get }
9-
var updateURL: URL? { get }
10-
func refreshPlugin()
11-
}
12-
13-
final class WooCommercePluginViewModel: PluginDetailsViewModel {
14-
var updateURL: URL? {
15-
guard let url = storesManager.sessionManager.defaultSite?.pluginsURL,
16-
updateAvailable
17-
else {
18-
return nil
19-
}
20-
21-
return URL(string: url)
22-
}
23-
24-
var updateAvailable: Bool {
25-
guard let plugin = plugin else {
26-
return false
27-
}
28-
return !VersionHelpers.isVersionSupported(version: plugin.version, minimumRequired: plugin.versionLatest)
29-
}
30-
31-
var version: String {
32-
plugin?.version ?? Localization.unknownVersionValue
33-
}
34-
35-
var versionLatest: String? {
36-
plugin?.versionLatest
37-
}
38-
39-
private var plugin: SystemPlugin?
40-
41-
let title: String
42-
5+
final class PluginDetailsViewModel: ObservableObject {
436
/// ID of the site to load plugins for
447
///
458
private let siteID: Int64
469

10+
/// Name of the plugin to show details for
11+
///
12+
private let pluginName: String
13+
4714
/// Reference to the StoresManager to dispatch Yosemite Actions.
4815
///
4916
private let storesManager: StoresManager
@@ -55,7 +22,7 @@ final class WooCommercePluginViewModel: PluginDetailsViewModel {
5522
/// Results controller for the plugin list
5623
///
5724
private lazy var resultsController: ResultsController<StorageSystemPlugin> = {
58-
let predicate = NSPredicate(format: "siteID = %ld AND name = %@", self.siteID, Constants.wooCommercePluginName)
25+
let predicate = NSPredicate(format: "siteID = %ld AND name = %@", self.siteID, pluginName)
5926
let resultsController = ResultsController<StorageSystemPlugin>(
6027
storageManager: storageManager,
6128
matching: predicate,
@@ -71,38 +38,92 @@ final class WooCommercePluginViewModel: PluginDetailsViewModel {
7138
return resultsController
7239
}()
7340

41+
/// Title for the plugin details row
42+
///
43+
let title: String
44+
45+
var updateAvailable: Bool {
46+
guard let plugin = plugin else {
47+
return false
48+
}
49+
return !VersionHelpers.isVersionSupported(version: plugin.version, minimumRequired: plugin.versionLatest)
50+
}
51+
52+
/// URL for the plugins page in WP-admin, used for the update webview when an update is available
53+
///
54+
@Published var updateURL: URL?
55+
56+
/// Version of the plugin installed on the current site
57+
///
58+
@Published var version: String
59+
60+
/// Latest version of the plugin installed on the current site
61+
///
62+
@Published var versionLatest: String?
63+
64+
var plugin: SystemPlugin? {
65+
didSet {
66+
version = plugin?.version ?? Localization.unknownVersionValue
67+
versionLatest = plugin?.versionLatest
68+
updateURL = updateURL(for: plugin)
69+
}
70+
}
71+
7472
init(siteID: Int64,
73+
pluginName: String,
7574
storesManager: StoresManager = ServiceLocator.stores,
76-
storageManager: StorageManagerType = ServiceLocator.storageManager,
77-
title: String = Localization.pluginDetailTitle) {
75+
storageManager: StorageManagerType = ServiceLocator.storageManager) {
7876
self.siteID = siteID
77+
self.pluginName = pluginName
7978
self.storesManager = storesManager
8079
self.storageManager = storageManager
81-
self.title = title
82-
observeWooCommercePlugin { self.plugin = self.resultsController.fetchedObjects.first }
80+
self.title = String(format: Localization.pluginDetailTitle, pluginName)
81+
self.plugin = nil
82+
self.updateURL = nil
83+
self.version = ""
84+
self.versionLatest = nil
85+
observePlugin { self.plugin = self.resultsController.fetchedObjects.first }
8386
}
8487

8588
/// Start fetching and observing plugin data from local storage.
8689
///
87-
private func observeWooCommercePlugin(onDataChanged: @escaping () -> Void) {
90+
private func observePlugin(onDataChanged: @escaping () -> Void) {
8891
resultsController.onDidChangeContent = onDataChanged
8992
}
9093

94+
/// Used to refresh the store after the webview is used to perform an update
95+
///
9196
func refreshPlugin() {
9297
let action = SystemStatusAction.synchronizeSystemPlugins(siteID: siteID) { _ in }
9398
storesManager.dispatch(action)
9499
}
95100
}
96101

97-
private enum Constants {
98-
static let wooCommercePluginName = "WooCommerce"
102+
private extension PluginDetailsViewModel {
103+
private func updateURL(for plugin: SystemPlugin?) -> URL? {
104+
guard let url = storesManager.sessionManager.defaultSite?.pluginsURL,
105+
updateAvailable(for: plugin)
106+
else {
107+
return nil
108+
}
109+
110+
return URL(string: url)
111+
}
112+
113+
private func updateAvailable(for plugin: SystemPlugin?) -> Bool {
114+
guard let plugin = plugin else {
115+
return false
116+
}
117+
return !VersionHelpers.isVersionSupported(version: plugin.version, minimumRequired: plugin.versionLatest)
118+
}
119+
99120
}
100121

101122
private enum Localization {
102123
static let pluginDetailTitle = NSLocalizedString(
103-
"WooCommerce Version",
104-
comment: "Title for the WooCommerce plugin version detail row in settings. This is displayed with the " +
105-
"current version number, and whether an update is available.")
124+
"%1$@ Version",
125+
comment: "Title for the plugin version detail row in settings. %1$@ is a placeholder for the plugin name. " +
126+
"This is displayed with the current version number, and whether an update is available.")
106127

107128
static let unknownVersionValue = NSLocalizedString(
108129
"unknown",

WooCommerce/Classes/ViewRelated/Dashboard/Settings/Settings/SettingsViewController.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ final class SettingsViewController: UIViewController {
1717

1818
private let viewModel: ViewModel
1919

20-
private lazy var woocommercePluginViewModel: WooCommercePluginViewModel = WooCommercePluginViewModel(siteID: stores.sessionManager.defaultStoreID ?? 0,
21-
title: "WooCommerce Version")
20+
private lazy var woocommercePluginViewModel: PluginDetailsViewModel = PluginDetailsViewModel(
21+
siteID: stores.sessionManager.defaultStoreID ?? 0,
22+
pluginName: "WooCommerce")
2223

2324
/// Main TableView
2425
///

0 commit comments

Comments
 (0)