Skip to content

Commit

Permalink
Created basic version of edit sheet. Refactored pipeline builders.
Browse files Browse the repository at this point in the history
  • Loading branch information
erikdoe committed Mar 24, 2024
1 parent b46e82c commit ff9928c
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 93 deletions.
14 changes: 8 additions & 6 deletions CCMenu/Source/Model/PipelineModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ final class PipelineModel: ObservableObject {
}

func update(pipeline: Pipeline) {
guard let idx = pipelines.firstIndex(where: { $0.id == pipeline.id }) else {
debugPrint("trying to update unknown pipeline \(pipelines.debugDescription)")
return
}
guard let idx = pipelines.firstIndex(where: { $0.id == pipeline.id }) else { return }
let change = StatusChange(pipeline: pipeline, previousStatus: pipelines[idx].status)
pipelines[idx] = pipeline
pipelines[idx].lastUpdated = Date()
Expand All @@ -43,13 +40,18 @@ final class PipelineModel: ObservableObject {

@discardableResult
func add(pipeline newPipeline: Pipeline) -> Bool {
if pipelines.contains(where: {$0.id == newPipeline.id}){
if pipelines.contains(where: { $0.id == newPipeline.id }) {
return false
}
pipelines.append(newPipeline)
return true
}

func remove(pipelineId: String) {
guard let idx = pipelines.firstIndex(where: { $0.id == pipelineId }) else { return }
pipelines.remove(at: idx)
}

private func updateSettings() {
// TODO: this is called, too, every time the status gets updated...
let list = pipelines.map({ $0.asDictionaryForPersisting() })
Expand Down Expand Up @@ -78,7 +80,7 @@ final class PipelineModel: ObservableObject {
}

private func addCCMenu2Pipeline() {
let p0 = Pipeline(name: "ccmenu2 (build-and-test)", feed: Pipeline.Feed(type: .github, url: "https://api.github.com/repos/erikdoe/ccmenu2/actions/workflows/build-and-test.yaml/runs"))
let p0 = Pipeline(name: "ccmenu2 | build-and-test", feed: Pipeline.Feed(type: .github, url: "https://api.github.com/repos/erikdoe/ccmenu2/actions/workflows/build-and-test.yaml/runs"))
pipelines.append(p0)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,7 @@ struct AddCCTrayPipelineSheet: View {
.autocorrectionDisabled(true)
.onSubmit {
if !url.isEmpty {
Task {
let c = !credential.isEmpty ? credential : nil
await projectList.updateProjects(url: $url, credential: c)
}
Task { await projectList.updateProjects(url: $url, credential: credentialOptional()) }
}
}

Expand All @@ -63,15 +60,15 @@ struct AddCCTrayPipelineSheet: View {
.accessibilityIdentifier("Project picker")
.disabled(!projectList.selected.isValid)
.onChange(of: projectList.selected) { _ in
pipelineBuilder.updateName(project: projectList.selected)
pipelineBuilder.setDefaultName(project: projectList.selected)
}
.padding(.bottom)

HStack {
TextField("Display name:", text: $pipelineBuilder.name)
.accessibilityIdentifier("Display name field")
Button("Reset", systemImage: "arrowshape.turn.up.backward") {
pipelineBuilder.updateName(project: projectList.selected)
pipelineBuilder.setDefaultName(project: projectList.selected)
}
}
.padding(.bottom)
Expand All @@ -83,16 +80,7 @@ struct AddCCTrayPipelineSheet: View {
}
.keyboardShortcut(.cancelAction)
Button("Apply") {
var feedUrl = url
if useBasicAuth && !credential.user.isEmpty {
feedUrl = CCTrayPipelineBuilder.setUser(credential.user, inURL: url)
do {
try Keychain().setPassword(credential.password, forURL: feedUrl)
} catch {
// TODO: Figure out what to do here – so many errors...
}
}
let p = pipelineBuilder.makePipeline(feedUrl: feedUrl, name: projectList.selected.name)
let p = pipelineBuilder.makePipeline(feedUrl: url, credential: credentialOptional(), project: projectList.selected)
model.add(pipeline: p)
presentation.dismiss()
}
Expand All @@ -104,6 +92,11 @@ struct AddCCTrayPipelineSheet: View {
.frame(idealWidth: 450)
.padding()
}

private func credentialOptional() -> HTTPCredential? {
(useBasicAuth && !credential.isEmpty) ? credential : nil
}

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,28 @@ import Foundation
class CCTrayPipelineBuilder: ObservableObject {
@Published var name: String = ""

func updateName(project: CCTrayProject) {
var newName = ""
if project.isValid {
newName.append(project.name)
}
name = newName
func setDefaultName(project: CCTrayProject) {
name = project.isValid ? project.name : ""
}

func makePipeline(feedUrl: String, name: String) -> Pipeline {
// TODO: Consider what is the best place for this code and how much state it should be aware of
// (and see same comment in GitHubPipelineBuilder)
let feed = Pipeline.Feed(type:.cctray, url: feedUrl, name: name)
var p: Pipeline = Pipeline(name: self.name, feed: feed)
func makePipeline(feedUrl: String, credential: HTTPCredential?, project: CCTrayProject) -> Pipeline {
var feedUrl = feedUrl
if let credential {
feedUrl = setUser(credential.user, inURL: feedUrl)
do {
try Keychain().setPassword(credential.password, forURL: feedUrl)
} catch {
// TODO: Figure out what to do here – so many errors...
}
}
let feed = Pipeline.Feed(type: .cctray, url: feedUrl, name: project.name)
var p: Pipeline = Pipeline(name: name, feed: feed)
p.status = Pipeline.Status(activity: .sleeping)
p.status.lastBuild = Build(result: .unknown)
return p
}

static func setUser(_ user: String?, inURL urlString: String) -> String {
// TODO: Consider what is the best place for this code
private func setUser(_ user: String?, inURL urlString: String) -> String {
guard let user, !user.isEmpty else {
return urlString
}
Expand Down
46 changes: 19 additions & 27 deletions CCMenu/Source/Pipeline Window/EditPipelineSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,48 +8,40 @@ import SwiftUI


struct EditPipelineSheet: View {
var pipeline: Pipeline
@State var pipeline: Pipeline
@State var name: String = ""
@ObservedObject var model: PipelineModel
@Environment(\.presentationMode) @Binding var presentation

var body: some View {
VStack {
Text("Edit Pipeline")
.font(.headline)
Text("missing")
.padding(.bottom)
Form {
TextField("Name:", text: $name)
.accessibilityIdentifier("Name field")
}
.padding(.bottom)
HStack {
Button("Cancel") {
presentation.dismiss()
}
.keyboardShortcut(.cancelAction)
Button("Apply") {
// var p = Pipeline(name: "erikdoe/ocmock", feedUrl: "http://localhost:4567/cc.xml")
// p.status = Pipeline.Status(activity: .sleeping, lastBuild: Build(result: .success))
// model.pipelines[editIndex] = p
pipeline.name = name
model.update(pipeline: pipeline)
presentation.dismiss()
}
.buttonStyle(DefaultButtonStyle())
.keyboardShortcut(.defaultAction)
.disabled(pipeline.name.isEmpty)
}
.onAppear() {
name = pipeline.name
}
}
.padding(EdgeInsets(top: 10, leading:10, bottom: 10, trailing: 10))
}
}


struct EditPipelineSheet_Previews: PreviewProvider {
static var previews: some View {
Group {
// AddPipelineSheet(model: makeViewModel())
}
}

static func makeViewModel() -> PipelineModel {
let model = PipelineModel()

var p0 = Pipeline(name: "connectfour", feed: Pipeline.Feed(type: .cctray, url: "http://localhost:4567/cctray.xml"))
p0.status = Pipeline.Status(activity: .building, lastBuild: Build(result: .failure))
model.pipelines = [p0]
return model
.frame(minWidth: 400)
.frame(idealWidth: 450)
.padding()
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,9 @@ struct AddGitHubPipelineSheet: View {
.accessibilityIdentifier("Repository picker")
.disabled(!repositoryList.selected.isValid)
.onChange(of: repositoryList.selected) { _ in
pipelineBuilder.updateName(repository: repositoryList.selected, workflow: workflowList.selected)
pipelineBuilder.setDefaultName(repository: repositoryList.selected, workflow: workflowList.selected)
if repositoryList.selected.isValid {
Task {
await workflowList.updateWorkflows(owner: owner, repository: repositoryList.selected.name, token: authenticator.token)
}
Task { await workflowList.updateWorkflows(owner: owner, repository: repositoryList.selected.name, token: authenticator.token) }
} else {
workflowList.clearWorkflows()
}
Expand All @@ -84,15 +82,15 @@ struct AddGitHubPipelineSheet: View {
.accessibilityIdentifier("Workflow picker")
.disabled(!workflowList.selected.isValid)
.onChange(of: workflowList.selected) { _ in
pipelineBuilder.updateName(repository: repositoryList.selected, workflow: workflowList.selected)
pipelineBuilder.setDefaultName(repository: repositoryList.selected, workflow: workflowList.selected)
}
.padding(.bottom)

HStack {
TextField("Display name:", text: $pipelineBuilder.name)
.accessibilityIdentifier("Display name field")
Button("Reset", systemImage: "arrowshape.turn.up.backward") {
pipelineBuilder.updateName(repository: repositoryList.selected, workflow: workflowList.selected)
pipelineBuilder.setDefaultName(repository: repositoryList.selected, workflow: workflowList.selected)
}
}
.padding(.bottom)
Expand All @@ -104,7 +102,7 @@ struct AddGitHubPipelineSheet: View {
}
.keyboardShortcut(.cancelAction)
Button("Apply") {
let p = pipelineBuilder.makePipeline(owner: owner)
let p = pipelineBuilder.makePipeline(owner: owner, repository: repositoryList.selected, workflow: workflowList.selected)
model.add(pipeline: p)
presentation.dismiss()
}
Expand All @@ -116,20 +114,10 @@ struct AddGitHubPipelineSheet: View {
.frame(idealWidth: 450)
.padding()
.onAppear() {
do {
let token = try Keychain().getToken(forService: "GitHub")
authenticator.token = token
authenticator.tokenDescription = token ?? ""
} catch {
}
authenticator.fetchTokenFromKeychain()
}
.onDisappear() {
guard let token = authenticator.token else { return }
do {
try Keychain().setToken(token, forService: "GitHub")
} catch {
// TODO: Figure out what to do here – so many errors...
}
authenticator.storeTokenInKeychain()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,24 @@ class GitHubAuthenticator: ObservableObject {
NSWorkspace.shared.open(GitHubAPI.applicationsUrl())
}

func fetchTokenFromKeychain() {
do {
token = try Keychain().getToken(forService: "GitHub")
} catch {
// TODO: Figure out what to do here – so many errors...
token = nil
}
tokenDescription = token ?? ""
}


func storeTokenInKeychain() {
guard let token else { return }
do {
try Keychain().setToken(token, forService: "GitHub")
} catch {
// TODO: Figure out what to do here – so many errors...
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,20 @@ import Foundation
import SwiftUI

class GitHubPipelineBuilder: ObservableObject {
private var repository = GitHubRepository()
private var workflow = GitHubWorkflow()
@Published var name: String = ""

func updateName(repository: GitHubRepository, workflow: GitHubWorkflow) {
func setDefaultName(repository: GitHubRepository, workflow: GitHubWorkflow) {
var newName = ""
if repository.isValid {
newName.append(repository.name)
if workflow.isValid {
newName.append(String(format: " | %@", workflow.name))
}
}
self.repository = repository
self.workflow = workflow
self.name = newName
}

func makePipeline(owner: String) -> Pipeline {
// TODO: Consider what is the best place for this code and how much state it should be aware of
func makePipeline(owner: String, repository: GitHubRepository, workflow: GitHubWorkflow) -> Pipeline {
let url = GitHubAPI.feedUrl(owner: owner, repository: repository.name, workflow: workflow.filename)
let feed = Pipeline.Feed(type: .github, url:url)
let pipeline = Pipeline(name: name, feed: feed)
Expand Down
3 changes: 2 additions & 1 deletion CCMenu/Source/Pipeline Window/PipelineListToolbar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ struct PipelineListToolbar: ToolbarContent {
.frame(height: 28)
.opacity(0.7)
.background() {
// TODO: Fix transparency in dark mode
Color(.unemphasizedSelectedContentBackgroundColor).opacity(isHoveringOverAddMenu ? 0.45 : 0)
}
.onHover {
Expand All @@ -92,7 +93,7 @@ struct PipelineListToolbar: ToolbarContent {

Button() {
withAnimation {
model.pipelines.removeAll(where: { viewState.selection.contains($0.id) })
viewState.selection.forEach({ model.remove(pipelineId: $0) })
viewState.selection.removeAll()
}
} label: {
Expand Down
19 changes: 19 additions & 0 deletions CCMenuUITests/PipelineWindowTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,23 @@ class PipelineWindowTests: XCTestCase {
XCTAssertTrue(window.tables.staticTexts["connectfour"].exists == false)
}

func testRenamesPipeline() throws {
let app = TestHelper.launchApp(pipelines: "CCTrayPipeline.json")
let window = app.windows["Pipelines"]
let sheet = window.sheets.firstMatch

window.tables.staticTexts["connectfour"].click()
window.toolbars.buttons["Edit pipeline"].click()

let nameField = sheet.textFields["Name field"]
nameField.click()
sheet.typeKey("a", modifierFlags: [ .command ])
sheet.typeText("TEST-TEST-TEST")
sheet.buttons["Apply"].click()

let titleText = window.tables.staticTexts["Pipeline title"]
expectation(for: NSPredicate(format: "value == 'TEST-TEST-TEST'"), evaluatedWith: titleText)
waitForExpectations(timeout: 2)
}

}
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,24 @@ For now the roadmap is tracked in this readme file.
### Pre-release 5 (planned)

- [X] Optimised CCTray reader requests
- [ ] Edit pipelines
- [X] Edit pipelines
- [X] Remaining menu appearance options
- [X] Reduced polling frequency on low data connections

### Later
### Pre-release 6 (planned)

- [ ] Sounds
- [ ] Import and export of pipeline config
- [ ] Set user/password for CCTray pipelines
- [ ] Refresh GitHub token

### To consider
### To consider

- [ ] Sounds
- [ ] Support for workflow-specific GitHub tokens
- [ ] Improve accessibility
- [ ] Add support for localisation
- [ ] Show avatar in notifications (committer or repo owner)
- [ ] Support for log in with GitHub (is this even possible?)
- [ ] Support for GitHub apps
- [ ] Pipeline groups with submenus
- [ ] Add Nevergreen-style dashboard (full screen window)
Expand Down

0 comments on commit ff9928c

Please sign in to comment.