add poi type to feed item shape
This commit is contained in:
@@ -162,18 +162,23 @@ final class ContextOrchestrator: NSObject, ObservableObject {
|
||||
return (true, "initial")
|
||||
}
|
||||
let now = Date()
|
||||
if let last = lastRecomputeAt, now.timeIntervalSince(last) > 15 * 60 {
|
||||
return (true, "timer_15m")
|
||||
let speed = max(0, location.speed)
|
||||
let moving = speed >= 1.0
|
||||
let maxInterval: TimeInterval = moving ? 5 * 60 : 15 * 60
|
||||
if let last = lastRecomputeAt, now.timeIntervalSince(last) > maxInterval {
|
||||
return (true, moving ? "timer_5m_moving" : "timer_15m")
|
||||
}
|
||||
if let lastLoc = lastRecomputeLocation {
|
||||
let dist = location.distance(from: lastLoc)
|
||||
if dist > 250 {
|
||||
return (true, "moved_250m")
|
||||
let distanceThreshold: CLLocationDistance = moving ? 100 : 250
|
||||
if dist > distanceThreshold {
|
||||
return (true, moving ? "moved_100m_moving" : "moved_250m")
|
||||
}
|
||||
}
|
||||
if let lastAcc = lastRecomputeAccuracy, location.horizontalAccuracy > 0, lastAcc > 0 {
|
||||
if lastAcc - location.horizontalAccuracy > 50 {
|
||||
return (true, "accuracy_improved_50m")
|
||||
let improvementThreshold: CLLocationAccuracy = moving ? 30 : 50
|
||||
if lastAcc - location.horizontalAccuracy > improvementThreshold {
|
||||
return (true, moving ? "accuracy_improved_30m" : "accuracy_improved_50m")
|
||||
}
|
||||
}
|
||||
return (false, "no_trigger")
|
||||
@@ -217,6 +222,7 @@ final class ContextOrchestrator: NSObject, ObservableObject {
|
||||
|
||||
var rightNowCandidates: [HeuristicRanker.Ranked] = []
|
||||
var calendarItems: [FeedItem] = []
|
||||
var poiItems: [FeedItem] = []
|
||||
var weatherNowItem: FeedItem? = nil
|
||||
var fetchFailed = false
|
||||
var wxDiagnostics: [String: String] = [:]
|
||||
@@ -317,8 +323,29 @@ final class ContextOrchestrator: NSObject, ObservableObject {
|
||||
}
|
||||
|
||||
switch poiRes {
|
||||
case .success:
|
||||
break
|
||||
case .success(let pois):
|
||||
if pois.isEmpty {
|
||||
logger.info("no points of interests found")
|
||||
}
|
||||
for poi in pois.prefix(2) {
|
||||
let subtitle = poiSubtitle(for: poi)
|
||||
let confidence = min(max(poi.confidence, 0.0), 1.0)
|
||||
let item = FeedItem(
|
||||
id: poi.id,
|
||||
type: .poiNearby,
|
||||
title: poi.name.truncated(maxLength: TextConstraints.titleMax),
|
||||
subtitle: subtitle.truncated(maxLength: TextConstraints.subtitleMax),
|
||||
priority: confidence,
|
||||
ttlSec: max(1, poi.ttlSec),
|
||||
condition: nil,
|
||||
startsAt: nil,
|
||||
poiType: poi.poiType,
|
||||
bucket: .fyi,
|
||||
actions: ["DISMISS"]
|
||||
)
|
||||
poiItems.append(item)
|
||||
rightNowCandidates.append(.init(item: item, confidence: confidence, isEligibleForRightNow: true))
|
||||
}
|
||||
case .failure(let error):
|
||||
fetchFailed = true
|
||||
logger.error("poi fetch failed: \(String(describing: error), privacy: .public)")
|
||||
@@ -364,7 +391,7 @@ final class ContextOrchestrator: NSObject, ObservableObject {
|
||||
lastWeatherDiagnostics = wxDiagnostics
|
||||
lastCalendarDiagnostics = calDiagnostics
|
||||
|
||||
logger.info("pipeline right_now_candidates=\(rightNowCandidates.count) calendar_items=\(calendarItems.count) fetchFailed=\(fetchFailed) elapsed_ms=\(elapsedMs)")
|
||||
logger.info("pipeline right_now_candidates=\(rightNowCandidates.count) calendar_items=\(calendarItems.count) poi_items=\(poiItems.count) fetchFailed=\(fetchFailed) elapsed_ms=\(elapsedMs)")
|
||||
|
||||
if fetchFailed, rightNowCandidates.isEmpty, calendarItems.isEmpty, weatherNowItem == nil {
|
||||
let fallbackFeed = store.getFeed(now: nowEpoch)
|
||||
@@ -399,6 +426,28 @@ final class ContextOrchestrator: NSObject, ObservableObject {
|
||||
ttlSec: max(1, item.ttlSec),
|
||||
condition: item.condition,
|
||||
startsAt: item.startsAt,
|
||||
poiType: item.poiType,
|
||||
bucket: .fyi,
|
||||
actions: ["DISMISS"]
|
||||
)
|
||||
})
|
||||
|
||||
let fyiPOI = poiItems
|
||||
.filter { $0.id != winnerItem.id }
|
||||
.filter { !store.isSuppressed(id: $0.id, type: $0.type, now: nowEpoch) }
|
||||
.prefix(2)
|
||||
|
||||
fyi.append(contentsOf: fyiPOI.map { item in
|
||||
FeedItem(
|
||||
id: item.id,
|
||||
type: item.type,
|
||||
title: item.title.truncated(maxLength: TextConstraints.titleMax),
|
||||
subtitle: item.subtitle.truncated(maxLength: TextConstraints.subtitleMax),
|
||||
priority: min(max(item.priority, 0.0), 1.0),
|
||||
ttlSec: max(1, item.ttlSec),
|
||||
condition: item.condition,
|
||||
startsAt: item.startsAt,
|
||||
poiType: item.poiType,
|
||||
bucket: .fyi,
|
||||
actions: ["DISMISS"]
|
||||
)
|
||||
@@ -467,6 +516,22 @@ final class ContextOrchestrator: NSObject, ObservableObject {
|
||||
meta: FeedMeta(winnerId: base.meta.winnerId, unreadCount: cards.count)
|
||||
)
|
||||
}
|
||||
|
||||
private func poiSubtitle(for poi: POIDataSource.POI) -> String {
|
||||
let distance = distanceText(meters: poi.distanceMeters)
|
||||
let walk = "\(poi.walkingMinutes) min walk"
|
||||
let parts = [poi.category, distance, walk]
|
||||
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
.filter { !$0.isEmpty }
|
||||
return parts.joined(separator: " • ")
|
||||
}
|
||||
|
||||
private func distanceText(meters: CLLocationDistance) -> String {
|
||||
if meters >= 1000 {
|
||||
return String(format: "%.1f km", meters / 1000.0)
|
||||
}
|
||||
return "\(Int(meters.rounded())) m"
|
||||
}
|
||||
}
|
||||
|
||||
extension ContextOrchestrator: CLLocationManagerDelegate {
|
||||
|
||||
Reference in New Issue
Block a user