Improve migrate header on iOS (#881)

Refactor into more subviews.
This commit is contained in:
Davide 2024-11-16 15:07:47 +01:00 committed by GitHub
parent 589f2f62e0
commit afa22719bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 122 additions and 94 deletions

View File

@ -47,27 +47,9 @@ extension MigrateContentView {
var body: some View { var body: some View {
List { List {
Section { profilesSection
Text(Strings.Views.Migrate.Sections.Main.header)
}
Section {
ForEach(profiles, id: \.id) {
if isEditing {
EditableRowView(profile: $0, selection: $selection)
} else {
ControlView(
step: step,
profile: $0,
isIncluded: isIncludedBinding(for: $0.id),
status: statusBinding(for: $0.id)
)
}
}
} header: {
editButton
}
.disabled(!step.canSelect)
} }
.themeNavigationDetail()
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { ToolbarItem(placement: .confirmationAction) {
performButton() performButton()
@ -79,36 +61,40 @@ extension MigrateContentView {
} }
private extension MigrateContentView.ListView { private extension MigrateContentView.ListView {
var editButton: some View { var actionsHeader: some View {
HStack { VStack(alignment: .leading, spacing: 16) {
if isEditing { Text(Strings.Views.Migrate.Sections.Main.header)
Button(Strings.Global.cancel) { EditProfilesButton(isEditing: $isEditing, selection: $selection) {
isEditing = false onDelete(profiles.filter {
} selection.contains($0.id)
})
// disable isEditing after confirmation
} }
Spacer()
Button(isEditing ? Strings.Global.delete : Strings.Global.edit, role: isEditing ? .destructive : nil) {
if isEditing {
if !selection.isEmpty {
onDelete(profiles.filter {
selection.contains($0.id)
})
// disable isEditing after confirmation
} else {
isEditing = false
}
} else {
selection = []
isEditing = true
}
}
.disabled(isEditing && selection.isEmpty)
} }
.frame(height: 30) .textCase(.none)
.listRowInsets(.init(top: 8, leading: 0, bottom: 8, trailing: 0))
}
var profilesSection: some View {
Section {
ForEach(profiles, id: \.id) {
if isEditing {
EditableRowView(profile: $0, selection: $selection)
} else {
ControlView(
step: step,
profile: $0,
isIncluded: isIncludedBinding(for: $0.id),
status: statusBinding(for: $0.id)
)
}
}
} header: {
actionsHeader
}
.disabled(!step.canSelect)
} }
}
private extension MigrateContentView.ListView {
func isIncludedBinding(for profileId: UUID) -> Binding<Bool> { func isIncludedBinding(for profileId: UUID) -> Binding<Bool> {
Binding { Binding {
statuses[profileId] != .excluded statuses[profileId] != .excluded
@ -134,6 +120,46 @@ private extension MigrateContentView.ListView {
} }
} }
// MARK: - Subviews
private extension MigrateContentView.ListView {
struct EditProfilesButton: View {
@Binding
var isEditing: Bool
@Binding
var selection: Set<UUID>
let onDelete: () -> Void
var body: some View {
HStack {
if isEditing {
Button(Strings.Global.cancel) {
isEditing = false
}
}
Spacer()
Button(isEditing ? Strings.Global.delete : Strings.Global.edit, role: isEditing ? .destructive : nil) {
if isEditing {
if !selection.isEmpty {
onDelete()
} else {
isEditing = false
}
} else {
selection = []
isEditing = true
}
}
.disabled(isEditing && selection.isEmpty)
}
.frame(height: 30)
}
}
}
private extension MigrateContentView.ListView { private extension MigrateContentView.ListView {
struct EditableRowView: View { struct EditableRowView: View {
let profile: MigratableProfile let profile: MigratableProfile

View File

@ -45,40 +45,8 @@ extension MigrateContentView {
var body: some View { var body: some View {
Form { Form {
Section { messageSection
Text(Strings.Views.Migrate.Sections.Main.header) profilesSection
}
Section {
Table(profiles) {
TableColumn(Strings.Global.name) {
Text($0.name)
.foregroundStyle(statuses.style(for: $0.id))
}
TableColumn(Strings.Global.lastUpdate) {
Text($0.timestamp)
.foregroundStyle(statuses.style(for: $0.id))
}
TableColumn("") {
ControlView(
step: step,
isIncluded: isIncludedBinding(for: $0.id),
status: statuses[$0.id]
)
.environmentObject(theme) // TODO: #873, Table loses environment
}
.width(20)
TableColumn("") { profile in
Button {
onDelete([profile])
} label: {
ThemeImage(.editableSectionRemove)
}
.environmentObject(theme) // TODO: #873, Table loses environment
}
.width(20)
}
}
.disabled(!step.canSelect)
} }
.themeForm() .themeForm()
.toolbar { .toolbar {
@ -89,6 +57,46 @@ extension MigrateContentView {
} }
private extension MigrateContentView.TableView { private extension MigrateContentView.TableView {
var messageSection: some View {
Section {
Text(Strings.Views.Migrate.Sections.Main.header)
}
}
var profilesSection: some View {
Section {
Table(profiles) {
TableColumn(Strings.Global.name) {
Text($0.name)
.foregroundStyle(statuses.style(for: $0.id))
}
TableColumn(Strings.Global.lastUpdate) {
Text($0.timestamp)
.foregroundStyle(statuses.style(for: $0.id))
}
TableColumn("") {
ControlView(
step: step,
isIncluded: isIncludedBinding(for: $0.id),
status: statuses[$0.id]
)
.environmentObject(theme) // TODO: #873, Table loses environment
}
.width(20)
TableColumn("") { profile in
Button {
onDelete([profile])
} label: {
ThemeImage(.editableSectionRemove)
}
.environmentObject(theme) // TODO: #873, Table loses environment
}
.width(20)
}
}
.disabled(!step.canSelect)
}
func isIncludedBinding(for profileId: UUID) -> Binding<Bool> { func isIncludedBinding(for profileId: UUID) -> Binding<Bool> {
Binding { Binding {
statuses[profileId] != .excluded statuses[profileId] != .excluded
@ -102,6 +110,14 @@ private extension MigrateContentView.TableView {
} }
} }
private extension MigratableProfile {
var timestamp: String {
lastUpdate?.localizedDescription(style: .timestamp) ?? ""
}
}
// MARK: - Subviews
private extension MigrateContentView.TableView { private extension MigrateContentView.TableView {
struct ControlView: View { struct ControlView: View {
let step: MigrateViewStep let step: MigrateViewStep
@ -143,9 +159,3 @@ private extension MigrateContentView.TableView {
} }
} }
} }
private extension MigratableProfile {
var timestamp: String {
lastUpdate?.localizedDescription(style: .timestamp) ?? ""
}
}

View File

@ -101,15 +101,7 @@ extension AppContext {
profilesContainerName: Constants.shared.containers.legacyV2, profilesContainerName: Constants.shared.containers.legacyV2,
cloudKitIdentifier: BundleConfiguration.mainString(for: .legacyV2CloudKitId) cloudKitIdentifier: BundleConfiguration.mainString(for: .legacyV2CloudKitId)
) )
#if DEBUG
let migrationManager = MigrationManager(profileStrategy: profileStrategy, simulation: .init(
fakeProfiles: true,
maxMigrationTime: 3.0,
randomFailures: true
))
#else
let migrationManager = MigrationManager(profileStrategy: profileStrategy) let migrationManager = MigrationManager(profileStrategy: profileStrategy)
#endif
return AppContext( return AppContext(
iapManager: .sharedForApp, iapManager: .sharedForApp,