Skip to content
This repository was archived by the owner on Sep 20, 2023. It is now read-only.

Subscribe/Unsubscribe #2482

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion Classes/Issues/GithubClient+Issues.swift
Original file line number Diff line number Diff line change
@@ -168,7 +168,8 @@ extension GithubClient {
viewerCanAdminister: canAdmin,
defaultBranch: repository.defaultBranchRef?.name ?? "master",
fileChanges: issueType.fileChanges,
mergeModel: issueType.mergeModel(availableTypes: availableMergeTypes)
mergeModel: issueType.mergeModel(availableTypes: availableMergeTypes),
subscriptionState: issueType.subscriptionState
)

DispatchQueue.main.async {
@@ -297,6 +298,37 @@ extension GithubClient {
}
}

func setSubscription(
previous: IssueResult,
subscribe: Bool,
completion: ((Result<SubscriptionState?>) -> Void)? = nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, what would it mean if the state is nil? Would that be an actual success?

) {

let state: SubscriptionState = subscribe
? .subscribed
: .unsubscribed

let optimisticResult = previous.updated(subscriptionState: state)

let cache = self.cache
cache.set(value: optimisticResult)

let mutation = UpdateSubscriptionMutation(subscribable_Id: previous.id, subscription_state: state)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change these parameters to camelCase?


client.mutate(mutation, result: { data in
data.updateSubscription?.subscribable
}, completion: { result in
switch result {
case .success(let subscribable):
completion?(.success(subscribable.viewerSubscription))
case .failure(let error):
cache.set(value: previous)
completion?(.error(error))
Squawk.show(error: error)
}
})
}

enum CollaboratorPermission: String {
case admin
case write
4 changes: 4 additions & 0 deletions Classes/Issues/Issue+IssueType.swift
Original file line number Diff line number Diff line change
@@ -11,6 +11,10 @@ import IGListKit

extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsIssue: IssueType {

var subscriptionState: SubscriptionState {
return viewerSubscription ?? .unsubscribed
}

var pullRequest: Bool {
return false
}
45 changes: 35 additions & 10 deletions Classes/Issues/IssueManagingContextController.swift
Original file line number Diff line number Diff line change
@@ -84,10 +84,12 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
case lock
case reopen
case close
case subscribe
case unsubscribe
}

var actions: [Action] {
if case .none = permissions { return [] }

guard let result = self.result else { return [] }

var actions = [Action]()
@@ -97,21 +99,30 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
if result.pullRequest {
actions.append(.reviewers)
}
}

if result.subscriptionState == .subscribed {
actions.append(.unsubscribe)
} else {
actions.append(.subscribe)
}

if case .collaborator = permissions {
if result.labels.locked {
actions.append(.unlock)
} else {
actions.append(.lock)
}
}

switch result.labels.status.status {
case .closed:
actions.append(.reopen)
case .open:
actions.append(.close)
case .merged: break
if permissions == .collaborator || permissions == .author {
switch result.labels.status.status {
case .closed:
actions.append(.reopen)
case .open:
actions.append(.close)
case .merged: break
}
}

return actions
}

@@ -144,13 +155,19 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
case .close:
title = Constants.Strings.close
iconName = "x"
case .subscribe:
title = Constants.Strings.subscribe
iconName = "unmute"
case .unsubscribe:
title = Constants.Strings.unsubscribe
iconName = "mute"
}

// Lock always has the divider above it assuming you're a collaborator.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update this comment.

// If you aren't a collaborator (Lock does not show), close has the divider above it.
let separator: Bool
switch action {
case .lock, .unlock: separator = true
case .subscribe, .unsubscribe: separator = true
case .reopen, .close: separator = permissions != .collaborator
default: separator = false
}
@@ -186,6 +203,8 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
case .lock: strongSelf.lock(true)
case .reopen: strongSelf.close(false)
case .close: strongSelf.close(true)
case .subscribe: strongSelf.subscribe(true)
case .unsubscribe: strongSelf.subscribe(false)
}
}
}
@@ -271,6 +290,12 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
)
}

func subscribe(_ doSubscribe: Bool) {
guard let previous = result else { return }
delegate?.willMutateModel(from: self)
client.setSubscription(previous: previous, subscribe: doSubscribe)
Haptic.triggerNotification(.success)
}
func close(_ doClose: Bool) {
guard let previous = result else { return }
delegate?.willMutateModel(from: self)
13 changes: 9 additions & 4 deletions Classes/Issues/IssueResult.swift
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ struct IssueResult: Cachable {
let defaultBranch: String
let fileChanges: FileChanges?
let mergeModel: IssueMergeModel?
let subscriptionState: SubscriptionState

var timelineViewModels: [ListDiffable] {
return timelinePages.reduce([], { $0 + $1.viewModels })
@@ -77,7 +78,8 @@ struct IssueResult: Cachable {
hasIssuesEnabled: Bool? = nil,
viewerCanAdminister: Bool? = nil,
defaultBranch: String? = nil,
mergeModel: IssueMergeModel? = nil
mergeModel: IssueMergeModel? = nil,
subscriptionState: SubscriptionState? = nil
) -> IssueResult {
return IssueResult(
id: id ?? self.id,
@@ -95,7 +97,8 @@ struct IssueResult: Cachable {
viewerCanAdminister: viewerCanAdminister ?? self.viewerCanAdminister,
defaultBranch: defaultBranch ?? self.defaultBranch,
fileChanges: fileChanges,
mergeModel: mergeModel ?? self.mergeModel
mergeModel: mergeModel ?? self.mergeModel,
subscriptionState: subscriptionState ?? self.subscriptionState
)
}

@@ -119,7 +122,8 @@ struct IssueResult: Cachable {
viewerCanAdminister: self.viewerCanAdminister,
defaultBranch: self.defaultBranch,
fileChanges: self.fileChanges,
mergeModel: self.mergeModel
mergeModel: self.mergeModel,
subscriptionState: self.subscriptionState
)
}

@@ -143,7 +147,8 @@ struct IssueResult: Cachable {
viewerCanAdminister: self.viewerCanAdminister,
defaultBranch: self.defaultBranch,
fileChanges: self.fileChanges,
mergeModel: self.mergeModel
mergeModel: self.mergeModel,
subscriptionState: self.subscriptionState
)
}

2 changes: 2 additions & 0 deletions Classes/Issues/IssueType.swift
Original file line number Diff line number Diff line change
@@ -34,6 +34,8 @@ protocol IssueType {
var headPaging: HeadPaging { get }
var viewerCanUpdate: Bool { get }
var fileChanges: FileChanges? { get }
var subscriptionState: SubscriptionState { get }


var reviewRequestModel: IssueAssigneesModel? { get }
func mergeModel(availableTypes: [IssueMergeType]) -> IssueMergeModel?
4 changes: 4 additions & 0 deletions Classes/Issues/PullRequest+IssueType.swift
Original file line number Diff line number Diff line change
@@ -12,6 +12,10 @@ import StyledTextKit

extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsPullRequest: IssueType {

var subscriptionState: SubscriptionState {
return viewerSubscription ?? .unsubscribed
}

var pullRequest: Bool {
return true
}
2 changes: 2 additions & 0 deletions Classes/Views/Constants.swift
Original file line number Diff line number Diff line change
@@ -52,6 +52,8 @@ enum Constants {
static let reviewers = NSLocalizedString("Reviewers", comment: "")
static let clear = NSLocalizedString("Clear", comment: "")
static let preview = NSLocalizedString("Preview", comment: "")
static let subscribe = NSLocalizedString("Subscribe", comment: "")
static let unsubscribe = NSLocalizedString("Unsubscribe", comment: "")
static let overview = NSLocalizedString("Overview", comment: "")
}
}
4 changes: 1 addition & 3 deletions FreetimeTests/SplitViewTests.swift
Original file line number Diff line number Diff line change
@@ -114,9 +114,7 @@ class SplitViewTests: XCTestCase {
client: GithubClient(userSession: nil),
repo: RepositoryDetails(
owner: "Foo",
name: "Bar",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this changed? 🤔

defaultBranch: "Baz",
hasIssuesEnabled: false
name: "Bar"
))

let detail2 = IssuesViewController(
342 changes: 333 additions & 9 deletions gql/API.swift

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions gql/Fragments.graphql
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
fragment subscribableFields on Subscribable {
id
viewerCanSubscribe
viewerSubscription
}

fragment reactionFields on Reactable {
viewerCanReact
reactionGroups {
2 changes: 2 additions & 0 deletions gql/IssueOrPullRequest.graphql
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ query IssueOrPullRequest($owner: String!, $repo: String!, $number: Int!, $page_s
}
issueOrPullRequest(number: $number) {
... on Issue {
viewerSubscription
timeline(last: $page_size, before: $before) {
pageInfo{...headPaging}
nodes {
@@ -182,6 +183,7 @@ query IssueOrPullRequest($owner: String!, $repo: String!, $number: Int!, $page_s
title
}
... on PullRequest {
viewerSubscription
timeline(last: $page_size, before: $before) {
pageInfo{...headPaging}
nodes {
7 changes: 7 additions & 0 deletions gql/UpdateSubscription.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mutation UpdateSubscription($subscribable_Id: ID!, $subscription_state: SubscriptionState!) {
updateSubscription(input:{subscribableId: $subscribable_Id, state: $subscription_state}) {
subscribable{
...subscribableFields
}
}
}