-
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 stretch goal] Live stream discovery #53
Conversation
} | ||
} | ||
|
||
private func sorted(liveStreamEvents: [LiveStreamEvent]) -> [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.
if the code in this function looks suspiciously like one i cooked up in #45, then you are right. unfortunately i cant share the code cause one operates on LiveStreamEvent
s and the other on Project.LiveStream
:(((
the models are a bit messy due to hitting two different apis right now. we are gonna try to find a way to bridge these two worlds soon.
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.
actually never mind on this! with @justinswart's recent refactoring work to get rid of Project.LiveStream
#63 i was able to share all of this code.
events.forEach { event in | ||
self.appendRow(value: event, cellClass: LiveStreamDiscoveryCell.self, toSection: title.section) | ||
} | ||
} |
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 a fun lil thing. it does the work to create titles for live/upcoming/recent with live streams grouped into them. it's heavily tested too...
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.
cool! just thinking, we coullllld in the future, maybe, update Sorts to have these 3 categories.... if this feature becomes really popular, it might be nice to go directly to these sections. Could have a sort option for streams your friends subscribe too..
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.
@theginbin ^^^ that would be awesome! a really nice lil step after the initial release.
@@ -23,11 +23,11 @@ internal final class LiveStreamContainerViewControllerTests: TestCase { | |||
|> Project.LiveStream.lens.startDate .~ (MockDate().timeIntervalSince1970 - 86_400) | |||
|> Project.LiveStream.lens.isLiveNow .~ false | |||
let liveStreamEvent = .template | |||
|> LiveStreamEvent.lens.stream.hasReplay .~ 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 got rid of the Stream
sub-struct of LiveStreamEvent
in order to flatten the models a bit.
@@ -377,32 +379,3 @@ public final class LiveStreamCountdownViewController: UIViewController { | |||
self.eventDetailsViewModel.inputs.subscribeButtonTapped() | |||
} | |||
} | |||
|
|||
// Returns a fancy monospaced font for the countdown. | |||
private func countdownFont(label: UILabel) -> UIFont { |
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.
we use this twice now so i extracted it to a helper on UIFont
, can just do baseFont.countdownMonospaced
nav.viewControllers = [vc] | ||
DispatchQueue.main.async { | ||
self.present(nav, animated: true, completion: nil) | ||
} |
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.
we were seeing some weird flickering of the screen when not using DispatchQueue.main.async
:/ it seems to be entirely due to initializing the nav controller and then setting its controllers, as opposed to using the UINavigationController(rootViewController:)
initializer.
/// | ||
/// - returns: A signal producer. | ||
public func countdownProducer(to date: Date) | ||
-> SignalProducer<(day: String, hour: String, minute: String, second: String), NoError> { |
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 a fun function! we use it in the countdown controller and the countdown of the live stream discovery cells.
lots of tests here: https://github.com/kickstarter/ios-oss/pull/53/files#diff-c7a229a8e1612db2ccad1308b69894a5
@@ -297,6 +297,8 @@ private func stringsForTitle(params: DiscoveryParams) -> (filter: String, subcat | |||
|
|||
if params.staffPicks == true { | |||
filterText = Strings.Projects_We_Love() | |||
} else if params.hasLiveStreams == .some(true) { | |||
filterText = "Kickstarter Live" |
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.
do we need to localize this? or is this a branding thing? /cc @ktman @rreymundi
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.
Hey @awakinglim mind weighing in here? Curious what you think from brand, UX, localization perspective? This will appear in the discovery overlay and let you access cards for upcoming, live, and recent streams.
var discoveryPagesViewHidden: Signal<Bool, NoError> { get } | ||
|
||
/// Emits a boolean that determines if the live stream discovery view is hidden. | ||
var liveStreamDiscoveryViewHidden: Signal<Bool, NoError> { get } |
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.
so the livestream discovery controller is an embedded controller in DiscoveryViewController
, which means the discovery pages are its siblings. this vm will now take care of hiding and showing them appropriately.
let currentParams = Signal.merge( | ||
self.viewWillAppearProperty.signal.take(first: 1).mapConst(DiscoveryViewModel.defaultParams), | ||
self.filterWithParamsProperty.signal.skipNil() | ||
) |
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.
just a small refactor while i was here. don't know why i jumped through hoops for that weird producer thing when it can be done so much more easily...
|
||
_ = self.hoursSubtitleLabel | ||
|> UILabel.lens.text %~ { _ in localizedString(key: "days", defaultValue: "hours") } | ||
|> UILabel.lens.text %~ { _ in Strings.hours() } |
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.
we had a typo here! we were using days for hours! 😱
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.
😳 good catch!
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.
Just a couple observations!
Great work! This is going to be great 👍
@@ -49,6 +49,7 @@ | |||
"Continue_to_payment" = "Continue to payment"; | |||
"Continue_to_update_pledge" = "Continue to update pledge"; | |||
"Couldnt_add_attachment" = "Couldn't add attachment"; | |||
"Couldnt_open_live_stream_Try_again_later" = "Couldn‘t open live stream. Try again later."; |
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.
Is that a backtick in Couldn't? Doesn't look like a normal apostrophe.
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.
it's a "smart" apostrophe http://smartquotesforsmartpeople.com
that site is a bit much :/
@testable import Kickstarter_Framework | ||
@testable import Library | ||
@testable import LiveStream | ||
@testable import KsApi |
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.
Could alphabetize
parent.view.setNeedsLayout() | ||
vc.view.setNeedsLayout() | ||
parent.view.setNeedsUpdateConstraints() | ||
vc.view.setNeedsUpdateConstraints() |
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 were all the layout passes needed for?
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 cant remember! lemme try without and see what happens
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.
not needed at all! removing!
func testCountdownProducer() { | ||
let future: TimeInterval = TimeInterval(1*60*60*24) + TimeInterval(16*60*60) + TimeInterval(34*60) + 2 | ||
let futureDate = MockDate().addingTimeInterval(future).date | ||
let countdown = countdownProducer(to: futureDate) |
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.
So DRY 😄
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.
brave new world for me!
@testable import KsApi | ||
@testable import Library | ||
@testable import LiveStream | ||
import Prelude |
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.
Alphabetize
@testable import KsApi | ||
@testable import Library | ||
@testable import LiveStream | ||
import Prelude |
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.
☝️
@@ -91,7 +101,7 @@ LiveStreamContainerViewModelInputs, LiveStreamContainerViewModelOutputs { | |||
) | |||
.map(first) | |||
|
|||
let initialEvent = configData.map(second) | |||
let initialEvent = configData.map { _, event, _, _ in 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.
We should add a fourth
sometime 😄
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.
we really should!
} | ||
.map { (day: $0.day ?? 0, hour: $0.hour ?? 0, minute: $0.minute ?? 0, second: $0.second ?? 0) } | ||
.take(first: 1) | ||
.switchMap { countdownProducer(to: $0.startDate) } |
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.
👍
@@ -64,6 +64,40 @@ public struct LiveStreamEvent: Equatable { | |||
public var definitelyHasReplay: Bool { | |||
return self.hasReplay && self.replayUrl != nil | |||
} | |||
|
|||
public static func canonicalLiveStreamEventComparator(now: Date) -> Prelude.Comparator<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.
🚀
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.
👍
Hey everyone, Justin and I wanted to get one more thing done before my time in cape town is over (sniff), and so we thought about adding a lil bit of discovery to the live streams. Currently you can only find live streams if you go straight to a project, no deep-linking or push notifications yet :/
So, we just added a lil "Kickstarter Live" entry to the discovery filters:
Tapping that takes you to a feed of live, upcoming and recent live streams:
Tapping those cards either goes to the countdown, live stream or reply.
NB
This PR has a few things that I wanna call out:
We are now hitting two api endpoints on the live api: one to get a list of events and one to get a single event. these two endpoints return subtly different json unfortunately and so i had to do a bit of gymnastics to accommodate for this. i'll call out the lines in my self-review.
Due to the above, i had to change the live stream models quite a bit, which then wreaked havoc on the lenses. So, I wanted to try something new to ease the creation of lenses for us (at least until we get code gen). If we set all of the fields of a struct to
public private(set) var field
, we still get to act like we are immutable to the outside world, but inside this file only we can make lenses like:not only is it less code, but it doesnt break when we add new fields. whatdya'll think?