Improve migrate design (#885)
- Mention iCloud in informational message - Redesign to always show message despite no migratable profiles - Refine the looks
This commit is contained in:
parent
1ddea2ad5e
commit
c93a43702c
|
@ -46,8 +46,9 @@ extension MigrateContentView {
|
|||
private var selection: Set<UUID> = []
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
profilesSection
|
||||
VStack {
|
||||
messageView
|
||||
profilesList
|
||||
}
|
||||
.themeNavigationDetail()
|
||||
.toolbar {
|
||||
|
@ -61,38 +62,43 @@ extension MigrateContentView {
|
|||
}
|
||||
|
||||
private extension MigrateContentView.ListView {
|
||||
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
|
||||
}
|
||||
}
|
||||
.textCase(.none)
|
||||
.listRowInsets(.init(top: 8, leading: 0, bottom: 8, trailing: 0))
|
||||
var isEmpty: Bool {
|
||||
step.isReady && profiles.isEmpty
|
||||
}
|
||||
|
||||
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)
|
||||
)
|
||||
var messageView: some View {
|
||||
Text(Strings.Views.Migrate.Sections.Main.header)
|
||||
.padding([.top, .leading, .trailing])
|
||||
}
|
||||
|
||||
var profilesList: some View {
|
||||
List {
|
||||
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: {
|
||||
EditProfilesButton(isEditing: $isEditing, selection: $selection) {
|
||||
onDelete(profiles.filter {
|
||||
selection.contains($0.id)
|
||||
})
|
||||
// disable isEditing after confirmation
|
||||
}
|
||||
.textCase(.none)
|
||||
}
|
||||
} header: {
|
||||
actionsHeader
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.disabled(!step.canSelect)
|
||||
.themeEmpty(if: isEmpty, message: Strings.Views.Migrate.noProfiles)
|
||||
}
|
||||
|
||||
func isIncludedBinding(for profileId: UUID) -> Binding<Bool> {
|
||||
|
|
|
@ -44,11 +44,10 @@ extension MigrateContentView {
|
|||
let performButton: () -> PerformButton
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
messageSection
|
||||
profilesSection
|
||||
VStack(spacing: .zero) {
|
||||
messageView
|
||||
profilesForm
|
||||
}
|
||||
.themeForm()
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .confirmationAction, content: performButton)
|
||||
}
|
||||
|
@ -57,14 +56,17 @@ extension MigrateContentView {
|
|||
}
|
||||
|
||||
private extension MigrateContentView.TableView {
|
||||
var messageSection: some View {
|
||||
Section {
|
||||
Text(Strings.Views.Migrate.Sections.Main.header)
|
||||
}
|
||||
var isEmpty: Bool {
|
||||
step.isReady && profiles.isEmpty
|
||||
}
|
||||
|
||||
var profilesSection: some View {
|
||||
Section {
|
||||
var messageView: some View {
|
||||
Text(Strings.Views.Migrate.Sections.Main.header)
|
||||
.padding([.top, .leading, .trailing])
|
||||
}
|
||||
|
||||
var profilesForm: some View {
|
||||
Form {
|
||||
Table(profiles) {
|
||||
TableColumn(Strings.Global.name) {
|
||||
Text($0.name)
|
||||
|
@ -95,6 +97,8 @@ private extension MigrateContentView.TableView {
|
|||
}
|
||||
}
|
||||
.disabled(!step.canSelect)
|
||||
.themeForm()
|
||||
.themeEmpty(if: isEmpty, message: Strings.Views.Migrate.noProfiles)
|
||||
}
|
||||
|
||||
func isIncludedBinding(for profileId: UUID) -> Binding<Bool> {
|
||||
|
|
|
@ -45,6 +45,12 @@ extension MigrateView {
|
|||
}
|
||||
}
|
||||
|
||||
extension MigrateViewStep {
|
||||
var isReady: Bool {
|
||||
![.initial, .fetching].contains(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension MigrateView.Model {
|
||||
var visibleProfiles: [MigratableProfile] {
|
||||
profiles
|
||||
|
|
|
@ -74,11 +74,7 @@ struct MigrateView: View {
|
|||
onDelete: onDelete,
|
||||
performButton: performButton
|
||||
)
|
||||
.themeProgress(
|
||||
if: [.initial, .fetching].contains(model.step),
|
||||
isEmpty: model.profiles.isEmpty,
|
||||
emptyMessage: Strings.Views.Migrate.noProfiles
|
||||
)
|
||||
.themeProgress(if: !model.step.isReady)
|
||||
.themeAnimation(on: model, category: .profiles)
|
||||
.themeConfirmation(
|
||||
isPresented: $isDeleting,
|
||||
|
|
|
@ -744,8 +744,8 @@ public enum Strings {
|
|||
}
|
||||
public enum Sections {
|
||||
public enum Main {
|
||||
/// Select below the profiles from old versions of Passepartout that you want to import. They will disappear from this list once imported successfully.
|
||||
public static let header = Strings.tr("Localizable", "views.migrate.sections.main.header", fallback: "Select below the profiles from old versions of Passepartout that you want to import. They will disappear from this list once imported successfully.")
|
||||
/// Select below the profiles from old versions of Passepartout that you want to import. In case your profiles are stored on iCloud, they may take a while to synchronize. If you do not see them now, please come back later.
|
||||
public static let header = Strings.tr("Localizable", "views.migrate.sections.main.header", fallback: "Select below the profiles from old versions of Passepartout that you want to import. In case your profiles are stored on iCloud, they may take a while to synchronize. If you do not see them now, please come back later.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@
|
|||
"views.migrate.no_profiles" = "Nothing to migrate";
|
||||
"views.migrate.items.discard" = "Discard";
|
||||
"views.migrate.items.migrate" = "Proceed";
|
||||
"views.migrate.sections.main.header" = "Select below the profiles from old versions of Passepartout that you want to import. They will disappear from this list once imported successfully.";
|
||||
"views.migrate.sections.main.header" = "Select below the profiles from old versions of Passepartout that you want to import. In case your profiles are stored on iCloud, they may take a while to synchronize. If you do not see them now, please come back later.";
|
||||
"views.migrate.alerts.delete.message" = "Do you want to discard these profiles? You will not be able to recover them later.\n\n%@";
|
||||
|
||||
"views.donate.title" = "Make a donation";
|
||||
|
|
|
@ -179,6 +179,16 @@ extension View {
|
|||
modifier(ThemeAnimationModifier(value: value, category: category))
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
public func themeEmpty(if isEmpty: Bool, message: String) -> some View {
|
||||
if !isEmpty {
|
||||
self
|
||||
} else {
|
||||
Text(message)
|
||||
.themeEmptyMessage()
|
||||
}
|
||||
}
|
||||
|
||||
public func themeProgress(if isProgressing: Bool) -> some View {
|
||||
modifier(ThemeProgressViewModifier(isProgressing: isProgressing) {
|
||||
EmptyView()
|
||||
|
|
Loading…
Reference in New Issue