-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[wip] Remove Project.LiveStream #63
Conversation
…sts that will be fixed soon
) | ||
|
||
self.configureChildViewControllersWithProjectAndLiveStreams = | ||
Signal.combineLatest(project, liveStreamEvents, refTag) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the combineLatest
that I mentioned in the PR. This emits quite a few times and each will cause a reload of the datasource and table view, perhaps we can buffer/throttle it in some way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think in this case we actually want all of those emissions! for once!
basically we want the most recent values of project and live streams for any emission and update the data source accordingly.
or, we could model it to emit only twice, once for the initial data and once we have both the newest project and newest live streams. however, for that we'll need to add a timeout to the live streams so that it doesnt prevent the project from showing if it fails. wanna discuss over slack how to do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did a first pass review of this. most of it looks great and is so much simpler. let's attack my comments and then i'll do one more pass and we'll get it merged!
@@ -126,7 +124,7 @@ public final class LiveStreamContainerViewController: UIViewController { | |||
|
|||
_ = self.loaderActivityIndicatorView | |||
|> UIActivityIndicatorView.lens.activityIndicatorViewStyle .~ .white | |||
|> UIActivityIndicatorView.lens.animating .~ true | |||
|> UIActivityIndicatorView.lens.hidesWhenStopped .~ true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 i remember us talking about this. important to do only stylistic stuff in bindStyles
since it can be called multiple times (like on rotation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀
return .replay | ||
} | ||
|
||
return .countdown | ||
} | ||
|
||
private func stateContext(forLiveStream liveStream: Project.LiveStream) -> LiveStreamStateContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
boy does it warm my heart to see this code go away!
@@ -105,16 +114,17 @@ final class ProjectPamphletViewModelTests: TestCase { | |||
self.setNavigationBarAnimated.assertValues([false, true, false]) | |||
} | |||
|
|||
// Tests that ref tags and referral credit cookies are tracked in koala and saved like we expect. | |||
//Tests that ref tags and referral credit cookies are tracked in koala and saved like we expect. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 whatchu doin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
woops!
@@ -213,22 +203,8 @@ LiveStreamCountdownViewModelInputs, LiveStreamCountdownViewModelOutputs { | |||
public var outputs: LiveStreamCountdownViewModelOutputs { return self } | |||
} | |||
|
|||
private func flipProjectLiveStreamToLive(project: Project, currentLiveStream: Project.LiveStream) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙌
@@ -285,16 +238,3 @@ public final class LiveStreamEventDetailsViewModel: LiveStreamEventDetailsViewMo | |||
public var inputs: LiveStreamEventDetailsViewModelInputs { return self } | |||
public var outputs: LiveStreamEventDetailsViewModelOutputs { return self } | |||
} | |||
|
|||
private func fetchEvent(forProject project: Project, liveStream: Project.LiveStream, event: LiveStreamEvent?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙌
.map { $0.liveStreams } | ||
.skipNil() | ||
let trackLiveStreamEvents = liveStreamEvents | ||
.skip(first: 1) // Skip first as we are prefixing with an empty array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice nice, im guess a test caught this right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yip!
} | ||
} | ||
|
||
public func == (lhs: LiveStreamEventsEnvelope, rhs: LiveStreamEventsEnvelope) -> Bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious if where this is needed...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah probably not necessary, remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah if it's not used, prob for the best
|
||
let urlString = "\(Secrets.LiveStreams.Api.base)/projects/\(projectId)\(uidString)" | ||
guard let url = URL(string: urlString) else { | ||
observer.send(error: .invalidEventId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oof, so "invalidEventId" isn't quite the right error here
.flatMap { "?uid=\($0)" } | ||
.coalesceWith("") | ||
|
||
let urlString = "\(Secrets.LiveStreams.Api.base)/projects/\(projectId)\(uidString)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this we cant really test this function i'd like to do something a little safer around url construction. we can use URLComponents
for the query params
let url = URL(string: Secrets.LiveStreams.Api.base)?.appendingPathComponent("projects/\(projectId)")
var components = url.flatMap { URLComponents(url: $0, resolvingAgainstBaseURL: false) }
components?.queryItems = uid.map { [URLQueryItem(name: "uid", value: "\($0)")] }
guard let url = components?.url ...
did that without the type checker, but something along those lines...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok cool, looks good!
@@ -132,7 +132,7 @@ internal final class LiveStreamViewModel: LiveStreamViewModelType, LiveStreamVie | |||
isMaxOpenTokViewersReached, | |||
|
|||
liveStreamEvent | |||
.map { event in event.stream.isRtmp || didEndNormally(event: event) } | |||
.map { event in event.isRtmp ?? false || didEndNormally(event: event) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm a big fan of doing explicit event.isRtmp == .some(true)
over coalescing for booleans
# Conflicts: # Screenshots/_64/Kickstarter_Framework_iOSTests.ProjectPamphletContentViewControllerTests/testNonBacker_LiveProject_WithLiveStreams_lang_es_device_pad@2x.png # Screenshots/_64/Kickstarter_Framework_iOSTests.ProjectPamphletContentViewControllerTests/testNonBacker_LiveProject_WithLiveStreams_lang_es_device_phone4_7inch@2x.png
@@ -214,14 +214,12 @@ LiveStreamContainerViewModelInputs, LiveStreamContainerViewModelOutputs { | |||
|
|||
let hideWhenReplay = Signal.merge( | |||
project.mapConst(true), | |||
event.map { !$0.liveNow }, | |||
self.showErrorAlert.mapConst(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The screenshot tests that were failing were because of this line - previously we would fetch the LiveStreamEvent
asynchronously and if it failed we would keep some views hidden. Because this is now available immediately at initialisation this sort of thing is no longer necessary as errors are now only shown for live stream playback issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest we probably don't even need the project.mapConst(true)
anymore either as we know immediately if it's live or a replay. I'll look at maybe removing that for simplification.
…arter/ios-oss into remove_project_livestream
) | ||
|
||
self.createAndConfigureLiveStreamViewController = Signal.combineLatest(project, event) | ||
self.configureLiveStreamViewController = Signal.combineLatest(project, event.skip(first: 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the feeling on using .skip(first: 1)
here? We know that we only want to configure the live stream view controller once the fresh event has been fetched and our tests confirm this so should be cool?
# Conflicts: # Library/ViewModels/ProjectPamphletSubpageCellViewModel.swift # Screenshots/_64/Kickstarter_Framework_iOSTests.ProjectPamphletContentViewControllerTests/testNonBacker_LiveProject_WithLiveStreams_lang_de_device_pad@2x.png # Screenshots/_64/Kickstarter_Framework_iOSTests.ProjectPamphletContentViewControllerTests/testNonBacker_LiveProject_WithLiveStreams_lang_de_device_phone4_7inch@2x.png # Screenshots/_64/Kickstarter_Framework_iOSTests.ProjectPamphletContentViewControllerTests/testNonBacker_LiveProject_WithLiveStreams_lang_en_device_pad@2x.png # Screenshots/_64/Kickstarter_Framework_iOSTests.ProjectPamphletContentViewControllerTests/testNonBacker_LiveProject_WithLiveStreams_lang_en_device_phone4_7inch@2x.png
These tests will pass once #67 is merged. |
# Conflicts: # Frameworks/ios-ksapi
.map { URL(string: $0.photo.full) } | ||
self.projectImageUrl = Signal.merge( | ||
configData.mapConst(nil), | ||
Signal.combineLatest( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mbrandonw this was a weird one, even though configData
is definitely emitting before project
(because project
is configData.map(first)
, I couldn't seem to get nil
to emit first without merging in that combineLatest
?
project.map { URL(string: $0.photo.full) }, | ||
configData.ignoreValues() | ||
).map(first) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think we can just re-express as:
self.projectImageUrl = Signal.merge(
self.viewDidLoadProperty.signal.mapConst(nil),
project.map { URL(string: $0.photo.full) }
)
?
also down to just remove the placeholder image in the storyboard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.projectImageUrl = project | ||
.map { URL(string: $0.photo.full) } | ||
self.projectImageUrl = project.flatMap { project in | ||
SignalProducer(value: URL(string: project.photo.full)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mbrandonw ok so this fixes it and the tests pass. I know using a SignalProducer
might not be the desired approach but it's handy to be able to prefix
it with nil
and makes what we're wanting to do pretty clear?
I could not get nil
to emit first using viewDidLoad
without a nested combineLatest
which I also didn't like.
And I think having this testable is safer than only removing the image from the story board in case it was ever added back?
When we initially began integrating the LiveStream framework we were receiving some information about live streams along with the
Project
fetch JSON payload. All that this contained was the live streameventId
, a title, startDate, etc. We then, upon launching theLiveStreamContainerViewController
orLiveStreamCountdownViewController
, would perform an additional fetch for theLiveStreamEvent
which would tell us everything else about the event.This quickly turned out to be a bad idea and there was just too much information being duplicated between
Project.LiveStream
andLiveStreamEvent
so the decision was taken to removeProject.LiveStream
in its entirety and to fetch a list ofLiveStreamEvent
s from a new endpoint based on theProject
's ID. We perform this fetch when we land on a project page and once the list is returned we reload the project's subpage cells with any live streams that may have been returned.The amount of files that this touched is a lot more than I'd anticipated! But it's much simpler this was and it should also prepare us for a lot the work that we would have needed to do for discovery.
Things I've done here:
Project.LiveStream
from models and tests that referenced it.LiveStreamEvent
fromLiveStreamEventDetailsViewModel
.LiveStreamEvent
to now expect this as non-optional.Project.LiveStream
.LiveStreamEvent
for the live stream discovery work. This was necessary to parse the new endpoint @Jwomers had set up for us.Things to review:
combineLatest
ofproject
,freshProject
andliveStreams
?Things still needed:
ProjectPamphletViewModelTests
and confirm that we're still satisfied withProjectPamphletContentDataSourceTests
(these should be fine).LiveStreamEvent.startDate
to aTimeInterval
as I went through this 😐