Rewrite ReloadingSection to be semantic only

Use Group instead of Section. Also fix elements not loading if
initially empty.
This commit is contained in:
Davide De Rosa 2022-04-18 11:03:40 +02:00
parent f5c87d43c2
commit 017182fe81
5 changed files with 80 additions and 74 deletions

View File

@ -68,7 +68,7 @@
0E9AA978259F756A003FAFF1 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */; };
0E9C233027F47032007D5FC7 /* IntentsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9C232F27F47032007D5FC7 /* IntentsManager.swift */; };
0E9C233327F47E95007D5FC7 /* IntentDispatcher+Activities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */; };
0E9C3B6C27FB3A9C00D0F02E /* ReloadingSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9C3B6B27FB3A9C00D0F02E /* ReloadingSection.swift */; };
0E9C3B6C27FB3A9C00D0F02E /* ReloadingContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9C3B6B27FB3A9C00D0F02E /* ReloadingContent.swift */; };
0E9C3B6F27FC573E00D0F02E /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E9C3B6E27FC573E00D0F02E /* CloudKit.framework */; };
0E9E5AEF27B44CF1008C95DA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E9E5AE227B44CF1008C95DA /* Localizable.strings */; };
0E9ED48127FD9BAE003B2316 /* CopySavingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9ED48027FD9BAE003B2316 /* CopySavingButton.swift */; };
@ -250,7 +250,7 @@
0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
0E9C232F27F47032007D5FC7 /* IntentsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentsManager.swift; sourceTree = "<group>"; };
0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IntentDispatcher+Activities.swift"; sourceTree = "<group>"; };
0E9C3B6B27FB3A9C00D0F02E /* ReloadingSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReloadingSection.swift; sourceTree = "<group>"; };
0E9C3B6B27FB3A9C00D0F02E /* ReloadingContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReloadingContent.swift; sourceTree = "<group>"; };
0E9C3B6E27FC573E00D0F02E /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
0E9E5AE327B44CF1008C95DA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
0E9E5AE427B44CF1008C95DA /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -448,7 +448,7 @@
0E5324A527D297BB002565C3 /* InApp.swift */,
0EF0FAF827DD212C007EB181 /* IntentActivity.swift */,
0E5324A827D2AC55002565C3 /* LongContentView.swift */,
0E9C3B6B27FB3A9C00D0F02E /* ReloadingSection.swift */,
0E9C3B6B27FB3A9C00D0F02E /* ReloadingContent.swift */,
0ED30DCB27EA197C0057D8A3 /* RevealingSecureField.swift */,
0E2C172A27CB63F9007E8488 /* Reviewer.swift */,
0ED89C1427DE0A0C008B36D6 /* Shortcut.swift */,
@ -944,7 +944,7 @@
0EF2213127E674BD001D0BD7 /* AddProviderViewModel.swift in Sources */,
0E90DFE627BACC1500EF5078 /* AddHostViewModel.swift in Sources */,
0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */,
0E9C3B6C27FB3A9C00D0F02E /* ReloadingSection.swift in Sources */,
0E9C3B6C27FB3A9C00D0F02E /* ReloadingContent.swift in Sources */,
0E5324A627D297BB002565C3 /* InApp.swift in Sources */,
0E3B7FCD27E47B3700C66F13 /* AddHostView.swift in Sources */,
0EF2212D27E66EB5001D0BD7 /* AddProviderView.swift in Sources */,

View File

@ -1,5 +1,5 @@
//
// ReloadingSection.swift
// ReloadingContent.swift
// Passepartout
//
// Created by Davide De Rosa on 4/4/22.
@ -25,36 +25,49 @@
import SwiftUI
struct ReloadingSection<Header: View, Footer: View, T: Equatable, Content: View>: View {
struct ReloadingContent<T: Equatable, Content: View>: View {
@Environment(\.scenePhase) private var scenePhase
let header: Header
private let elements: [T]
let footer: Footer
private let equality: ([T], [T]) -> Bool
let elements: [T]
private let reload: (() -> Void)?
var equality: ([T], [T]) -> Bool = { $0 == $1 }
var isReloading = false
var reload: (() -> Void)?
@ViewBuilder let content: ([T]) -> Content
@ViewBuilder private let content: ([T]) -> Content
@State private var localElements: [T] = []
init(
observing elements: [T],
equality: @escaping ([T], [T]) -> Bool = { $0 == $1 },
reload: (() -> Void)? = nil,
@ViewBuilder content: @escaping ([T]) -> Content
) {
self.elements = elements
self.equality = equality
self.reload = reload
self.content = content
// XXX: not sure about this, but if content() is empty .onAppear() will
// never trigger, thus never setting initial elements
//
// BEWARE: localElements will not be automatically bound to changes
// in elements (use a Binding for that), but this is actually intended
_localElements = State(initialValue: elements)
if elements.isEmpty {
reload?()
}
}
var body: some View {
Section(
header: header,//progressHeader,
footer: footer
) {
Group {
content(localElements)
}.onAppear {
localElements = elements
if localElements.isEmpty {
reload?()
}
// }.onAppear {
// localElements = elements
// if localElements.isEmpty {
// reload?()
// }
}.onChange(of: elements) { newElements in
guard !equality(localElements, newElements) else {
return
@ -68,14 +81,4 @@ struct ReloadingSection<Header: View, Footer: View, T: Equatable, Content: View>
}
}
}
// private var progressHeader: some View {
// HStack {
// header
// if isReloading {
// ProgressView()
// .padding(.leading, 5)
// }
// }
// }
}

View File

@ -82,20 +82,22 @@ struct DonateView: View {
}
private var productsSection: some View {
ReloadingSection(
Section(
header: Text(L10n.Donate.Sections.OneTime.header),
footer: Text(L10n.Donate.Sections.OneTime.footer)
.xxxThemeTruncation(),
elements: productManager.donations,
equality: {
Set($0.map(\.productIdentifier)) == Set($1.map(\.productIdentifier))
},
isReloading: productManager.isRefreshingProducts,
reload: {
productManager.refreshProducts()
}
.xxxThemeTruncation()
) {
ForEach($0, id: \.productIdentifier, content: productRow)
ReloadingContent(
observing: productManager.donations,
equality: {
Set($0.map(\.productIdentifier)) == Set($1.map(\.productIdentifier))
},
reload: {
productManager.refreshProducts()
}
) {
ForEach($0, id: \.productIdentifier, content: productRow)
}
}
}

View File

@ -53,22 +53,22 @@ extension OrganizerView {
var body: some View {
debugChanges()
return ReloadingSection(
header: Text(Unlocalized.VPN.vpn),
footer: EmptyView(),
elements: profileManager.headers,
equality: {
Set($0) == Set($1)
}
) {
if !$0.isEmpty {
ForEach($0.sorted(), content: navigationLink(forHeader:))
.onAppear(perform: selectActiveProfile)
} else {
AddProfileMenu(
withImportedURLs: false,
bindings: addProfileMenuBindings
)
return Section {
ReloadingContent(
observing: profileManager.headers,
equality: {
Set($0) == Set($1)
}
) {
if !$0.isEmpty {
ForEach($0.sorted(), content: navigationLink(forHeader:))
.onAppear(perform: selectActiveProfile)
} else {
AddProfileMenu(
withImportedURLs: false,
bindings: addProfileMenuBindings
)
}
}
}.onAppear(perform: performMigrationsIfNeeded)

View File

@ -100,22 +100,23 @@ extension PaywallView {
}
private var productsSection: some View {
ReloadingSection(
Section(
header: Text(L10n.Paywall.title),
footer: Text(L10n.Paywall.Sections.Products.footer),
elements: productManager.products,
equality: {
Set($0.map(\.productIdentifier)) == Set($1.map(\.productIdentifier))
},
isReloading: productManager.isRefreshingProducts,
reload: {
productManager.refreshProducts()
},
content: { _ in
footer: Text(L10n.Paywall.Sections.Products.footer)
) {
ReloadingContent(
observing: productManager.products,
equality: {
Set($0.map(\.productIdentifier)) == Set($1.map(\.productIdentifier))
},
reload: {
productManager.refreshProducts()
}
) { _ in
ForEach(productRowModels, id: \.product.productIdentifier, content: productRow)
restoreRow
}
)
}
}
private func productRow(_ model: RowModel) -> some View {