aix 0.7.1-alpha.1 → 0.7.1-alpha.2

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.
Files changed (2) hide show
  1. package/ios/HybridAix.swift +46 -22
  2. package/package.json +1 -1
@@ -134,6 +134,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
134
134
  blankView = nil
135
135
  cells.removeAllObjects()
136
136
  lastReportedBlankViewSize = (size: .zero, index: 0)
137
+ lastCalculatedBlankSize = 0
137
138
  }
138
139
  }
139
140
 
@@ -230,11 +231,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
230
231
  private var boundsObservation: NSKeyValueObservation?
231
232
 
232
233
  // MARK: - Context References (weak to avoid retain cycles)
233
- weak var blankView: HybridAixCellView? = nil {
234
- didSet {
235
- // Could add observers or callbacks here when blank view changes
236
- }
237
- }
234
+ weak var blankView: HybridAixCellView? = nil
238
235
 
239
236
  weak var composerView: HybridAixComposer? = nil
240
237
 
@@ -324,33 +321,58 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
324
321
  return 0
325
322
  }
326
323
 
324
+ /// Cache the last successfully calculated blank size to avoid jumps when cells are temporarily missing
325
+ private var lastCalculatedBlankSize: CGFloat = 0
326
+
327
327
  private func calculateBlankSize(keyboardHeight: CGFloat, additionalContentInsetBottom: CGFloat) -> CGFloat {
328
- guard let scrollView, let blankView else { return 0 }
328
+ guard let scrollView, let blankView else { return lastCalculatedBlankSize }
329
329
 
330
+ let blankViewIndex = Int(blankView.index)
331
+ let endIndex = blankViewIndex - 1
330
332
  let startIndex: Int
331
- let endIndex = Int(blankView.index) - 1
333
+
332
334
  if let penultimateCellIndex {
333
- startIndex = Int(penultimateCellIndex)
335
+ let penultimate = Int(penultimateCellIndex)
336
+ // Ensure penultimateCellIndex is valid (not beyond current cells)
337
+ // If penultimateCellIndex >= blankViewIndex, it means props are out of sync with cells
338
+ if penultimate >= blankViewIndex {
339
+ // Props are ahead of cells - use previous behavior (last cell before blank)
340
+ startIndex = endIndex
341
+ } else {
342
+ startIndex = penultimate
343
+ }
334
344
  } else {
335
345
  startIndex = endIndex
336
346
  }
337
347
 
338
348
  var cellsBeforeBlankViewHeight: CGFloat = 0
339
- if startIndex <= endIndex {
349
+ var hasMissingCells = false
350
+ if startIndex >= 0 && startIndex <= endIndex {
340
351
  for i in startIndex...endIndex {
341
- if let cell = getCell(index: i) {
352
+ if let cell = getCell(index: i) {
342
353
  cellsBeforeBlankViewHeight += cell.view.frame.height
354
+ } else {
355
+ hasMissingCells = true
343
356
  }
344
357
  }
345
358
  }
346
359
 
347
- let blankViewHeight = blankView.view.frame.height
360
+ // If any required cells are missing, return the last known good value to avoid jumps
361
+ // When all cells register, we'll calculate correctly
362
+ if hasMissingCells {
363
+ return lastCalculatedBlankSize
364
+ }
348
365
 
349
- // Calculate visible area above all bottom chrome (keyboard, composer, additional insets)
350
- // The blank size fills the remaining space so the last message can scroll to the top
366
+ let blankViewHeight = blankView.view.frame.height
351
367
  let visibleAreaHeight = scrollView.bounds.height - keyboardHeight - composerHeight - additionalContentInsetBottom
352
368
  let inset = visibleAreaHeight - blankViewHeight - cellsBeforeBlankViewHeight
353
- return max(0, inset)
369
+
370
+ // Cache this value for when cells are temporarily missing
371
+ lastCalculatedBlankSize = inset
372
+
373
+ // Return raw inset value (can be negative when cells are taller than visible area)
374
+ // The clamping happens in contentInsetBottom to ensure total inset is never negative
375
+ return inset
354
376
  }
355
377
 
356
378
  /// Calculate the blank size - the space needed to push content up
@@ -362,7 +384,9 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
362
384
  /// The content inset for the bottom of the scroll view
363
385
 
364
386
  private func calculateContentInsetBottom(keyboardHeight: CGFloat, blankSize: CGFloat, additionalContentInsetBottom: CGFloat) -> CGFloat {
365
- return blankSize + keyboardHeight + composerHeight + additionalContentInsetBottom
387
+ // Clamp to 0 here instead of in calculateBlankSize to preserve correct math
388
+ // when keyboard is open and blankSize goes negative
389
+ return max(0, blankSize + keyboardHeight + composerHeight + additionalContentInsetBottom)
366
390
  }
367
391
  var contentInsetBottom: CGFloat {
368
392
  return calculateContentInsetBottom(keyboardHeight: self.keyboardHeight, blankSize: self.blankSize, additionalContentInsetBottom: self.additionalContentInsetBottom)
@@ -596,9 +620,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
596
620
 
597
621
  func reportBlankViewSizeChange(size: CGSize, index: Int) {
598
622
  let didAlreadyUpdate = size.height == lastReportedBlankViewSize.size.height && size.width == lastReportedBlankViewSize.size.width && index == lastReportedBlankViewSize.index
599
- if didAlreadyUpdate {
600
- return
601
- }
623
+ if didAlreadyUpdate { return }
602
624
 
603
625
  lastReportedBlankViewSize = (size: size, index: index)
604
626
 
@@ -630,14 +652,16 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
630
652
  func registerCell(_ cell: HybridAixCellView) {
631
653
  cells.setObject(cell, forKey: NSNumber(value: cell.index))
632
654
 
633
- // If this cell is marked as last, update our blank view reference
634
655
  if cell.isLast {
635
656
  blankView = cell
636
657
 
637
- // Trigger initial setup - handleLayoutChange may have already fired before
638
- // the cell was in window, so we need to report here to ensure setup happens
658
+ // Trigger initial setup
639
659
  let currentSize = cell.view.bounds.size
640
660
  reportBlankViewSizeChange(size: currentSize, index: Int(cell.index))
661
+ } else if didScrollToEndInitially {
662
+ // A content cell registered after initial setup - recalculate insets
663
+ // since this cell's height affects the blank size calculation
664
+ applyAllInsets()
641
665
  }
642
666
  }
643
667
 
@@ -647,7 +671,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
647
671
  // If this was our blank view, clear it
648
672
  if blankView === cell {
649
673
  blankView = nil
650
- }
674
+ }
651
675
  }
652
676
 
653
677
  func registerComposerView(_ composerView: HybridAixComposer) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aix",
3
- "version": "0.7.1-alpha.1",
3
+ "version": "0.7.1-alpha.2",
4
4
  "author": "Fernando Rojo",
5
5
  "repository": {
6
6
  "type": "git",