Skip to content

Commit

Permalink
Add table view of password rules
Browse files Browse the repository at this point in the history
  • Loading branch information
grgar committed Oct 13, 2023
1 parent 87b5985 commit 6884131
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 22 deletions.
10 changes: 6 additions & 4 deletions PI.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
37 changes: 28 additions & 9 deletions Rule/PasswordRuleChips.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,39 @@ import SwiftUI

struct PasswordRuleChips: View {
let rule: Rule

struct Length: View {
let min: Int?
let max: Int?

var body: some View {
HStack(alignment: .firstTextBaseline) {
var body: some View {
HStack(spacing: 0) {
if let minLength = rule.minLength {
Image(systemName: "\(minLength).square")
}
if rule.minLength != nil, let max = rule.maxLength, max <= 50 {
Text("")
if let min {
Image(systemName: "\(min).square")
} else {
Image(systemName: "square")
.foregroundStyle(.quaternary)
}
if let maxLength = rule.maxLength, maxLength <= 50 {
Image(systemName: "\(maxLength).square")
Text("")
if let max {
if max <= 50 {
Image(systemName: "\(max).square")
} else {
Text(max.description)
.font(.caption)
.padding(.horizontal, 2)
}
} else {
Image(systemName: "square")
.foregroundStyle(.quaternary)
}
}
}
}

var body: some View {
HStack(alignment: .firstTextBaseline) {
Length(min: rule.minLength, max: rule.maxLength)
ForEach(rule.required.sorted()) { set in
if let symbol = set.symbol {
Image(systemName: symbol)
Expand Down
71 changes: 62 additions & 9 deletions Rule/PasswordRules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,15 @@ struct PasswordRules: View {
#endif

@ScaledMetric(wrappedValue: 24, relativeTo: .body) private var faviconHeight

@AppStorage("showFavicon") private var showFavicon = true

@State private var sortOrder = [KeyPathComparator(\Rule.id)]

var body: some View {
let responses = response
.sorted { $0.id.lexicographicallyPrecedes($1.id) }
.filter { searchText == "" || $0.id.localizedCaseInsensitiveContains(searchText) }
.sorted(using: sortOrder)
let isError = Binding(get: {
error != nil
}, set: {
Expand Down Expand Up @@ -83,22 +87,67 @@ struct PasswordRules: View {
}
.listStyle(.plain)
} else {
Table(of: Rule.self) {
TableColumn("Domain") { domain in
Favicon(domain: domain.id)
Table(of: Rule.self, sortOrder: $sortOrder) {
TableColumn("Domain", value: \.id) { domain in
HStack {
if showFavicon {
Favicon(domain: domain.id)
.frame(maxHeight: faviconHeight)
}
Text(domain.id)
}
}

TableColumn("Length", value: \.sumLength) { domain in
if showFavicon {
PasswordRuleChips.Length(min: domain.minLength, max: domain.maxLength)
.symbolVariant(.fill)
.symbolRenderingMode(.hierarchical)
.font(.title)
} else {
Text("\(domain.minLength?.description ?? "?")\(domain.maxLength?.description ?? "?")")
}
}
.width(min: 48, ideal: 48, max: 64)

TableColumn("Required") { domain in
HStack {
ForEach(domain.required.sorted()) { set in
if let symbol = set.symbol {
Image(systemName: symbol)
} else {
Text(set.description)
}
}
}
}
TableColumn("Allowed") { domain in
HStack {
ForEach(domain.allowed.sorted()) { set in
if let symbol = set.symbol {
Image(systemName: symbol)
} else {
Text(set.description)
}
}
}
}
TableColumn("id", value: \.id)
TableColumn("id", value: \.id)
TableColumn("id", value: \.id)
TableColumn("id", value: \.id)
TableColumn("id", value: \.id)
} rows: {
ForEach(responses) { rule in
TableRow(rule)
}
}
}
}
.toolbar {
ToolbarItemGroup(placement: .automatic) {
Picker("Favicon", selection: $showFavicon) {
Label("Show", systemImage: "checklist.unchecked").tag(true)
Label("Hide", systemImage: "list.bullet").tag(false)
}
.pickerStyle(.segmented)
}
}
.searchable(text: $searchText, prompt: Text("Search Domains"))
.alert(
isPresented: isError,
Expand Down Expand Up @@ -154,3 +203,7 @@ struct RefreshButton: View {
PasswordRules()
}
}

#Preview("Table", traits: .fixedLayout(width: 960, height: 640)) {
PasswordRules()
}
2 changes: 2 additions & 0 deletions Rule/Rule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ struct Rule: Identifiable {
var maxLength: Int?
var required = Set<PasswordCharacter>()
var allowed = Set<PasswordCharacter>()

var sumLength: Int { (minLength ?? 0) + (maxLength ?? 0) }

enum PasswordCharacter: LosslessStringConvertible, Hashable, CaseIterable, Comparable, Identifiable {
case lower, upper, digit, special, unicode, other(Set<Character>)
Expand Down

0 comments on commit 6884131

Please sign in to comment.