Add stream tests
This commit is contained in:
parent
176e4feaf8
commit
8a2861b37f
|
@ -7,8 +7,6 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
065FA1FE29866CD600012EA0 /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = 065FA1FD29866CD600012EA0 /* LRUCache */; };
|
||||
065FA20A298675BA00012EA0 /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = 065FA209298675BA00012EA0 /* LRUCache */; };
|
||||
069709A5298C8545006E4CB5 /* Atkinson-Hyperlegible-Regular-102.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 069709A3298C8545006E4CB5 /* Atkinson-Hyperlegible-Regular-102.ttf */; };
|
||||
069709A8298C87B5006E4CB5 /* OpenDyslexic-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 069709A7298C87B5006E4CB5 /* OpenDyslexic-Regular.otf */; };
|
||||
069709AA298C9AD7006E4CB5 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 069709A9298C9AD7006E4CB5 /* AboutView.swift */; };
|
||||
|
@ -93,13 +91,10 @@
|
|||
9FD542E72962D2FF0045321A /* Lists in Frameworks */ = {isa = PBXBuildFile; productRef = 9FD542E62962D2FF0045321A /* Lists */; };
|
||||
9FE151A6293C90F900E9683D /* IconSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE151A5293C90F900E9683D /* IconSelectorView.swift */; };
|
||||
9FE3DB57296FEFCA00628CB0 /* AppAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 9FE3DB56296FEFCA00628CB0 /* AppAccount */; };
|
||||
9FF614472B2EDCE500F7B0E6 /* GiphyUISDK in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, maccatalyst, ); productRef = 9FF614462B2EDCE500F7B0E6 /* GiphyUISDK */; };
|
||||
9FF614492B2EDCEC00F7B0E6 /* GiphyUISDK in Frameworks */ = {isa = PBXBuildFile; productRef = 9FF614482B2EDCEC00F7B0E6 /* GiphyUISDK */; };
|
||||
9FFF677C299B7B2C00FE700A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677B299B7B2C00FE700A /* Notifications */; };
|
||||
9FFF677E299B7D2800FE700A /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677D299B7D2800FE700A /* Status */; };
|
||||
9FFF6780299B7D2B00FE700A /* DesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677F299B7D2B00FE700A /* DesignSystem */; };
|
||||
9FFF6782299B7D3A00FE700A /* Account in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF6781299B7D3A00FE700A /* Account */; };
|
||||
9FFF6784299B7D4400FE700A /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF6783299B7D4400FE700A /* LRUCache */; };
|
||||
C9B22677297F6C2E001F9EFE /* ContentSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B22676297F6C2E001F9EFE /* ContentSettingsView.swift */; };
|
||||
D08A9C3529956CFA00204A4A /* SwipeActionsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08A9C3429956CFA00204A4A /* SwipeActionsSettingsView.swift */; };
|
||||
DA0B24FB2A6876D50045BDD7 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = DA0B24FA2A6876D50045BDD7 /* SFSafeSymbols */; };
|
||||
|
@ -265,7 +260,6 @@
|
|||
9FFF677C299B7B2C00FE700A /* Notifications in Frameworks */,
|
||||
9F7D93942980063100EE6B7A /* AppAccount in Frameworks */,
|
||||
9FFF6780299B7D2B00FE700A /* DesignSystem in Frameworks */,
|
||||
9FFF6784299B7D4400FE700A /* LRUCache in Frameworks */,
|
||||
9F2A5428296AB683009B2D7C /* Models in Frameworks */,
|
||||
9FFF6782299B7D3A00FE700A /* Account in Frameworks */,
|
||||
9F2A5426296AB67E009B2D7C /* KeychainSwift in Frameworks */,
|
||||
|
@ -281,12 +275,10 @@
|
|||
9FAD85A2297456A400496AB1 /* Env in Frameworks */,
|
||||
9FAD85A0297456A100496AB1 /* Models in Frameworks */,
|
||||
9FAD85A4297456A800496AB1 /* DesignSystem in Frameworks */,
|
||||
9FF614492B2EDCEC00F7B0E6 /* GiphyUISDK in Frameworks */,
|
||||
9FAD859E2974569B00496AB1 /* Account in Frameworks */,
|
||||
9FAD859C2974422700496AB1 /* AppAccount in Frameworks */,
|
||||
9FAD859A297440CB00496AB1 /* KeychainSwift in Frameworks */,
|
||||
9FAD85982974405D00496AB1 /* Status in Frameworks */,
|
||||
065FA20A298675BA00012EA0 /* LRUCache in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -297,7 +289,6 @@
|
|||
9F7335EF29674F7100AFF0BA /* QuickLook.framework in Frameworks */,
|
||||
9F7335ED2967463400AFF0BA /* AVKit.framework in Frameworks */,
|
||||
9F2A540C29699705009B2D7C /* RevenueCat in Frameworks */,
|
||||
065FA1FE29866CD600012EA0 /* LRUCache in Frameworks */,
|
||||
9F2A540E2969A0B0009B2D7C /* StoreKit.framework in Frameworks */,
|
||||
9F55C6902955993C00F94077 /* Explore in Frameworks */,
|
||||
9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */,
|
||||
|
@ -309,7 +300,6 @@
|
|||
9F398AAB2935FFDB00A889F2 /* Models in Frameworks */,
|
||||
9F5E581929545BE700A53960 /* Env in Frameworks */,
|
||||
9F2A540A29699705009B2D7C /* ReceiptParser in Frameworks */,
|
||||
9FF614472B2EDCE500F7B0E6 /* GiphyUISDK in Frameworks */,
|
||||
9F35DB44294F9A7D00B3281A /* Status in Frameworks */,
|
||||
DA0B24FB2A6876D50045BDD7 /* SFSafeSymbols in Frameworks */,
|
||||
9F295540292B6C3400E0E81B /* Timeline in Frameworks */,
|
||||
|
@ -578,7 +568,6 @@
|
|||
9FFF677D299B7D2800FE700A /* Status */,
|
||||
9FFF677F299B7D2B00FE700A /* DesignSystem */,
|
||||
9FFF6781299B7D3A00FE700A /* Account */,
|
||||
9FFF6783299B7D4400FE700A /* LRUCache */,
|
||||
);
|
||||
productName = IceCubesNotifications;
|
||||
productReference = 9F2A5416296AB631009B2D7C /* IceCubesNotifications.appex */;
|
||||
|
@ -605,8 +594,6 @@
|
|||
9FAD859F297456A100496AB1 /* Models */,
|
||||
9FAD85A1297456A400496AB1 /* Env */,
|
||||
9FAD85A3297456A800496AB1 /* DesignSystem */,
|
||||
065FA209298675BA00012EA0 /* LRUCache */,
|
||||
9FF614482B2EDCEC00F7B0E6 /* GiphyUISDK */,
|
||||
);
|
||||
productName = IceCubesShareExtension;
|
||||
productReference = 9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */;
|
||||
|
@ -644,9 +631,7 @@
|
|||
9F2A540929699705009B2D7C /* ReceiptParser */,
|
||||
9F2A540B29699705009B2D7C /* RevenueCat */,
|
||||
9FE3DB56296FEFCA00628CB0 /* AppAccount */,
|
||||
065FA1FD29866CD600012EA0 /* LRUCache */,
|
||||
DA0B24FA2A6876D50045BDD7 /* SFSafeSymbols */,
|
||||
9FF614462B2EDCE500F7B0E6 /* GiphyUISDK */,
|
||||
);
|
||||
productName = IceCubesApp;
|
||||
productReference = 9FBFE639292A715500C250E9 /* Ice Cubes.app */;
|
||||
|
@ -727,9 +712,7 @@
|
|||
packageReferences = (
|
||||
9FAE4ACC29379A5A00772766 /* XCRemoteSwiftPackageReference "keychain-swift" */,
|
||||
9F2A540829699705009B2D7C /* XCRemoteSwiftPackageReference "purchases-ios" */,
|
||||
065FA1FC29866CD600012EA0 /* XCRemoteSwiftPackageReference "LRUCache" */,
|
||||
DA0B24F92A6876D40045BDD7 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
|
||||
9FF614452B2EDCE500F7B0E6 /* XCRemoteSwiftPackageReference "giphy-ios-sdk" */,
|
||||
);
|
||||
productRefGroup = 9FBFE63A292A715500C250E9 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -1410,14 +1393,6 @@
|
|||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
065FA1FC29866CD600012EA0 /* XCRemoteSwiftPackageReference "LRUCache" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/nicklockwood/LRUCache";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.0.0;
|
||||
};
|
||||
};
|
||||
9F2A540829699705009B2D7C /* XCRemoteSwiftPackageReference "purchases-ios" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/RevenueCat/purchases-ios.git";
|
||||
|
@ -1434,14 +1409,6 @@
|
|||
kind = branch;
|
||||
};
|
||||
};
|
||||
9FF614452B2EDCE500F7B0E6 /* XCRemoteSwiftPackageReference "giphy-ios-sdk" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/Giphy/giphy-ios-sdk";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.2.7;
|
||||
};
|
||||
};
|
||||
DA0B24F92A6876D40045BDD7 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/SFSafeSymbols/SFSafeSymbols";
|
||||
|
@ -1453,16 +1420,6 @@
|
|||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
065FA1FD29866CD600012EA0 /* LRUCache */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 065FA1FC29866CD600012EA0 /* XCRemoteSwiftPackageReference "LRUCache" */;
|
||||
productName = LRUCache;
|
||||
};
|
||||
065FA209298675BA00012EA0 /* LRUCache */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 065FA1FC29866CD600012EA0 /* XCRemoteSwiftPackageReference "LRUCache" */;
|
||||
productName = LRUCache;
|
||||
};
|
||||
9F29553F292B6C3400E0E81B /* Timeline */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Timeline;
|
||||
|
@ -1568,16 +1525,6 @@
|
|||
isa = XCSwiftPackageProductDependency;
|
||||
productName = AppAccount;
|
||||
};
|
||||
9FF614462B2EDCE500F7B0E6 /* GiphyUISDK */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 9FF614452B2EDCE500F7B0E6 /* XCRemoteSwiftPackageReference "giphy-ios-sdk" */;
|
||||
productName = GiphyUISDK;
|
||||
};
|
||||
9FF614482B2EDCEC00F7B0E6 /* GiphyUISDK */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 9FF614452B2EDCE500F7B0E6 /* XCRemoteSwiftPackageReference "giphy-ios-sdk" */;
|
||||
productName = GiphyUISDK;
|
||||
};
|
||||
9FFF677B299B7B2C00FE700A /* Notifications */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Notifications;
|
||||
|
@ -1594,11 +1541,6 @@
|
|||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Account;
|
||||
};
|
||||
9FFF6783299B7D4400FE700A /* LRUCache */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 065FA1FC29866CD600012EA0 /* XCRemoteSwiftPackageReference "LRUCache" */;
|
||||
productName = LRUCache;
|
||||
};
|
||||
DA0B24FA2A6876D50045BDD7 /* SFSafeSymbols */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = DA0B24F92A6876D40045BDD7 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */;
|
||||
|
|
|
@ -30558,6 +30558,7 @@
|
|||
}
|
||||
},
|
||||
"iPhone" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"be" : {
|
||||
"stringUnit" : {
|
||||
|
@ -73626,4 +73627,4 @@
|
|||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
}
|
|
@ -23,6 +23,8 @@ let package = Package(
|
|||
.package(name: "Network", path: "../Network"),
|
||||
.package(name: "Env", path: "../Env"),
|
||||
.package(name: "DesignSystem", path: "../DesignSystem"),
|
||||
.package(url: "https://github.com/Giphy/giphy-ios-sdk", from: "2.2.7"),
|
||||
.package(url: "https://github.com/nicklockwood/LRUCache", from: "1.0.4"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
@ -34,6 +36,8 @@ let package = Package(
|
|||
.product(name: "Network", package: "Network"),
|
||||
.product(name: "Env", package: "Env"),
|
||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||
.product(name: "GiphyUISDK", package: "giphy-ios-sdk"),
|
||||
.product(name: "LRUCache", package: "LRUCache"),
|
||||
],
|
||||
swiftSettings: [
|
||||
.enableExperimentalFeature("StrictConcurrency"),
|
||||
|
|
|
@ -11,6 +11,10 @@ actor TimelineDatasource {
|
|||
func get() -> [Status] {
|
||||
statuses.filter { $0.filtered?.first?.filter.filterAction != .hide }
|
||||
}
|
||||
|
||||
func count() -> Int {
|
||||
statuses.count
|
||||
}
|
||||
|
||||
func reset() {
|
||||
statuses = []
|
||||
|
|
|
@ -122,8 +122,10 @@ public struct TimelineView: View {
|
|||
SoundEffectManager.shared.playSound(.refresh)
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id) {
|
||||
if let latestEvent = watcher.latestEvent {
|
||||
viewModel.handleEvent(event: latestEvent, currentAccount: account)
|
||||
Task {
|
||||
if let latestEvent = watcher.latestEvent {
|
||||
await viewModel.handleEvent(event: latestEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: timeline) { _, newValue in
|
||||
|
|
|
@ -54,15 +54,11 @@ import SwiftUI
|
|||
var tag: Tag?
|
||||
|
||||
// Internal source of truth for a timeline.
|
||||
private var datasource = TimelineDatasource()
|
||||
private(set) var datasource = TimelineDatasource()
|
||||
private let cache = TimelineCache()
|
||||
private var visibileStatusesIds = Set<String>()
|
||||
private var canStreamEvents: Bool = true
|
||||
|
||||
private var accountId: String? {
|
||||
CurrentAccount.shared.account?.id
|
||||
}
|
||||
|
||||
var client: Client? {
|
||||
didSet {
|
||||
if oldValue != client {
|
||||
|
@ -108,35 +104,33 @@ import SwiftUI
|
|||
await datasource.reset()
|
||||
}
|
||||
|
||||
func handleEvent(event: any StreamEvent, currentAccount _: CurrentAccount) {
|
||||
Task {
|
||||
if let event = event as? StreamEventUpdate,
|
||||
timeline == .home,
|
||||
canStreamEvents,
|
||||
isTimelineVisible,
|
||||
await !datasource.contains(statusId: event.status.id)
|
||||
{
|
||||
pendingStatusesObserver.pendingStatuses.insert(event.status.id, at: 0)
|
||||
let newStatus = event.status
|
||||
await datasource.insert(newStatus, at: 0)
|
||||
func handleEvent(event: any StreamEvent) async {
|
||||
if let event = event as? StreamEventUpdate,
|
||||
timeline == .home,
|
||||
canStreamEvents,
|
||||
isTimelineVisible,
|
||||
await !datasource.contains(statusId: event.status.id)
|
||||
{
|
||||
pendingStatusesObserver.pendingStatuses.insert(event.status.id, at: 0)
|
||||
let newStatus = event.status
|
||||
await datasource.insert(newStatus, at: 0)
|
||||
await cacheHome()
|
||||
let statuses = await datasource.get()
|
||||
withAnimation {
|
||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
||||
}
|
||||
} else if let event = event as? StreamEventDelete {
|
||||
await datasource.remove(event.status)
|
||||
await cacheHome()
|
||||
let statuses = await datasource.get()
|
||||
withAnimation {
|
||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
||||
}
|
||||
} else if let event = event as? StreamEventStatusUpdate {
|
||||
if let originalIndex = await datasource.indexOf(statusId: event.status.id) {
|
||||
await datasource.replace(event.status, at: originalIndex)
|
||||
await cacheHome()
|
||||
let statuses = await datasource.get()
|
||||
withAnimation {
|
||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
||||
}
|
||||
} else if let event = event as? StreamEventDelete {
|
||||
await datasource.remove(event.status)
|
||||
await cacheHome()
|
||||
let statuses = await datasource.get()
|
||||
withAnimation {
|
||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
||||
}
|
||||
} else if let event = event as? StreamEventStatusUpdate {
|
||||
if let originalIndex = await datasource.indexOf(statusId: event.status.id) {
|
||||
await datasource.replace(event.status, at: originalIndex)
|
||||
await cacheHome()
|
||||
statusesState = await .display(statuses: datasource.get(), nextPageState: .hasNextPage)
|
||||
}
|
||||
statusesState = await .display(statuses: datasource.get(), nextPageState: .hasNextPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
@testable import Timeline
|
||||
import XCTest
|
||||
|
||||
final class TimelineTests: XCTestCase {
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct
|
||||
// results.
|
||||
XCTAssertEqual(Timeline().text, "Hello, World!")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
@testable import Timeline
|
||||
import XCTest
|
||||
import Network
|
||||
import Models
|
||||
|
||||
@MainActor
|
||||
final class TimelineViewModelTests: XCTestCase {
|
||||
func testStreamEventInsertNewStatus() async throws {
|
||||
let subject = TimelineViewModel()
|
||||
let client = Client(server: "localhost")
|
||||
subject.client = client
|
||||
subject.timeline = .home
|
||||
subject.isTimelineVisible = true
|
||||
|
||||
let isEmpty = await subject.datasource.isEmpty
|
||||
XCTAssertTrue(isEmpty)
|
||||
await subject.datasource.append(.placeholder())
|
||||
var count = await subject.datasource.count()
|
||||
XCTAssertTrue(count == 1)
|
||||
await subject.handleEvent(event: StreamEventUpdate(status: .placeholder()))
|
||||
count = await subject.datasource.count()
|
||||
XCTAssertTrue(count == 2)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue