@@ -160,24 +160,24 @@ struct TodayView: View {
160160 switch timelineContent {
161161 case . empty:
162162 EmptyView ( )
163- case . journal( let document, let refreshedAt, let recentItems , let timelineItems ) :
163+ case . journal( let document, let refreshedAt, let recentSection , let timelineSection ) :
164164 switch activeTimelineMode {
165165 case . topic:
166- journalTimeline ( document: document, refreshedAt: refreshedAt, recentItems : recentItems )
166+ journalTimeline ( document: document, refreshedAt: refreshedAt, recentSection : recentSection )
167167 case . timeline:
168- timelineCard ( items : timelineItems )
168+ timelineCard ( section : timelineSection )
169169 case nil :
170170 EmptyView ( )
171171 }
172- case . raw( let items ) :
173- timelineCard ( items : items )
172+ case . raw( let section ) :
173+ timelineCard ( section : section )
174174 }
175175 }
176176
177177 private func journalTimeline(
178178 document: JournalMarkdownDocument ,
179179 refreshedAt: Date ,
180- recentItems : [ TimelineItem ]
180+ recentSection : TimelineSection
181181 ) -> some View {
182182 VStack ( alignment: . leading, spacing: 20 ) {
183183 VStack ( alignment: . leading, spacing: 0 ) {
@@ -218,28 +218,26 @@ struct TodayView: View {
218218 . frame ( maxWidth: . infinity, alignment: . leading)
219219 . background ( Color ( nsColor: . controlBackgroundColor) , in: RoundedRectangle ( cornerRadius: 24 ) )
220220
221- if recentItems . isEmpty == false {
221+ if recentSection . isEmpty == false {
222222 VStack ( alignment: . leading, spacing: 12 ) {
223223 Text ( " Recent Activity " )
224224 . font ( . headline)
225225 Text ( " Summary last refreshed at \( OpenbirdDateFormatting . timeString ( for: refreshedAt) ) . Newer captured activity is shown below. " )
226226 . font ( . subheadline)
227227 . foregroundStyle ( . secondary)
228- timelineCard ( items : recentItems )
228+ timelineCard ( section : recentSection )
229229 }
230230 }
231231 }
232232 }
233233
234- private func timelineCard( items: [ TimelineItem ] ) -> some View {
235- let insights = TimelineInsightBuilder . build ( from: items)
236-
234+ private func timelineCard( section: TimelineSection ) -> some View {
237235 return LazyVStack ( alignment: . leading, spacing: 0 ) {
238- ForEach ( insights. indices , id: \. self ) { index in
236+ ForEach ( Array ( section . insights. enumerated ( ) ) , id: \. element . id ) { index, insight in
239237 if index > 0 {
240238 Divider ( )
241239 }
242- timelineInsightRow ( insights [ index ] )
240+ timelineInsightRow ( insight )
243241 . padding ( 24 )
244242 }
245243 }
@@ -428,8 +426,8 @@ struct TodayView: View {
428426 let journal = model. todayJournal
429427 let rawEvents = model. rawEvents
430428 let installedApplications = model. installedApplications
431- let rawItemsTask = Task . detached ( priority: . userInitiated) {
432- Self . buildTimelineItems (
429+ let rawSectionTask = Task . detached ( priority: . userInitiated) {
430+ Self . buildTimelineSection (
433431 rawEvents: rawEvents,
434432 installedApplications: installedApplications
435433 )
@@ -443,13 +441,13 @@ struct TodayView: View {
443441 }
444442
445443 guard let journal else {
446- let rawItems = await rawItemsTask . value
444+ let rawSection = await rawSectionTask . value
447445
448446 guard Task . isCancelled == false else {
449447 return
450448 }
451449
452- timelineContent = rawItems . isEmpty ? . empty : . raw( rawItems )
450+ timelineContent = rawSection . isEmpty ? . empty : . raw( rawSection )
453451 return
454452 }
455453
@@ -465,26 +463,29 @@ struct TodayView: View {
465463 return
466464 }
467465
468- let rawItems = await rawItemsTask . value
466+ let rawSection = await rawSectionTask . value
469467
470468 guard Task . isCancelled == false else {
471469 return
472470 }
473471
474472 guard parsedJournal. hasSummaryContent else {
475- timelineContent = rawItems . isEmpty ? . empty : . raw( rawItems )
473+ timelineContent = rawSection . isEmpty ? . empty : . raw( rawSection )
476474 return
477475 }
478476
479- let recentItems : [ TimelineItem ]
477+ let recentSection : TimelineSection
480478 if parsedJournal. hasNewerActivity {
481479 timelinePreparationStatus = . buildingRecentActivity
482- recentItems = Self . recentTimelineItems (
483- from: rawItems ,
480+ let recentItems = Self . recentTimelineItems (
481+ from: rawSection . items ,
484482 matching: Set ( parsedJournal. uncompiledRawEvents. map ( \. id) )
485483 )
484+ recentSection = await Task . detached ( priority: . userInitiated) {
485+ TimelineSection . build ( from: recentItems)
486+ } . value
486487 } else {
487- recentItems = [ ]
488+ recentSection = . empty
488489 }
489490
490491 guard Task . isCancelled == false else {
@@ -495,8 +496,8 @@ struct TodayView: View {
495496 timelineContent = . journal(
496497 document: parsedJournal. document,
497498 refreshedAt: journal. updatedAt,
498- recentItems : recentItems ,
499- timelineItems : rawItems
499+ recentSection : recentSection ,
500+ timelineSection : rawSection
500501 )
501502 }
502503
@@ -517,16 +518,16 @@ struct TodayView: View {
517518 )
518519 }
519520
520- nonisolated private static func buildTimelineItems (
521+ nonisolated private static func buildTimelineSection (
521522 rawEvents: [ ActivityEvent ] ,
522523 installedApplications: [ InstalledApplication ]
523- ) -> [ TimelineItem ] {
524+ ) -> TimelineSection {
524525 let groupedRawEvents = ActivityEvidencePreprocessor . groupedMeaningfulEvents ( from: rawEvents)
525526 let applicationsByBundleID = Dictionary ( uniqueKeysWithValues: installedApplications. map {
526527 ( $0. bundleID. lowercased ( ) , $0)
527528 } )
528529
529- return groupedRawEvents
530+ let items = groupedRawEvents
530531 . filter { $0. isExcluded == false }
531532 . map { event in
532533 let bundlePath = applicationsByBundleID [ event. bundleId. lowercased ( ) ] ? . bundlePath
@@ -552,6 +553,8 @@ struct TodayView: View {
552553 appName: event. appName
553554 )
554555 }
556+
557+ return TimelineSection . build ( from: items)
555558 }
556559
557560 nonisolated private static func recentTimelineItems(
@@ -568,19 +571,37 @@ struct TodayView: View {
568571 }
569572}
570573
574+ struct TimelineSection : Sendable {
575+ let items : [ TimelineItem ]
576+ let insights : [ TimelineInsightGroup ]
577+
578+ static let empty = TimelineSection ( items: [ ] , insights: [ ] )
579+
580+ var isEmpty : Bool {
581+ items. isEmpty
582+ }
583+
584+ static func build( from items: [ TimelineItem ] ) -> TimelineSection {
585+ TimelineSection (
586+ items: items,
587+ insights: TimelineInsightBuilder . build ( from: items)
588+ )
589+ }
590+ }
591+
571592private enum TimelineContent : Sendable {
572593 case empty
573- case journal( document: JournalMarkdownDocument , refreshedAt: Date , recentItems : [ TimelineItem ] , timelineItems : [ TimelineItem ] )
574- case raw( [ TimelineItem ] )
594+ case journal( document: JournalMarkdownDocument , refreshedAt: Date , recentSection : TimelineSection , timelineSection : TimelineSection )
595+ case raw( TimelineSection )
575596
576597 var isEmpty : Bool {
577598 switch self {
578599 case . empty:
579600 return true
580- case . journal( let document, _, _, let timelineItems ) :
581- return ( document. leadingBlocks. isEmpty && document. sections. isEmpty) && timelineItems . isEmpty
582- case . raw( let items ) :
583- return items . isEmpty
601+ case . journal( let document, _, _, let timelineSection ) :
602+ return ( document. leadingBlocks. isEmpty && document. sections. isEmpty) && timelineSection . isEmpty
603+ case . raw( let section ) :
604+ return section . isEmpty
584605 }
585606 }
586607
@@ -596,10 +617,10 @@ private enum TimelineContent: Sendable {
596617 switch self {
597618 case . empty:
598619 return false
599- case . journal( _, _, _, let timelineItems ) :
600- return timelineItems . isEmpty == false
601- case . raw( let items ) :
602- return items . isEmpty == false
620+ case . journal( _, _, _, let timelineSection ) :
621+ return timelineSection . isEmpty == false
622+ case . raw( let section ) :
623+ return section . isEmpty == false
603624 }
604625 }
605626
0 commit comments