- RosettaListView: core UIScrollView с ручным node management, visibility culling (500pt), CADisplayLink анимации - RosettaListNode: базовый класс ячейки с animation state (height/alpha/spring) - RosettaListItem: протокол с async layout closure (Telegram asyncLayout pattern) - RosettaTransactionQueue: FIFO сериализатор обновлений - ChatMessageListItem: bridge ChatMessage → RosettaListItem (WIP, не подключен) Следующий шаг: NativeMessageCell → NativeMessageView (UIView) рефакторинг Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
140 lines
3.9 KiB
Swift
140 lines
3.9 KiB
Swift
import UIKit
|
|
|
|
// MARK: - Layout Params
|
|
|
|
/// Parameters for layout calculation (screen width, insets).
|
|
/// Telegram equivalent: `ListViewItemLayoutParams`.
|
|
struct RosettaListLayoutParams: Sendable {
|
|
let width: CGFloat
|
|
let leftInset: CGFloat
|
|
let rightInset: CGFloat
|
|
}
|
|
|
|
// MARK: - Node Layout
|
|
|
|
/// Calculated layout result — content size + insets.
|
|
/// Telegram equivalent: `ListViewItemNodeLayout`.
|
|
struct RosettaListNodeLayout {
|
|
let contentSize: CGSize
|
|
let insets: UIEdgeInsets
|
|
|
|
var size: CGSize {
|
|
CGSize(
|
|
width: contentSize.width + insets.left + insets.right,
|
|
height: contentSize.height + insets.top + insets.bottom
|
|
)
|
|
}
|
|
}
|
|
|
|
// MARK: - Update Animation
|
|
|
|
/// Animation type for node updates.
|
|
/// Telegram equivalent: `ListViewItemUpdateAnimation`.
|
|
enum RosettaListUpdateAnimation {
|
|
case none
|
|
case system(duration: Double, transition: ControlledTransitionConfig)
|
|
}
|
|
|
|
struct ControlledTransitionConfig {
|
|
let duration: Double
|
|
let curve: AnimationCurve
|
|
|
|
enum AnimationCurve {
|
|
case spring(damping: CGFloat)
|
|
case easeInOut
|
|
}
|
|
|
|
static let `default` = ControlledTransitionConfig(
|
|
duration: 0.4,
|
|
curve: .spring(damping: 0.78)
|
|
)
|
|
}
|
|
|
|
// MARK: - Direction Hint
|
|
|
|
/// Hint for insertion/deletion animation direction.
|
|
/// Telegram equivalent: `ListViewItemOperationDirectionHint`.
|
|
enum RosettaListDirectionHint {
|
|
case up
|
|
case down
|
|
}
|
|
|
|
// MARK: - Scroll Position
|
|
|
|
/// Target scroll position for programmatic scrolling.
|
|
/// Telegram equivalent: `ListViewScrollPosition`.
|
|
enum RosettaListScrollPosition {
|
|
case top(CGFloat)
|
|
case center
|
|
case bottom(CGFloat)
|
|
case visible
|
|
}
|
|
|
|
// MARK: - List Item Protocol
|
|
|
|
/// Data model protocol for list items.
|
|
/// Telegram equivalent: `ListViewItem` (ListViewItem.swift:72-106).
|
|
///
|
|
/// Two-phase async layout pattern:
|
|
/// 1. `nodeConfiguredForParams` — creates node + calculates layout on background
|
|
/// 2. Completion delivers ready-to-display node + layout to main thread
|
|
protocol RosettaListItem: AnyObject {
|
|
/// Unique identifier for diffing.
|
|
var id: String { get }
|
|
|
|
/// Estimated height before layout calculation. Used for scroll position
|
|
/// estimation when exact layout isn't ready yet.
|
|
/// Telegram: `approximateHeight` property.
|
|
var approximateHeight: CGFloat { get }
|
|
|
|
/// Reuse identifier for node pool recycling.
|
|
var reuseIdentifier: String { get }
|
|
|
|
/// Create a new node and calculate its layout.
|
|
/// `async` closure schedules work on background queue.
|
|
/// Telegram: `nodeConfiguredForParams` (ListViewItem.swift:72-75).
|
|
func nodeConfiguredForParams(
|
|
async: @escaping (@escaping () -> Void) -> Void,
|
|
params: RosettaListLayoutParams,
|
|
previousItem: RosettaListItem?,
|
|
nextItem: RosettaListItem?,
|
|
completion: @escaping (RosettaListNode, RosettaListNodeLayout) -> Void
|
|
)
|
|
|
|
/// Update an existing (recycled) node with new data.
|
|
/// Telegram: `updateNode` (ListViewItem.swift:78-88).
|
|
func updateNode(
|
|
async: @escaping (@escaping () -> Void) -> Void,
|
|
node: RosettaListNode,
|
|
params: RosettaListLayoutParams,
|
|
previousItem: RosettaListItem?,
|
|
nextItem: RosettaListItem?,
|
|
animation: RosettaListUpdateAnimation,
|
|
completion: @escaping (RosettaListNodeLayout) -> Void
|
|
)
|
|
}
|
|
|
|
// MARK: - Insert/Delete/Update Operations
|
|
|
|
/// Batch operations for list updates.
|
|
/// Telegram equivalent: `ListViewInsertItem`, `ListViewDeleteItem`, `ListViewUpdateItem`.
|
|
|
|
struct RosettaListInsertItem {
|
|
let index: Int
|
|
let previousIndex: Int?
|
|
let item: RosettaListItem
|
|
let directionHint: RosettaListDirectionHint?
|
|
}
|
|
|
|
struct RosettaListDeleteItem {
|
|
let index: Int
|
|
let directionHint: RosettaListDirectionHint?
|
|
}
|
|
|
|
struct RosettaListUpdateItem {
|
|
let index: Int
|
|
let previousIndex: Int
|
|
let item: RosettaListItem
|
|
let directionHint: RosettaListDirectionHint?
|
|
}
|