field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

TeraTodayCardPresentation.swift (2665B)


      1 import Foundation
      2 
      3 enum TeraTodayCardPresentation {
      4   case feed
      5   case detail
      6 
      7   static let excerptCharacters = 640
      8   static let excerptBytes = 4096
      9   static let labelCharacters = 160
     10   static let labelBytes = 1024
     11   static let thumbnails = 3
     12 
     13   func content(_ value: String) -> String {
     14     self == .detail ? value : Self.excerpt(value, characters: Self.excerptCharacters, bytes: Self.excerptBytes)
     15   }
     16 
     17   func label(_ value: String) -> String {
     18     self == .detail ? value : Self.excerpt(value, characters: Self.labelCharacters, bytes: Self.labelBytes)
     19   }
     20 
     21   func media(_ values: [TeraMediaReference]) -> [TeraMediaReference] {
     22     self == .detail ? values : Array(values.prefix(Self.thumbnails))
     23   }
     24 
     25   func accessibility(_ card: TeraTodayCard, locale: Locale = .current, timeZone: TimeZone = .current) -> String {
     26     if self == .detail {
     27       return card.accessibilitySummary(locale: locale, timeZone: timeZone)
     28     }
     29     var parts = [card.type.label, "by \(label(card.authorName))"]
     30     if let title = card.title {
     31       parts.append(label(title))
     32     }
     33     parts.append(content(card.content))
     34     if let timing = card.calendarTiming {
     35       parts.append(TeraCalendarPresentation(locale: locale, timeZone: timeZone).summary(timing))
     36     }
     37     let food = TeraFoodPresentation(locale: locale)
     38     if let price = food.price(card) {
     39       parts.append(label(price))
     40     }
     41     if let quantity = food.quantity(card) {
     42       parts.append(label(quantity))
     43     }
     44     if card.lifecycle != .active {
     45       parts.append(card.lifecycle.rawValue)
     46     }
     47     if let operation = card.localOperationState {
     48       parts.append(label(operation))
     49     }
     50     return parts.joined(separator: ", ")
     51   }
     52 
     53   /// Bound bytes before walking graphemes: a single combining sequence can be
     54   /// arbitrarily long. Drop an incomplete final cluster rather than split it.
     55   static func excerpt(_ value: String, characters: Int, bytes: Int) -> String {
     56     precondition(characters > 0 && bytes >= 3)
     57     let prefix = Array(value.utf8.prefix(bytes + 1))
     58     let byteTruncated = prefix.count > bytes
     59     var bounded: String
     60     if byteTruncated {
     61       var encoded = Array(prefix.prefix(bytes - 3))
     62       while String(bytes: encoded, encoding: .utf8) == nil {
     63         encoded.removeLast()
     64       }
     65       bounded = String(bytes: encoded, encoding: .utf8) ?? ""
     66       if !bounded.isEmpty {
     67         bounded.removeLast()
     68       }
     69     } else {
     70       bounded = value
     71     }
     72     let clusterPrefix = bounded.prefix(characters + 1)
     73     let truncated = byteTruncated || clusterPrefix.count > characters
     74     return truncated ? String(clusterPrefix.prefix(characters - 1)) + "…" : String(clusterPrefix)
     75   }
     76 }