Skip to content

Commit

Permalink
[MBL-983] Already Reported Project Label (#1857)
Browse files Browse the repository at this point in the history
* create ReportProjectCell to display swiftui view

* UITableView cell will be added to the Project Page Table View
* Used to display a new swiftui view for the report this project label and already reported label

* add new cell below overviewSubpages and pass in project's flagging prop

* rename label view

* fix precondition failure: cyclic graph

ReportProjectLabelView (SwiftUI View) re-renders when its text labels contain hyperlinks. Since we're rendering hyperlinks with the helper in Text+HTML.swift, this seems to cause a `precondition failure: cyclic graph` crash. This check prevents that by making sure the cell is only configured once on dequeue.

* layout tableview if needed

needs to be called because we're adding a SwiftUI view to the cell's contentView

* conditionally render labels using `flagged` property

* cleanup

* pass project flagged property along with projectURL to viewcontroller so we know which label can be tappable

* remove temp fix for cyclic graph crash

* update tests

* updateProjectPageViewControllerDataSourceTests  tests

* fix pamphletsubpage cell divider

we don't need to hide it now that there is a new section below it.

* fix view styling on iPad

* formatting

* support more than one hyperlink in a string

* undo change in deprecated file

* update hyperlink urls

* set accessibility traits and cell's traitCollection

* pop views on successful submissionupdate contraint margins
  • Loading branch information
scottkicks authored Sep 28, 2023
1 parent 70dc8b3 commit 8f40c4c
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public final class ProjectPageViewController: UIViewController, MessageBannerVie
self.tableView.registerCellClass(ImageViewElementCell.self)
self.tableView.registerCellClass(AudioVideoViewElementCell.self)
self.tableView.registerCellClass(ExternalSourceViewElementCell.self)
self.tableView.registerCellClass(ReportProjectCell.self)
self.tableView.register(nib: .ProjectPamphletMainCell)
self.tableView.register(nib: .ProjectPamphletSubpageCell)
self.tableView.registerCellClass(ProjectRisksCell.self)
Expand Down Expand Up @@ -382,8 +383,9 @@ public final class ProjectPageViewController: UIViewController, MessageBannerVie

self.viewModel.outputs.goToReportProject
.observeForControllerAction()
.observeValues { [weak self] in
self?.goToReportProject(projectUrl: $0)
.observeValues { [weak self] flagged, projectUrl in
guard !flagged else { return }
self?.goToReportProject(projectUrl: projectUrl)
}

self.viewModel.outputs.goToUpdates
Expand Down Expand Up @@ -829,9 +831,9 @@ extension ProjectPageViewController: UITableViewDelegate {
self.viewModel.inputs.tappedComments()
} else if self.dataSource.indexPathIsUpdatesSubpage(indexPath) {
self.viewModel.inputs.tappedUpdates()
} else if self.dataSource.indexPathIsReportProject(indexPath) {
self.viewModel.inputs.tappedReportProject()
}
case ProjectPageViewControllerDataSource.Section.overviewReportProject.rawValue:
self.viewModel.inputs.tappedReportProject()
case ProjectPageViewControllerDataSource.Section.faqsAskAQuestion.rawValue:
self.viewModel.inputs.askAQuestionCellTapped()
case ProjectPageViewControllerDataSource.Section.faqs.rawValue:
Expand Down Expand Up @@ -863,7 +865,9 @@ extension ProjectPageViewController: UITableViewDelegate {

/// If we are displaying the `ProjectPamphletSubpageCell` we do not want to show the cells separator.
self.tableView.separatorStyle = indexPath.section == ProjectPageViewControllerDataSource.Section
.overviewSubpages.rawValue ? .none : .singleLine
.overviewReportProject.rawValue ? .none : .singleLine

self.tableView.layoutIfNeeded()
}

public func tableView(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ internal final class ProjectPageViewControllerDataSource: ValueCellDataSource {
case overviewCreatorHeader
case overview
case overviewSubpages
case overviewReportProject
case campaignHeader
case campaign
case faqsHeader
Expand Down Expand Up @@ -104,15 +105,20 @@ internal final class ProjectPageViewControllerDataSource: ValueCellDataSource {

let values: [ProjectPamphletSubpage] = [
.comments(project.stats.commentsCount as Int?, .first),
.updates(project.stats.updatesCount as Int?, .middle),
.reportProject(.last)
.updates(project.stats.updatesCount as Int?, .middle)
]

self.set(
values: values,
cellClass: ProjectPamphletSubpageCell.self,
inSection: Section.overviewSubpages.rawValue
)

self.set(
values: [project.flagging ?? false],
cellClass: ReportProjectCell.self,
inSection: Section.overviewReportProject.rawValue
)
case .campaign:
self.set(
values: [HeaderValue.campaign.description],
Expand Down Expand Up @@ -339,6 +345,8 @@ internal final class ProjectPageViewControllerDataSource: ValueCellDataSource {
cell.configureWith(value: value)
case let (cell as ExternalSourceViewElementCell, value as ExternalSourceViewElement):
cell.configureWith(value: value)
case let (cell as ReportProjectCell, value as Bool):
cell.configureWith(value: value)
default:
assertionFailure("Unrecognized combo: \(cell), \(value)")
}
Expand Down Expand Up @@ -449,10 +457,6 @@ internal final class ProjectPageViewControllerDataSource: ValueCellDataSource {
return (self[indexPath] as? ProjectPamphletSubpage)?.isUpdates == true
}

internal func indexPathIsReportProject(_ indexPath: IndexPath) -> Bool {
return (self[indexPath] as? ProjectPamphletSubpage)?.isReportProject == true
}

internal func isExpandedValuesForFAQsSection() -> [Bool]? {
guard let values = self[section: Section.faqs.rawValue] as? [(ProjectFAQ, Bool)] else { return nil }
return values.map { _, isExpanded in isExpanded }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
.rawValue
private let overviewSection = ProjectPageViewControllerDataSource.Section.overview.rawValue
private let overviewSubpagesSection = ProjectPageViewControllerDataSource.Section.overviewSubpages.rawValue
private let overviewReportProject = ProjectPageViewControllerDataSource.Section.overviewReportProject
.rawValue
private let faqsHeaderSection = ProjectPageViewControllerDataSource.Section.faqsHeader.rawValue
private let faqsEmptySection = ProjectPageViewControllerDataSource.Section.faqsEmpty.rawValue
private let faqsSection = ProjectPageViewControllerDataSource.Section.faqs.rawValue
Expand Down Expand Up @@ -144,7 +146,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
refTag: nil,
isExpandedStates: [false, false, false, false]
)
XCTAssertEqual(9, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(10, self.dataSource.numberOfSections(in: self.tableView))

// faqsHeader
XCTAssertEqual(
Expand Down Expand Up @@ -200,7 +202,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
refTag: nil,
isExpandedStates: [false, false, false, false]
)
XCTAssertEqual(8, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(9, self.dataSource.numberOfSections(in: self.tableView))

// faqsHeader
XCTAssertEqual(
Expand Down Expand Up @@ -245,7 +247,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
project: project,
refTag: nil
)
XCTAssertEqual(9, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(10, self.dataSource.numberOfSections(in: self.tableView))

// faqsHeader
XCTAssertEqual(
Expand Down Expand Up @@ -300,7 +302,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
project: project,
refTag: nil
)
XCTAssertEqual(7, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(8, self.dataSource.numberOfSections(in: self.tableView))

// faqsHeader
XCTAssertEqual(
Expand Down Expand Up @@ -343,7 +345,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
refTag: nil,
isExpandedStates: nil
)
XCTAssertEqual(12, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(13, self.dataSource.numberOfSections(in: self.tableView))

// risksHeader
XCTAssertEqual(
Expand Down Expand Up @@ -396,7 +398,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
refTag: nil,
isExpandedStates: nil
)
XCTAssertEqual(17, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(18, self.dataSource.numberOfSections(in: self.tableView))

XCTAssertEqual(
1,
Expand Down Expand Up @@ -477,7 +479,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
refTag: nil,
isExpandedStates: nil
)
XCTAssertEqual(17, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(18, self.dataSource.numberOfSections(in: self.tableView))

XCTAssertEqual(
1,
Expand Down Expand Up @@ -551,7 +553,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
refTag: nil,
isExpandedStates: nil
)
XCTAssertEqual(20, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(21, self.dataSource.numberOfSections(in: self.tableView))

// environmentCommitmentsHeader
XCTAssertEqual(
Expand Down Expand Up @@ -607,7 +609,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
refTag: nil,
isExpandedStates: nil
)
XCTAssertEqual(20, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(21, self.dataSource.numberOfSections(in: self.tableView))

// environmentCommitmentsHeader
XCTAssertEqual(
Expand Down Expand Up @@ -659,7 +661,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
refTag: nil,
isExpandedStates: nil
)
XCTAssertEqual(5, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(6, self.dataSource.numberOfSections(in: self.tableView))

// campaign header section
XCTAssertEqual(
Expand Down Expand Up @@ -715,7 +717,7 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
refTag: nil,
isExpandedStates: nil
)
XCTAssertEqual(3, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(4, self.dataSource.numberOfSections(in: self.tableView))

// overviewCreatorHeader
XCTAssertEqual(
Expand All @@ -731,10 +733,15 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {

// overviewSubpages
XCTAssertEqual(
3,
2,
self.dataSource.tableView(self.tableView, numberOfRowsInSection: self.overviewSubpagesSection)
)

XCTAssertEqual(
1,
self.dataSource.tableView(self.tableView, numberOfRowsInSection: self.overviewReportProject)
)

XCTAssertEqual(
"ProjectPamphletMainCell",
self.dataSource.reusableId(item: 0, section: self.overviewSection)
Expand All @@ -743,6 +750,11 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
"ProjectPamphletSubpageCell",
self.dataSource.reusableId(item: 0, section: self.overviewSubpagesSection)
)

XCTAssertEqual(
"ReportProjectCell",
self.dataSource.reusableId(item: 0, section: self.overviewReportProject)
)
}
}

Expand Down Expand Up @@ -818,31 +830,6 @@ final class ProjectPageViewControllerDataSourceTests: XCTestCase {
)
}

func testIndexPathIsReportProjectSubpage() {
let project = Project.template
|> \.displayPrelaunch .~ false
|> \.extendedProjectProperties .~ ExtendedProjectProperties(
environmentalCommitments: [],
faqs: [],
aiDisclosure: nil,
risks: "",
story: self.storyViewableElements,
minimumPledgeAmount: 1
)

self.dataSource.load(
navigationSection: .overview,
project: project,
refTag: nil
)

XCTAssertEqual(
self.dataSource
.indexPathIsReportProject(IndexPath(row: 2, section: self.overviewSubpagesSection)),
true
)
}

func testUpdatingCampaign_WithImageViewElementImage_Success() {
let project = Project.template
|> \.extendedProjectProperties .~ ExtendedProjectProperties(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Library
import Prelude
import SwiftUI
import UIKit

internal final class ReportProjectCell: UITableViewCell, ValueCell {
internal func configureWith(value projectFlagged: Bool) {
self.setupTableViewCellStyle(projectFlagged: projectFlagged)
self.setupReportProjectLabelView(projectFlagged: projectFlagged)
}

internal override func layoutSubviews() {
super.layoutSubviews()
}

internal override func bindStyles() {
super.bindStyles()

self.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: .greatestFiniteMagnitude)
self.setNeedsLayout()
}

// MARK: - Private Methods

private func setupTableViewCellStyle(projectFlagged: Bool) {
let accessibilityTraits = projectFlagged
? UIAccessibilityTraits.staticText
: UIAccessibilityTraits.button

_ = self
|> baseTableViewCellStyle()
|> ReportProjectCell.lens.accessibilityTraits .~ accessibilityTraits
}

private func setupReportProjectLabelView(projectFlagged: Bool) {
if #available(iOS 15.0, *) {
DispatchQueue.main.async {
let hostingController =
UIHostingController(rootView: ReportProjectLabelView(flagged: projectFlagged))

hostingController.view.translatesAutoresizingMaskIntoConstraints = false
hostingController.view.backgroundColor = .clear

self.contentView.addSubview(hostingController.view)

let leftRightInset = self.traitCollection.isRegularRegular ? Styles.grid(16) : Styles.gridHalf(5)

NSLayoutConstraint.activate([
hostingController.view.topAnchor
.constraint(equalTo: self.contentView.topAnchor, constant: Styles.gridHalf(5)),
hostingController.view.bottomAnchor
.constraint(equalTo: self.contentView.bottomAnchor, constant: -Styles.gridHalf(5)),
hostingController.view.leadingAnchor
.constraint(equalTo: self.contentView.leadingAnchor, constant: leftRightInset),
hostingController.view.trailingAnchor
.constraint(equalTo: self.contentView.trailingAnchor, constant: -leftRightInset)
])
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import Library
import SwiftUI

@available(iOS 15.0, *)
struct ReportProjectLabelView: View {
let flagged: Bool

@SwiftUI.Environment(\.horizontalSizeClass) private var horizontalSizeClass

var body: some View {
if flagged {
AlreadyReportedView()
} else {
HStack {
Text(Strings.Report_this_project_to())
.font(Font(UIFont.ksr_body(size: 14)))
.foregroundColor(Color(.ksr_support_700))

Spacer()

Image("chevron-right")
.resizable()
.scaledToFit()
.frame(width: 10, height: 10)
}
.padding(self.horizontalSizeClass == .regular ? 0 : 10)
}
}

private struct AlreadyReportedView: View {
var body: some View {
HStack(alignment: .top, spacing: 10) {
Image("info")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundColor(Color(.ksr_support_500))

Text(
html: Strings.It_looks(
our_rules: HelpType.prohibitedItems
.url(withBaseUrl: AppEnvironment.current.apiService.serverConfig.webBaseUrl)?
.absoluteString ?? "",
community_guidelines: HelpType.community
.url(withBaseUrl: AppEnvironment.current.apiService.serverConfig.webBaseUrl)?
.absoluteString ?? ""
),
with: [
ReportProjectHyperLinkType.ourRules.stringLiteral(),
ReportProjectHyperLinkType.communityGuidelines.stringLiteral()
]
)
.font(Font(UIFont.ksr_caption1()))
}
.padding()
.background(Color(.ksr_support_100))
.cornerRadius(15)
}
}
}

@available(iOS 15.0, *)
struct ReportProjectView_Previews: PreviewProvider {
static var previews: some View {
ReportProjectLabelView(flagged: false)
}
}
Loading

0 comments on commit 8f40c4c

Please sign in to comment.