Duplicate profile from context menu

This commit is contained in:
Davide De Rosa 2022-04-26 22:33:16 +02:00
parent 1ea380312a
commit 03fdab4833
7 changed files with 49 additions and 6 deletions

View File

@ -443,6 +443,8 @@ internal enum L10n {
internal static let domain = L10n.tr("Localizable", "global.strings.domain") internal static let domain = L10n.tr("Localizable", "global.strings.domain")
/// Domains /// Domains
internal static let domains = L10n.tr("Localizable", "global.strings.domains") internal static let domains = L10n.tr("Localizable", "global.strings.domains")
/// Duplicate
internal static let duplicate = L10n.tr("Localizable", "global.strings.duplicate")
/// Enabled /// Enabled
internal static let enabled = L10n.tr("Localizable", "global.strings.enabled") internal static let enabled = L10n.tr("Localizable", "global.strings.enabled")
/// Encryption /// Encryption

View File

@ -176,6 +176,10 @@ extension View {
"trash.fill" "trash.fill"
} }
var themeDuplicateImage: String {
"doc.on.doc.fill"
}
var themeRenameProfileImage: String { var themeRenameProfileImage: String {
"highlighter" "highlighter"
// "character.cursor.ibeam" // "character.cursor.ibeam"

View File

@ -108,6 +108,12 @@ extension OrganizerView {
header: header, header: header,
isActive: profileManager.isActiveProfile(header.id) isActive: profileManager.isActiveProfile(header.id)
) )
}.contextMenu {
Button {
duplicateProfile(withId: header.id)
} label: {
Label(L10n.Global.Strings.duplicate, systemImage: themeDuplicateImage)
}
}.themeTextButtonStyle() }.themeTextButtonStyle()
} }
@ -187,6 +193,10 @@ extension OrganizerView.ProfilesList {
profileManager.removeProfiles(withIds: toDelete) profileManager.removeProfiles(withIds: toDelete)
} }
private func duplicateProfile(withId id: UUID) {
profileManager.duplicateProfile(withId: id)
}
private func performMigrationsIfNeeded() { private func performMigrationsIfNeeded() {
Task { Task {

View File

@ -12,6 +12,7 @@
"global.strings.ok" = "OK"; "global.strings.ok" = "OK";
"global.strings.save" = "Save"; "global.strings.save" = "Save";
"global.strings.rename" = "Rename"; "global.strings.rename" = "Rename";
"global.strings.duplicate" = "Duplicate";
"global.strings.add" = "Add"; "global.strings.add" = "Add";
"global.strings.default" = "Default"; "global.strings.default" = "Default";
"global.strings.name" = "Name"; "global.strings.name" = "Name";

View File

@ -116,7 +116,7 @@ public class AppManager: ObservableObject {
migrated.forEach { migrated.forEach {
var profile = $0 var profile = $0
if profileManager.isExistingProfile(withName: profile.header.name) { if profileManager.isExistingProfile(withName: profile.header.name) {
profile = profile.renamedUniquely() profile = profile.renamedUniquely(withLastUpdate: true)
} }
profileManager.saveProfile(profile, isActive: nil) profileManager.saveProfile(profile, isActive: nil)
} }

View File

@ -69,15 +69,24 @@ extension Profile {
} }
extension Profile.Header { extension Profile.Header {
public func withNewId() -> Self {
Profile.Header(
uuid: .init(),
name: name,
providerName: providerName,
lastUpdate: lastUpdate
)
}
public func renamed(to newName: String) -> Self { public func renamed(to newName: String) -> Self {
var header = self var header = self
header.name = newName header.name = newName
return header return header
} }
public func renamedUniquely() -> Self { public func renamedUniquely(withLastUpdate: Bool) -> Self {
let suffix: String let suffix: String
if let lastUpdate = lastUpdate { if withLastUpdate, let lastUpdate = lastUpdate {
suffix = lastUpdate.timestamp suffix = lastUpdate.timestamp
} else { } else {
guard let leadingUUID = id.uuidString.components(separatedBy: "-").first else { guard let leadingUUID = id.uuidString.components(separatedBy: "-").first else {
@ -92,15 +101,21 @@ extension Profile.Header {
} }
extension Profile { extension Profile {
public func withNewId() -> Self {
var profile = self
profile.header = profile.header.withNewId()
return profile
}
public func renamed(to newName: String) -> Self { public func renamed(to newName: String) -> Self {
var profile = self var profile = self
profile.header = profile.header.renamed(to: newName) profile.header = profile.header.renamed(to: newName)
return profile return profile
} }
public func renamedUniquely() -> Self { public func renamedUniquely(withLastUpdate: Bool) -> Self {
var profile = self var profile = self
profile.header = profile.header.renamedUniquely() profile.header = profile.header.renamedUniquely(withLastUpdate: withLastUpdate)
return profile return profile
} }
} }

View File

@ -237,6 +237,17 @@ extension ProfileManager {
let ids = Array(allHeaders.keys) let ids = Array(allHeaders.keys)
removeProfiles(withIds: ids) removeProfiles(withIds: ids)
} }
public func duplicateProfile(withId id: UUID) {
guard let source = profile(withId: id) else {
return
}
let copy = source
.renamedUniquely(withLastUpdate: false)
.withNewId()
saveProfile(copy, isActive: nil)
}
public func persist() { public func persist() {
pp_log.info("Persisting profiles") pp_log.info("Persisting profiles")
@ -391,7 +402,7 @@ extension ProfileManager {
// headers.removeFirst() // headers.removeFirst()
headers.forEach { dupHeader in headers.forEach { dupHeader in
let uniqueHeader = dupHeader.renamedUniquely() let uniqueHeader = dupHeader.renamedUniquely(withLastUpdate: true)
pp_log.debug("Renaming duplicate profile \(dupHeader.logDescription) to \(uniqueHeader.logDescription)") pp_log.debug("Renaming duplicate profile \(dupHeader.logDescription) to \(uniqueHeader.logDescription)")
guard var uniqueProfile = profile(withId: uniqueHeader.id) else { guard var uniqueProfile = profile(withId: uniqueHeader.id) else {
pp_log.warning("Skipping profile \(dupHeader.logDescription) renaming, not found") pp_log.warning("Skipping profile \(dupHeader.logDescription) renaming, not found")