damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

ErrorView.swift (4729B)


      1 //
      2 //  ErrorView.swift
      3 //  damus
      4 //
      5 //  Created by Daniel D'Aquino on 2025-01-08.
      6 //
      7 
      8 import SwiftUI
      9 
     10 /// A generic user-presentable error view
     11 ///
     12 /// Use this to handle and display errors to the user when it does not make sense to create a custom error view.
     13 /// This includes good error handling UX practices, such as:
     14 /// - Clear description
     15 /// - Actionable advice for the user on what to do next.
     16 /// - One-click support contact options
     17 struct ErrorView: View {
     18     let damus_state: DamusState?
     19     let error: UserPresentableError
     20     
     21     @Environment(\.dismiss) var dismiss
     22     
     23     var body: some View {
     24         VStack(spacing: 6) {
     25             Image(systemName: "exclamationmark.circle")
     26                 .resizable()
     27                 .frame(width: 30, height: 30)
     28                 .foregroundStyle(.red)
     29                 .accessibilityHidden(true)
     30             Text("Oops!", comment: "Heading for an error screen")
     31                 .font(.title)
     32                 .bold()
     33                 .padding(.bottom, 10)
     34                 .accessibilityHeading(.h1)
     35             Text(error.user_visible_description)
     36                 .foregroundStyle(.secondary)
     37             
     38             VStack(alignment: .leading, spacing: 6) {
     39                 HStack(spacing: 5) {
     40                     Image(systemName: "sparkles")
     41                         .accessibilityHidden(true)
     42                     Text("Advice", comment: "Heading for some advice text to help the user with an error")
     43                         .font(.headline)
     44                         .accessibilityHeading(.h3)
     45                 }
     46                 Text(error.tip)
     47             }
     48             .padding()
     49             .background(Color.secondary.opacity(0.2))
     50             .cornerRadius(10)
     51             .padding(.vertical, 30)
     52             
     53             Spacer()
     54             
     55             if let damus_state, damus_state.is_privkey_user {
     56                 Button(action: {
     57                     damus_state.nav.push(route: .DMChat(dms: .init(our_pubkey: damus_state.keypair.pubkey, pubkey: Constants.SUPPORT_PUBKEY)))
     58                     dismiss()
     59                 }, label: {
     60                     Text("Contact support via DMs", comment: "Button label to contact support from an error screen")
     61                 })
     62                 .padding(.vertical, 20)
     63             }
     64             Text("Contact support via email at [support@damus.io](mailto:support@damus.io)", comment: "Text to contact support via email")
     65                 .font(.caption)
     66                 .foregroundStyle(.secondary)
     67         }
     68         .padding(20)
     69         .padding(.top, 20)
     70     }
     71     
     72     /// An error that is displayed to the user, and can be sent to the Developers as well.
     73     struct UserPresentableError {
     74         /// The description of the error to be shown to the user
     75         ///
     76         /// **Requirements:**
     77         /// - This should not be technical. It should use accessible language
     78         /// - Should be localized
     79         /// - It should try to explain the user what happened, and — if possible — why.
     80         let user_visible_description: String
     81         
     82         /// Helpful tip/advice to the user, to help them overcome the error
     83         ///
     84         /// **Requirements:**
     85         /// - Should provide actionable advice to the user
     86         /// - This should not be overly technical
     87         /// - Should be localized
     88         /// - Should NOT include support contact (The view that will display this will already include support contact options)
     89         ///
     90         /// **Implementation notes:**
     91         /// - This is NOT an optional value, because part of good UX is making sure error messages are actionable, which is something that is often forgotten. It's not uncommon for error messages to be written in vague, technical, and/or unactionable terms, but this is when the user needs help the most. And so this field is made mandatory to force developers to write actionable content to the user
     92         let tip: String
     93         
     94         /// Technical information about the error, which will be sendable to developers
     95         ///
     96         /// Note: This is still unutilized, but this will be used in the future.
     97         ///
     98         /// **Requirements**
     99         /// - Should never include any sensitive info
    100         /// - Should be in English. The developers are the main audience.
    101         /// - Should include helping info, such as context in which the error happens.
    102         /// - Should be technical
    103         let technical_info: String?
    104         
    105     }
    106 }
    107 
    108 
    109 
    110 #Preview {
    111     ErrorView(
    112         damus_state: test_damus_state,
    113         error: .init(
    114             user_visible_description: "We are still too early",
    115             tip: "Stay humble, keep building, stack sats",
    116             technical_info: nil
    117         )
    118     )
    119 }