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:
Davide 2024-11-17 14:02:40 +01:00 committed by GitHub
parent 1ddea2ad5e
commit c93a43702c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 68 additions and 46 deletions

View File

@ -46,8 +46,9 @@ extension MigrateContentView {
private var selection: Set<UUID> = []
var body: some View {
List {
profilesSection
VStack {
messageView
profilesList
}
.themeNavigationDetail()
.toolbar {
@ -61,21 +62,17 @@ 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 {
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 {
@ -90,9 +87,18 @@ private extension MigrateContentView.ListView {
}
}
} header: {
actionsHeader
EditProfilesButton(isEditing: $isEditing, selection: $selection) {
onDelete(profiles.filter {
selection.contains($0.id)
})
// disable isEditing after confirmation
}
.textCase(.none)
}
}
.listStyle(.plain)
.disabled(!step.canSelect)
.themeEmpty(if: isEmpty, message: Strings.Views.Migrate.noProfiles)
}
func isIncludedBinding(for profileId: UUID) -> Binding<Bool> {

View File

@ -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> {

View File

@ -45,6 +45,12 @@ extension MigrateView {
}
}
extension MigrateViewStep {
var isReady: Bool {
![.initial, .fetching].contains(self)
}
}
extension MigrateView.Model {
var visibleProfiles: [MigratableProfile] {
profiles

View File

@ -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,

View File

@ -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.")
}
}
}

View File

@ -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";

View File

@ -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()