APNSettingsView.swift 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. // LoopFollow
  2. // APNSettingsView.swift
  3. import SwiftUI
  4. struct APNSettingsView: View {
  5. @State private var keyId: String = Storage.shared.lfKeyId.value
  6. @State private var apnsKey: String = Storage.shared.lfApnsKey.value
  7. private var keyIdValid: Bool {
  8. APNsCredentialValidator.isValidKeyId(keyId)
  9. }
  10. private var apnsKeyValid: Bool {
  11. APNsCredentialValidator.isValidApnsKey(apnsKey)
  12. }
  13. var body: some View {
  14. Form {
  15. Section(header: Text("LoopFollow APNs Credentials")) {
  16. VStack(alignment: .leading, spacing: 4) {
  17. HStack {
  18. Text("APNS Key ID")
  19. TogglableSecureInput(
  20. placeholder: "Enter APNS Key ID",
  21. text: $keyId,
  22. style: .singleLine
  23. )
  24. if !keyId.isEmpty {
  25. Image(systemName: keyIdValid ? "checkmark.circle.fill" : "exclamationmark.triangle.fill")
  26. .foregroundColor(keyIdValid ? .green : .orange)
  27. .accessibilityLabel(keyIdValid ? "Valid Key ID" : "Invalid Key ID")
  28. }
  29. }
  30. if !keyId.isEmpty, !keyIdValid {
  31. Text("Key ID must be exactly 10 uppercase letters or digits.")
  32. .font(.caption)
  33. .foregroundColor(.orange)
  34. }
  35. }
  36. VStack(alignment: .leading, spacing: 4) {
  37. HStack {
  38. Text("APNS Key")
  39. Spacer()
  40. if !apnsKey.isEmpty {
  41. Image(systemName: apnsKeyValid ? "checkmark.circle.fill" : "exclamationmark.triangle.fill")
  42. .foregroundColor(apnsKeyValid ? .green : .orange)
  43. .accessibilityLabel(apnsKeyValid ? "Valid APNs key" : "Invalid APNs key")
  44. }
  45. }
  46. TogglableSecureInput(
  47. placeholder: "Paste APNS Key",
  48. text: $apnsKey,
  49. style: .multiLine
  50. )
  51. .frame(minHeight: 110)
  52. if !apnsKey.isEmpty, !apnsKeyValid {
  53. Text("Paste the full .p8 contents — must include the BEGIN PRIVATE KEY and END PRIVATE KEY lines.")
  54. .font(.caption)
  55. .foregroundColor(.orange)
  56. }
  57. }
  58. }
  59. }
  60. .onChange(of: keyId) { newValue in
  61. Storage.shared.lfKeyId.value = newValue
  62. }
  63. .onChange(of: apnsKey) { newValue in
  64. let apnsService = LoopAPNSService()
  65. let normalized = apnsService.validateAndFixAPNSKey(newValue)
  66. Storage.shared.lfApnsKey.value = normalized
  67. // Reflect normalization (whitespace fixes etc.) in the field so the
  68. // green badge appears when paste added stray whitespace around an
  69. // otherwise-valid key.
  70. if normalized != newValue {
  71. apnsKey = normalized
  72. }
  73. }
  74. .preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
  75. .navigationTitle("APN")
  76. .navigationBarTitleDisplayMode(.inline)
  77. }
  78. }