expo-tvos-search 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ #if os(tvOS)
2
+
3
+ import SwiftUI
4
+
5
+ /// Thread-safe NSCache-backed image cache singleton.
6
+ /// Auto-evicts under memory pressure via NSCache's built-in LRU behavior.
7
+ final class ImageCache {
8
+ static let shared = ImageCache()
9
+
10
+ private let cache = NSCache<NSURL, UIImage>()
11
+
12
+ private init() {
13
+ cache.countLimit = 100
14
+ cache.totalCostLimit = 100 * 1024 * 1024 // 100 MB
15
+ }
16
+
17
+ func image(for url: URL) -> UIImage? {
18
+ cache.object(forKey: url as NSURL)
19
+ }
20
+
21
+ func setImage(_ image: UIImage, for url: URL) {
22
+ let cost = image.cgImage.map { $0.bytesPerRow * $0.height } ?? 0
23
+ cache.setObject(image, forKey: url as NSURL, cost: cost)
24
+ }
25
+ }
26
+
27
+ /// SwiftUI view that loads images with NSCache backing.
28
+ /// Cached images appear instantly (no loading flash).
29
+ /// On failure, renders EmptyView so the parent placeholder shows through.
30
+ struct CachedAsyncImage: View {
31
+ let url: URL
32
+ let contentMode: ContentMode
33
+ let width: CGFloat
34
+ let height: CGFloat
35
+
36
+ @State private var uiImage: UIImage?
37
+ @State private var isLoading = true
38
+
39
+ var body: some View {
40
+ Group {
41
+ if let uiImage = uiImage {
42
+ Image(uiImage: uiImage)
43
+ .resizable()
44
+ .aspectRatio(contentMode: contentMode)
45
+ .frame(width: width, height: height)
46
+ } else if isLoading {
47
+ ProgressView()
48
+ } else {
49
+ // Failure — show nothing, parent placeholder shows through
50
+ EmptyView()
51
+ }
52
+ }
53
+ .frame(width: width, height: height)
54
+ .task(id: url) {
55
+ uiImage = nil
56
+ isLoading = true
57
+
58
+ if let cached = ImageCache.shared.image(for: url) {
59
+ uiImage = cached
60
+ isLoading = false
61
+ return
62
+ }
63
+
64
+ do {
65
+ let (data, _) = try await URLSession.shared.data(from: url)
66
+ if let image = UIImage(data: data) {
67
+ ImageCache.shared.setImage(image, for: url)
68
+ uiImage = image
69
+ }
70
+ } catch {
71
+ // Cancelled or network error — placeholder shows through
72
+ }
73
+ isLoading = false
74
+ }
75
+ }
76
+ }
77
+
78
+ #endif
@@ -8,11 +8,28 @@ public class ExpoTvosSearchModule: Module {
8
8
  private static let maxMarqueeDelay: Double = 60.0
9
9
  private static let maxStringLength = 500
10
10
 
11
+ /// Truncates a string to maxStringLength and emits a validation warning if truncation occurred.
12
+ private static func truncateString(
13
+ _ value: String,
14
+ propName: String,
15
+ view: ExpoTvosSearchView
16
+ ) -> String {
17
+ let truncated = String(value.prefix(maxStringLength))
18
+ if truncated.count < value.count {
19
+ view.onValidationWarning([
20
+ "type": "value_truncated",
21
+ "message": "\(propName) truncated to \(maxStringLength) characters",
22
+ "context": "original length: \(value.count)"
23
+ ])
24
+ }
25
+ return truncated
26
+ }
27
+
11
28
  public func definition() -> ModuleDefinition {
12
29
  Name("ExpoTvosSearch")
13
30
 
14
31
  View(ExpoTvosSearchView.self) {
15
- Events("onSearch", "onSelectItem", "onError", "onValidationWarning")
32
+ Events("onSearch", "onSelectItem", "onError", "onValidationWarning", "onSearchFieldFocused", "onSearchFieldBlurred")
16
33
 
17
34
  Prop("results") { (view: ExpoTvosSearchView, results: [[String: Any]]) in
18
35
  // Limit results array size to prevent memory issues
@@ -41,8 +58,15 @@ public class ExpoTvosSearchModule: Module {
41
58
  }
42
59
 
43
60
  Prop("placeholder") { (view: ExpoTvosSearchView, placeholder: String) in
44
- // Limit placeholder length to prevent layout issues
45
- view.placeholder = String(placeholder.prefix(Self.maxStringLength))
61
+ view.placeholder = Self.truncateString(placeholder, propName: "placeholder", view: view)
62
+ }
63
+
64
+ Prop("searchText") { (view: ExpoTvosSearchView, text: String?) in
65
+ if let text = text {
66
+ view.searchTextProp = Self.truncateString(text, propName: "searchText", view: view)
67
+ } else {
68
+ view.searchTextProp = nil
69
+ }
46
70
  }
47
71
 
48
72
  Prop("isLoading") { (view: ExpoTvosSearchView, isLoading: Bool) in
@@ -96,19 +120,19 @@ public class ExpoTvosSearchModule: Module {
96
120
  }
97
121
 
98
122
  Prop("emptyStateText") { (view: ExpoTvosSearchView, text: String) in
99
- view.emptyStateText = String(text.prefix(Self.maxStringLength))
123
+ view.emptyStateText = Self.truncateString(text, propName: "emptyStateText", view: view)
100
124
  }
101
125
 
102
126
  Prop("searchingText") { (view: ExpoTvosSearchView, text: String) in
103
- view.searchingText = String(text.prefix(Self.maxStringLength))
127
+ view.searchingText = Self.truncateString(text, propName: "searchingText", view: view)
104
128
  }
105
129
 
106
130
  Prop("noResultsText") { (view: ExpoTvosSearchView, text: String) in
107
- view.noResultsText = String(text.prefix(Self.maxStringLength))
131
+ view.noResultsText = Self.truncateString(text, propName: "noResultsText", view: view)
108
132
  }
109
133
 
110
134
  Prop("noResultsHintText") { (view: ExpoTvosSearchView, text: String) in
111
- view.noResultsHintText = String(text.prefix(Self.maxStringLength))
135
+ view.noResultsHintText = Self.truncateString(text, propName: "noResultsHintText", view: view)
112
136
  }
113
137
 
114
138
  Prop("textColor") { (view: ExpoTvosSearchView, colorHex: String?) in