parent
589f2f62e0
commit
afa22719bf
|
@ -47,27 +47,9 @@ extension MigrateContentView {
|
|||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
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)
|
||||
profilesSection
|
||||
}
|
||||
.themeNavigationDetail()
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
performButton()
|
||||
|
@ -79,36 +61,40 @@ extension MigrateContentView {
|
|||
}
|
||||
|
||||
private extension MigrateContentView.ListView {
|
||||
var editButton: some View {
|
||||
HStack {
|
||||
if isEditing {
|
||||
Button(Strings.Global.cancel) {
|
||||
isEditing = false
|
||||
}
|
||||
var actionsHeader: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
Text(Strings.Views.Migrate.Sections.Main.header)
|
||||
EditProfilesButton(isEditing: $isEditing, selection: $selection) {
|
||||
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> {
|
||||
Binding {
|
||||
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 {
|
||||
struct EditableRowView: View {
|
||||
let profile: MigratableProfile
|
||||
|
|
|
@ -45,40 +45,8 @@ extension MigrateContentView {
|
|||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
Text(Strings.Views.Migrate.Sections.Main.header)
|
||||
}
|
||||
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)
|
||||
messageSection
|
||||
profilesSection
|
||||
}
|
||||
.themeForm()
|
||||
.toolbar {
|
||||
|
@ -89,6 +57,46 @@ extension MigrateContentView {
|
|||
}
|
||||
|
||||
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> {
|
||||
Binding {
|
||||
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 {
|
||||
struct ControlView: View {
|
||||
let step: MigrateViewStep
|
||||
|
@ -143,9 +159,3 @@ private extension MigrateContentView.TableView {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension MigratableProfile {
|
||||
var timestamp: String {
|
||||
lastUpdate?.localizedDescription(style: .timestamp) ?? ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,15 +101,7 @@ extension AppContext {
|
|||
profilesContainerName: Constants.shared.containers.legacyV2,
|
||||
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)
|
||||
#endif
|
||||
|
||||
return AppContext(
|
||||
iapManager: .sharedForApp,
|
||||
|
|
Loading…
Reference in New Issue