Skip to content

Commit c1c3abb

Browse files
authored
feat: Activity Viewer (#1769)
* feat: Activity viewer - Introduces an Activity Viewer in the CE's toolbar - Implements TaskNotificationHandler to efficiently manage task notifications. - Enhances task creation process by enabling new task notifications from any where within the workspace. - Initialises TaskNotificationHandler at the workspace level to ensure separation and independent handling of notifications. * chore: Add task notification during workspace indexing * ✅ feat: Add unit tests for TaskNotificationHandler * 🚨 Made swiftlint happy * 🚨 Made swiftlint happy * fix: receiving notifications on the main thread
1 parent f92642a commit c1c3abb

15 files changed

+749
-12
lines changed

CodeEdit.xcodeproj/project.pbxproj

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,13 @@
281281
61538B902B111FE800A88846 /* String+AppearancesOfSubstring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61538B8F2B111FE800A88846 /* String+AppearancesOfSubstring.swift */; };
282282
61538B932B11201900A88846 /* String+Character.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61538B922B11201900A88846 /* String+Character.swift */; };
283283
615AA21A2B0CFD480013FCCC /* LazyStringLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615AA2192B0CFD480013FCCC /* LazyStringLoader.swift */; };
284+
617DB3D02C25AFAE00B58BFE /* TaskNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3CF2C25AFAE00B58BFE /* TaskNotificationHandler.swift */; };
285+
617DB3D32C25AFEA00B58BFE /* TaskNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3D22C25AFEA00B58BFE /* TaskNotificationModel.swift */; };
286+
617DB3D62C25B02D00B58BFE /* TaskNotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3D52C25B02D00B58BFE /* TaskNotificationView.swift */; };
287+
617DB3D82C25B04D00B58BFE /* CustomLoadingRingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3D72C25B04D00B58BFE /* CustomLoadingRingView.swift */; };
288+
617DB3DA2C25B07F00B58BFE /* TaskNotificationsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3D92C25B07F00B58BFE /* TaskNotificationsDetailView.swift */; };
289+
617DB3DC2C25B14A00B58BFE /* ActivityViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3DB2C25B14A00B58BFE /* ActivityViewer.swift */; };
290+
617DB3DF2C25E13800B58BFE /* TaskNotificationHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3DE2C25E13800B58BFE /* TaskNotificationHandlerTests.swift */; };
284291
6195E30D2B64044F007261CA /* WorkspaceDocument+SearchState+FindTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6195E30C2B64044F007261CA /* WorkspaceDocument+SearchState+FindTests.swift */; };
285292
6195E30F2B640474007261CA /* WorkspaceDocument+SearchState+FindAndReplaceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6195E30E2B640474007261CA /* WorkspaceDocument+SearchState+FindAndReplaceTests.swift */; };
286293
6195E3112B640485007261CA /* WorkspaceDocument+SearchState+IndexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6195E3102B640485007261CA /* WorkspaceDocument+SearchState+IndexTests.swift */; };
@@ -863,6 +870,13 @@
863870
61538B8F2B111FE800A88846 /* String+AppearancesOfSubstring.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+AppearancesOfSubstring.swift"; sourceTree = "<group>"; };
864871
61538B922B11201900A88846 /* String+Character.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Character.swift"; sourceTree = "<group>"; };
865872
615AA2192B0CFD480013FCCC /* LazyStringLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyStringLoader.swift; sourceTree = "<group>"; };
873+
617DB3CF2C25AFAE00B58BFE /* TaskNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationHandler.swift; sourceTree = "<group>"; };
874+
617DB3D22C25AFEA00B58BFE /* TaskNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationModel.swift; sourceTree = "<group>"; };
875+
617DB3D52C25B02D00B58BFE /* TaskNotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationView.swift; sourceTree = "<group>"; };
876+
617DB3D72C25B04D00B58BFE /* CustomLoadingRingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLoadingRingView.swift; sourceTree = "<group>"; };
877+
617DB3D92C25B07F00B58BFE /* TaskNotificationsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationsDetailView.swift; sourceTree = "<group>"; };
878+
617DB3DB2C25B14A00B58BFE /* ActivityViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityViewer.swift; sourceTree = "<group>"; };
879+
617DB3DE2C25E13800B58BFE /* TaskNotificationHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationHandlerTests.swift; sourceTree = "<group>"; };
866880
6195E30C2B64044F007261CA /* WorkspaceDocument+SearchState+FindTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WorkspaceDocument+SearchState+FindTests.swift"; sourceTree = "<group>"; };
867881
6195E30E2B640474007261CA /* WorkspaceDocument+SearchState+FindAndReplaceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WorkspaceDocument+SearchState+FindAndReplaceTests.swift"; sourceTree = "<group>"; };
868882
6195E3102B640485007261CA /* WorkspaceDocument+SearchState+IndexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WorkspaceDocument+SearchState+IndexTests.swift"; sourceTree = "<group>"; };
@@ -1461,6 +1475,7 @@
14611475
children = (
14621476
582213EE2918345500EFE361 /* About */,
14631477
588847642992A30900996D95 /* CEWorkspace */,
1478+
617DB3CE2C25AF5B00B58BFE /* ActivityViewer */,
14641479
587B9D7529300ABD00AC7927 /* CodeEditUI */,
14651480
58FD7603291EA1CB0051D6E4 /* Commands */,
14661481
043C321227E31FE8006AE443 /* Documents */,
@@ -1787,6 +1802,7 @@
17871802
children = (
17881803
283BDCC22972F211002AFF81 /* Acknowledgements */,
17891804
4EE96EC82960562000FFBEA8 /* Documents */,
1805+
617DB3DD2C25E11500B58BFE /* ActivityViewer */,
17901806
583E527429361B39001AB554 /* CodeEditUI */,
17911807
587B612C2934199800D5CD8F /* CodeFile */,
17921808
613899BD2B6E70E200A5CAF6 /* Search */,
@@ -2382,6 +2398,35 @@
23822398
path = FuzzySearch;
23832399
sourceTree = "<group>";
23842400
};
2401+
617DB3CE2C25AF5B00B58BFE /* ActivityViewer */ = {
2402+
isa = PBXGroup;
2403+
children = (
2404+
617DB3DB2C25B14A00B58BFE /* ActivityViewer.swift */,
2405+
617DB3D52C25B02D00B58BFE /* TaskNotificationView.swift */,
2406+
617DB3D92C25B07F00B58BFE /* TaskNotificationsDetailView.swift */,
2407+
617DB3D72C25B04D00B58BFE /* CustomLoadingRingView.swift */,
2408+
617DB3CF2C25AFAE00B58BFE /* TaskNotificationHandler.swift */,
2409+
617DB3D12C25AFD300B58BFE /* Models */,
2410+
);
2411+
path = ActivityViewer;
2412+
sourceTree = "<group>";
2413+
};
2414+
617DB3D12C25AFD300B58BFE /* Models */ = {
2415+
isa = PBXGroup;
2416+
children = (
2417+
617DB3D22C25AFEA00B58BFE /* TaskNotificationModel.swift */,
2418+
);
2419+
path = Models;
2420+
sourceTree = "<group>";
2421+
};
2422+
617DB3DD2C25E11500B58BFE /* ActivityViewer */ = {
2423+
isa = PBXGroup;
2424+
children = (
2425+
617DB3DE2C25E13800B58BFE /* TaskNotificationHandlerTests.swift */,
2426+
);
2427+
path = ActivityViewer;
2428+
sourceTree = "<group>";
2429+
};
23852430
66AF6CE02BF17CB100D83C9D /* ViewModels */ = {
23862431
isa = PBXGroup;
23872432
children = (
@@ -3403,6 +3448,7 @@
34033448
04BA7C142AE2AA7300584E1C /* GitCloneViewModel.swift in Sources */,
34043449
B61A606129F188AB009B43F9 /* ExternalLink.swift in Sources */,
34053450
587B9E9729301D8F00AC7927 /* BitBucketAccount+Token.swift in Sources */,
3451+
617DB3D62C25B02D00B58BFE /* TaskNotificationView.swift in Sources */,
34063452
587B9E7729301D8F00AC7927 /* String+PercentEncoding.swift in Sources */,
34073453
587B9E5B29301D8F00AC7927 /* GitCheckoutBranchView.swift in Sources */,
34083454
2813F93827ECC4AA00E305E4 /* FindNavigatorResultList.swift in Sources */,
@@ -3484,6 +3530,7 @@
34843530
58798284292ED0FB0085B254 /* TerminalEmulatorView.swift in Sources */,
34853531
B6C4F2AC2B3CC4D000B2B140 /* CommitChangedFileListItemView.swift in Sources */,
34863532
6C82D6B329BFD88700495C54 /* NavigateCommands.swift in Sources */,
3533+
617DB3D82C25B04D00B58BFE /* CustomLoadingRingView.swift in Sources */,
34873534
B6CFD8112C20A8EE00E63F1A /* NSFont+WithWeight.swift in Sources */,
34883535
B66A4E4C29C9179B004573B4 /* CodeEditApp.swift in Sources */,
34893536
661EF7B82BEE215300C3E577 /* ImageFileView.swift in Sources */,
@@ -3605,6 +3652,7 @@
36053652
581550D029FBD30400684881 /* FileSystemTableViewCell.swift in Sources */,
36063653
B607183F2B17DB07009CDAB4 /* SourceControlNavigatorRepositoryView+contextMenu.swift in Sources */,
36073654
B62AEDD42A27B29F009A9F52 /* PaneToolbar.swift in Sources */,
3655+
617DB3D32C25AFEA00B58BFE /* TaskNotificationModel.swift in Sources */,
36083656
D7E201B227E8D50000CB86D0 /* FindNavigatorForm.swift in Sources */,
36093657
287776E927E34BC700D46668 /* EditorTabBarView.swift in Sources */,
36103658
B60BE8BD297A167600841125 /* AcknowledgementRowView.swift in Sources */,
@@ -3634,6 +3682,7 @@
36343682
613899B52B6E700300A5CAF6 /* FuzzySearchModels.swift in Sources */,
36353683
58D01C94293167DC00C5B6B4 /* Color+HEX.swift in Sources */,
36363684
6C578D8729CD345900DC73B2 /* ExtensionSceneView.swift in Sources */,
3685+
617DB3D02C25AFAE00B58BFE /* TaskNotificationHandler.swift in Sources */,
36373686
B640A9A129E2188F00715F20 /* View+NavigationBarBackButtonVisible.swift in Sources */,
36383687
587B9E7929301D8F00AC7927 /* GitHubIssueRouter.swift in Sources */,
36393688
587B9E8029301D8F00AC7927 /* GitHubConfiguration.swift in Sources */,
@@ -3700,6 +3749,7 @@
37003749
58798218292D92370085B254 /* String+SafeOffset.swift in Sources */,
37013750
6C6BD70429CD17B600235D17 /* ExtensionsManager.swift in Sources */,
37023751
587B9E6129301D8F00AC7927 /* GitLabOAuthConfiguration.swift in Sources */,
3752+
617DB3DC2C25B14A00B58BFE /* ActivityViewer.swift in Sources */,
37033753
587B9E6229301D8F00AC7927 /* GitLabConfiguration.swift in Sources */,
37043754
61A53A7E2B4449870093BF8A /* WorkspaceDocument+Find.swift in Sources */,
37053755
6CABB19E29C5591D00340467 /* NSTableViewWrapper.swift in Sources */,
@@ -3810,6 +3860,7 @@
38103860
04C3255B2801F86400C8DA2D /* ProjectNavigatorViewController.swift in Sources */,
38113861
587B9E6029301D8F00AC7927 /* GitLabOAuthRouter.swift in Sources */,
38123862
B6AB09B32AB919CF0003A3A6 /* View+actionBar.swift in Sources */,
3863+
617DB3DA2C25B07F00B58BFE /* TaskNotificationsDetailView.swift in Sources */,
38133864
6C05A8AF284D0CA3007F4EAA /* WorkspaceDocument+Listeners.swift in Sources */,
38143865
588847632992A2A200996D95 /* CEWorkspaceFile.swift in Sources */,
38153866
6C2C155D29B4F4E500EA60A5 /* SplitViewReader.swift in Sources */,
@@ -3825,6 +3876,7 @@
38253876
files = (
38263877
583E528C29361B39001AB554 /* CodeEditUITests.swift in Sources */,
38273878
613053652B23A49300D767E3 /* TemporaryFile.swift in Sources */,
3879+
617DB3DF2C25E13800B58BFE /* TaskNotificationHandlerTests.swift in Sources */,
38283880
587B60F82934124200D5CD8F /* CEWorkspaceFileManagerTests.swift in Sources */,
38293881
6130535F2B23A31300D767E3 /* MemorySearchTests.swift in Sources */,
38303882
587B61012934170A00D5CD8F /* UnitTests_Extensions.swift in Sources */,

CodeEdit/Features/About/Acknowledgements/ViewModels/AcknowledgementsViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import SwiftUI
99

1010
final class AcknowledgementsViewModel: ObservableObject {
1111

12-
@Published private (set) var acknowledgements: [AcknowledgementDependency]
12+
@Published private(set) var acknowledgements: [AcknowledgementDependency]
1313

1414
var indexedAcknowledgements: [(index: Int, acknowledgement: AcknowledgementDependency)] {
1515
return Array(zip(acknowledgements.indices, acknowledgements))
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// ActivityViewer.swift
3+
// CodeEdit
4+
//
5+
// Created by Tommy Ludwig on 21.06.24.
6+
//
7+
8+
import SwiftUI
9+
10+
/// A view that shows the activity bar and the current status of any executed task
11+
struct ActivityViewer: View {
12+
@Environment(\.colorScheme)
13+
var colorScheme
14+
15+
@ObservedObject var taskNotificationHandler: TaskNotificationHandler
16+
var body: some View {
17+
HStack {
18+
HStack(spacing: 0) {
19+
// This is only a placeholder for the task popover(coming in the next pr)
20+
Rectangle()
21+
.frame(height: 22)
22+
.hidden()
23+
24+
Spacer()
25+
26+
TaskNotificationView(taskNotificationHandler: taskNotificationHandler)
27+
}
28+
.padding(.horizontal, 10)
29+
.background {
30+
if colorScheme == .dark {
31+
RoundedRectangle(cornerRadius: 5)
32+
.opacity(0.10)
33+
} else {
34+
RoundedRectangle(cornerRadius: 5)
35+
.opacity(0.1)
36+
}
37+
}
38+
.frame(minWidth: 200, idealWidth: 680)
39+
}
40+
.frame(height: 22)
41+
}
42+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// CustomLoadingRingView.swift
3+
// CodeEdit
4+
//
5+
// Created by Tommy Ludwig on 21.06.24.
6+
//
7+
8+
import SwiftUI
9+
10+
struct CustomLoadingRingView: View {
11+
@State private var isAnimating = false
12+
@State private var previousValue: Bool = false
13+
var progress: Double?
14+
var currentTaskCount: Int
15+
16+
let lineWidth: CGFloat = 2
17+
var body: some View {
18+
Circle()
19+
.stroke(style: StrokeStyle(lineWidth: lineWidth))
20+
.foregroundStyle(.tertiary)
21+
.overlay {
22+
if let progress = progress {
23+
Circle()
24+
.trim(from: 0, to: progress)
25+
.stroke(Color.blue.gradient, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))
26+
.animation(.easeInOut, value: progress)
27+
} else {
28+
Circle()
29+
.trim(from: 0, to: 0.5)
30+
.stroke(Color.blue.gradient, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))
31+
.rotationEffect(
32+
previousValue ?
33+
.degrees(isAnimating ? 0 : -360)
34+
: .degrees(isAnimating ? 360 : 0)
35+
)
36+
.animation(Animation.linear(duration: 1).repeatForever(autoreverses: false), value: isAnimating)
37+
.onAppear {
38+
self.previousValue = isAnimating
39+
self.isAnimating.toggle()
40+
}
41+
}
42+
}
43+
.rotationEffect(.degrees(-90))
44+
.overlay {
45+
if currentTaskCount > 1 {
46+
Text("\(currentTaskCount)")
47+
.font(.caption)
48+
}
49+
}
50+
}
51+
}
52+
53+
#Preview {
54+
Group {
55+
CustomLoadingRingView(currentTaskCount: 1)
56+
.frame(width: 22, height: 22)
57+
58+
CustomLoadingRingView(progress: 0.65, currentTaskCount: 1)
59+
.frame(width: 22, height: 22)
60+
}
61+
.padding()
62+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// TaskNotificationModel.swift
3+
// CodeEdit
4+
//
5+
// Created by Tommy Ludwig on 21.06.24.
6+
//
7+
8+
import Foundation
9+
10+
/// Represents a notifications or tasks, that are displayed in the activity viewer
11+
struct TaskNotificationModel: Equatable {
12+
var id: String
13+
var title: String
14+
var message: String?
15+
var percentage: Double?
16+
var isLoading: Bool = false
17+
}

0 commit comments

Comments
 (0)