From 2305797ee662001a950810216d30cd74c37950d7 Mon Sep 17 00:00:00 2001 From: David Christiandy <1299411+dvdchr@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:47:36 +0700 Subject: [PATCH 1/4] Add support for multiple accounts per service --- .../Post/PrepublishingAutoSharingView.swift | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift b/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift index 0b08fb1fd321..45be27472754 100644 --- a/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift +++ b/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift @@ -8,7 +8,7 @@ struct PrepublishingAutoSharingView: View { HStack { textStack Spacer() - if model.connections.count > 0 { + if model.services.count > 0 { socialIconsView } } @@ -41,8 +41,8 @@ struct PrepublishingAutoSharingView: View { private var socialIconsView: some View { HStack(spacing: -2.0) { - ForEach(model.connections, id: \.self) { connection in - iconImage(connection.serviceName.localIconImage, opaque: connection.enabled) + ForEach(model.services, id: \.self) { service in + iconImage(service.serviceName.localIconImage, opaque: service.usesOpaqueIcon) } } } @@ -84,22 +84,52 @@ private extension PrepublishingAutoSharingView { /// The value-type data model that drives the `PrepublishingAutoSharingView`. struct PrepublishingAutoSharingViewModel { - struct Connection: Hashable { + /// A value-type representation of `PublicizeService` that's simplified for the needs of the auto-sharing view. + struct Service: Hashable { let serviceName: PublicizeService.ServiceName + let connections: [Connection] + + /// Whether the icon for this service should be opaque or transparent. + /// If at least one account is enabled, we'll show an opaque version. + var usesOpaqueIcon: Bool { + connections.reduce(false) { partialResult, connection in + return partialResult || connection.enabled + } + } + + var enabledConnections: [Connection] { + connections.filter { $0.enabled } + } + } + + struct Connection: Hashable { let account: String let enabled: Bool } // TODO: Default values are for temporary testing purposes. Will be removed later. - let connections: [Connection] = [.init(serviceName: .facebook, account: "foo", enabled: true), - .init(serviceName: .twitter, account: "bar", enabled: false), - .init(serviceName: .tumblr, account: "baz", enabled: true)] + let services: [Service] = [ + Service(serviceName: .facebook, connections: [ + Connection(account: "foo", enabled: true), + Connection(account: "foo-2", enabled: false) + ]), + Service(serviceName: .twitter, connections: [Connection(account: "bar", enabled: false)]), + Service(serviceName: .tumblr, connections: [Connection(account: "baz", enabled: true)]) + ] // TODO: Default values are for temporary testing purposes. Will be removed later. let sharingLimit: PublicizeInfo.SharingLimit? = .init(remaining: 1, limit: 30) var enabledConnectionsCount: Int { - connections.filter({ $0.enabled }).count + services.reduce(0) { partialResult, service in + return partialResult + service.enabledConnections.count + } + } + + var totalConnectionsCount: Int { + services.reduce(0) { partialResult, service in + return partialResult + service.connections.count + } } var showsWarning: Bool { @@ -110,14 +140,14 @@ struct PrepublishingAutoSharingViewModel { } var labelText: String { - switch (enabledConnectionsCount, connections.count) { + switch (enabledConnectionsCount, totalConnectionsCount) { case (let enabled, _) where enabled == 0: // not sharing to any social media return Strings.notSharingText case (let enabled, let total) where enabled == total && total == 1: // sharing to the one and only connection - guard let account = connections.first?.account else { + guard let account = services.first?.connections.first?.account else { return String() } return String(format: Strings.singleConnectionTextFormat, account) From faae9211ad644e5a2282bc7b34d7b106dece3d39 Mon Sep 17 00:00:00 2001 From: David Christiandy <1299411+dvdchr@users.noreply.github.com> Date: Wed, 12 Jul 2023 13:45:41 +0700 Subject: [PATCH 2/4] Remove test default values --- .../Post/PrepublishingAutoSharingView.swift | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift b/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift index 45be27472754..93f1150a7de9 100644 --- a/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift +++ b/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift @@ -84,13 +84,15 @@ private extension PrepublishingAutoSharingView { /// The value-type data model that drives the `PrepublishingAutoSharingView`. struct PrepublishingAutoSharingViewModel { + // MARK: Helper Models + /// A value-type representation of `PublicizeService` that's simplified for the needs of the auto-sharing view. struct Service: Hashable { let serviceName: PublicizeService.ServiceName let connections: [Connection] /// Whether the icon for this service should be opaque or transparent. - /// If at least one account is enabled, we'll show an opaque version. + /// If at least one account is enabled, an opaque version should be shown. var usesOpaqueIcon: Bool { connections.reduce(false) { partialResult, connection in return partialResult || connection.enabled @@ -107,18 +109,12 @@ struct PrepublishingAutoSharingViewModel { let enabled: Bool } - // TODO: Default values are for temporary testing purposes. Will be removed later. - let services: [Service] = [ - Service(serviceName: .facebook, connections: [ - Connection(account: "foo", enabled: true), - Connection(account: "foo-2", enabled: false) - ]), - Service(serviceName: .twitter, connections: [Connection(account: "bar", enabled: false)]), - Service(serviceName: .tumblr, connections: [Connection(account: "baz", enabled: true)]) - ] - - // TODO: Default values are for temporary testing purposes. Will be removed later. - let sharingLimit: PublicizeInfo.SharingLimit? = .init(remaining: 1, limit: 30) + // MARK: Properties + + let services: [Service] + let sharingLimit: PublicizeInfo.SharingLimit? + + // MARK: Computed Properties var enabledConnectionsCount: Int { services.reduce(0) { partialResult, service in From 64b1624a11a75aa5d10c7cb3028cd30a0a3a0ddc Mon Sep 17 00:00:00 2001 From: David Christiandy <1299411+dvdchr@users.noreply.github.com> Date: Wed, 12 Jul 2023 13:46:12 +0700 Subject: [PATCH 3/4] Populate view model from the proper Core Data models --- ...blishingViewController+JetpackSocial.swift | 57 ++++++++++++++++--- .../Post/PrepublishingViewController.swift | 2 +- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/PrepublishingViewController+JetpackSocial.swift b/WordPress/Classes/ViewRelated/Post/PrepublishingViewController+JetpackSocial.swift index 54252bf3cac5..a8c13b5fe175 100644 --- a/WordPress/Classes/ViewRelated/Post/PrepublishingViewController+JetpackSocial.swift +++ b/WordPress/Classes/ViewRelated/Post/PrepublishingViewController+JetpackSocial.swift @@ -1,21 +1,20 @@ extension PrepublishingViewController { /// Determines whether the account and the post's blog is eligible to see auto-sharing options. - var isEligibleForAutoSharing: Bool { - let postObjectID = post.objectID - let blogSupportsPublicize = coreDataStack.performQuery { context in + func isEligibleForAutoSharing(isJetpack: Bool = AppConfiguration.isJetpack, + isFeatureEnabled: Bool = FeatureFlag.jetpackSocial.enabled) -> Bool { + let blogSupportsPublicize = coreDataStack.performQuery { [postObjectID = post.objectID] context in let post = (try? context.existingObject(with: postObjectID)) as? Post return post?.blog.supportsPublicize() ?? false } - return blogSupportsPublicize && FeatureFlag.jetpackSocial.enabled + return blogSupportsPublicize && isJetpack && isFeatureEnabled } func configureSocialCell(_ cell: UITableViewCell) { // TODO: // - Show the NoConnectionView if user has 0 connections. - // - Properly create and configure the view models. - let autoSharingView = UIView.embedSwiftUIView(PrepublishingAutoSharingView(model: .init())) + let autoSharingView = UIView.embedSwiftUIView(PrepublishingAutoSharingView(model: makeAutoSharingViewModel())) cell.contentView.addSubview(autoSharingView) // Pin constraints to the cell's layoutMarginsGuide so that the content is properly aligned. @@ -25,6 +24,50 @@ extension PrepublishingViewController { autoSharingView.bottomAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.bottomAnchor), autoSharingView.trailingAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.trailingAnchor) ]) - cell.accessoryType = .disclosureIndicator + cell.accessoryType = .disclosureIndicator // TODO: only for autoSharingView. } } + +// MARK: - Helper Methods + +private extension PrepublishingViewController { + + func makeAutoSharingViewModel() -> PrepublishingAutoSharingViewModel { + return coreDataStack.performQuery { [postObjectID = post.objectID] context in + guard let post = (try? context.existingObject(with: postObjectID)) as? Post, + let connections = post.blog.sortedConnections as? [PublicizeConnection], + let supportedServices = try? PublicizeService.allSupportedServices(in: context) else { + return .init(services: [], sharingLimit: nil) + } + + // first, build a dictionary to categorize the connections. + var connectionsMap = [PublicizeService.ServiceName: [PublicizeConnection]]() + connections.forEach { connection in + let serviceName = PublicizeService.ServiceName(rawValue: connection.service) ?? .unknown + var serviceConnections = connectionsMap[serviceName] ?? [] + serviceConnections.append(connection) + connectionsMap[serviceName] = serviceConnections + } + + // then, transform [PublicizeService] to [PrepublishingAutoSharingViewModel.Service]. + let modelServices = supportedServices.compactMap { service -> PrepublishingAutoSharingViewModel.Service? in + // skip services without connections. + guard let serviceConnections = connectionsMap[service.name], + !serviceConnections.isEmpty else { + return nil + } + + return PrepublishingAutoSharingViewModel.Service( + serviceName: service.name, + connections: serviceConnections.map { + .init(account: $0.externalDisplay, + enabled: !post.publicizeConnectionDisabledForKeyringID($0.keyringConnectionID)) + } + ) + } + + return .init(services: modelServices, sharingLimit: post.blog.sharingLimit) + } + } + +} diff --git a/WordPress/Classes/ViewRelated/Post/PrepublishingViewController.swift b/WordPress/Classes/ViewRelated/Post/PrepublishingViewController.swift index 49e25b58549b..119d28ac9c4a 100644 --- a/WordPress/Classes/ViewRelated/Post/PrepublishingViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/PrepublishingViewController.swift @@ -49,7 +49,7 @@ class PrepublishingViewController: UITableViewController { switch identifier { case .autoSharing: // skip the social cell if the post's blog is not eligible for auto-sharing. - guard isEligibleForAutoSharing else { + guard isEligibleForAutoSharing() else { return nil } break From 2aa090746c55303925b960cd2e1dd38f2b9e865a Mon Sep 17 00:00:00 2001 From: David Christiandy <1299411+dvdchr@users.noreply.github.com> Date: Wed, 12 Jul 2023 17:09:55 +0700 Subject: [PATCH 4/4] Simplify code --- .../Post/PrepublishingAutoSharingView.swift | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift b/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift index 93f1150a7de9..d357a707b7ca 100644 --- a/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift +++ b/WordPress/Classes/ViewRelated/Post/PrepublishingAutoSharingView.swift @@ -94,9 +94,7 @@ struct PrepublishingAutoSharingViewModel { /// Whether the icon for this service should be opaque or transparent. /// If at least one account is enabled, an opaque version should be shown. var usesOpaqueIcon: Bool { - connections.reduce(false) { partialResult, connection in - return partialResult || connection.enabled - } + connections.reduce(false) { $0 || $1.enabled } } var enabledConnections: [Connection] { @@ -117,15 +115,11 @@ struct PrepublishingAutoSharingViewModel { // MARK: Computed Properties var enabledConnectionsCount: Int { - services.reduce(0) { partialResult, service in - return partialResult + service.enabledConnections.count - } + services.reduce(0) { $0 + $1.enabledConnections.count } } var totalConnectionsCount: Int { - services.reduce(0) { partialResult, service in - return partialResult + service.connections.count - } + services.reduce(0) { $0 + $1.connections.count } } var showsWarning: Bool {