forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathActivityHubTab.swift
More file actions
112 lines (105 loc) · 4.05 KB
/
Copy pathActivityHubTab.swift
File metadata and controls
112 lines (105 loc) · 4.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import OmiTheme
import SwiftUI
/// The Activity destination inside the Memory hub: the chronological spine that used to be Home's
/// landing surface, wearing the same panel chrome (time filter, kind chips, corpus count) it wore
/// there. Home now lands in the conversation (`QueryShellMode.homeDefault`); this is where the
/// timeline went.
///
/// Deliberately **not** `QueryShellHome`: that surface is the app's one chat destination (INV-NAV-1)
/// and mounts the one composer. This host has no composer and no answer mode — it is the `.results`
/// body only, so the hub cannot grow a second chat.
struct ActivityHubTab: View {
let appState: AppState
@ObservedObject var memoriesViewModel: MemoriesViewModel
let onOpenConversation: (ServerConversation) -> Void
let onOpenMemory: (SpineMemory) -> Void
let onOpenBrainMap: () -> Void
let onOpenRewind: () -> Void
let selectedDestination: MemoryHubDestination
let onSelectDestination: (MemoryHubDestination) -> Void
@ObservedObject private var tasksStore = TasksStore.shared
@State private var filters = QueryShellFilters()
@State private var searchText = ""
@State private var screenCount: Int?
var body: some View {
GeometryReader { proxy in
let lane = QueryShellLayout.laneWidth(for: proxy.size.width)
// The panel arithmetic with the search bar in the hero slot but no composer inside the panel.
let room =
proxy.size.height - QueryShellLayout.surfaceTopInset
- QueryShellLayout.barMinHeight
- QueryShellLayout.panelGap
- BrainSectionPageMetrics.navigationHeight
- QueryShellLayout.panelChromeHeight(mode: .results, composerHeight: 0)
let bodyHeight = min(
QueryShellLayout.maximumBodyHeight, max(QueryShellLayout.minimumBodyHeight, room))
VStack(spacing: QueryShellLayout.panelGap) {
searchBar
QueryResultsPanel(
request: requestBinding,
mode: .results,
total: total,
onExitAnswer: nil,
bodyHeight: bodyHeight,
chipBehavior: .none,
topAccessory: {
BrainSectionNavigation(
selected: selectedDestination,
onSelect: onSelectDestination
)
},
headerAccessory: { EmptyView() },
footer: { EmptyView() }
) {
SpineStream(
request: QueryShellRequest(text: searchText, filters: filters),
appState: appState,
memoriesViewModel: memoriesViewModel,
tasksStore: tasksStore,
searchSurface: .activity,
onOpenConversation: onOpenConversation,
onOpenMemory: onOpenMemory,
onOpenBrainMap: onOpenBrainMap,
onOpenRewind: onOpenRewind
)
}
Spacer(minLength: 0)
}
.frame(width: lane)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.padding(.top, QueryShellLayout.surfaceTopInset)
}
.task { await loadScreenCount() }
}
private var searchBar: some View {
QuerySearchBar(
text: $searchText,
accessibilityID: "activity-search-field",
placeholder: "Search activity…",
focus: nil,
searchSurface: .activity
)
}
private var requestBinding: Binding<QueryShellRequest> {
Binding(
get: { QueryShellRequest(text: searchText, filters: filters) },
set: { next in
searchText = next.text
filters = next.filters
})
}
/// Same corpus arithmetic as Home's count line, minus nothing: the hub shows the same account.
private var total: Int? {
guard let screenCount else { return nil }
let conversations = appState.conversations.filter { $0.deleted != true }.count
let tasks = tasksStore.tasks.filter { !$0.isRetired }.count
return conversations + memoriesViewModel.memories.count + tasks + screenCount
}
private func loadScreenCount() async {
guard await SpineScreenIndex.poolWhenReady() != nil else {
screenCount = 0
return
}
screenCount = (try? await RewindDatabase.shared.getScreenshotCount()) ?? 0
}
}