Skip to content

Commit

Permalink
iOS: add reminders time to notification onboarding (#787)
Browse files Browse the repository at this point in the history
^ALTAPPS-1070
  • Loading branch information
ivan-magda authored Dec 15, 2023
1 parent a266330 commit f1e80d7
Show file tree
Hide file tree
Showing 15 changed files with 113 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
{
"images" : [
{
"filename" : "notifications-onboarding-illustration-light.pdf",
"idiom" : "universal"
"filename" : "notifications-onboarding-illustration.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "notifications-onboarding-illustration-dark.pdf",
"idiom" : "universal"
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ class PanModalPresentableViewController: UIViewController, PanModalPresentable {

var currentPresentationState: PanModalPresentationController.PresentationState?

var onDisappear: (() -> Void)?
var onDidAppear: (() -> Void)?
var onDidDisappear: (() -> Void)?

var shouldUpdateAdditionalSafeAreaInsets: Bool { true }

Expand All @@ -67,9 +68,14 @@ class PanModalPresentableViewController: UIViewController, PanModalPresentable {
self.updateAdditionalSafeAreaInsets()
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
onDidAppear?()
}

override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
onDisappear?()
onDidDisappear?()
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ private struct PanModalViewControllerModifier: ViewModifier {

private func updatePresentation(_ isPresented: Bool) {
defer {
panModalViewController?.onDisappear = {
panModalViewController?.onDidDisappear = {
self.isPresented = false
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,6 @@ enum Images {
}
}

// MARK: - NotificationsOnboarding -

enum NotificationsOnboarding {
static let illustration = "notifications-onboarding-illustration"
}

// MARK: - TopicsRepetitions -

enum TopicsRepetitions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,10 @@ enum Strings {
// MARK: - NotificationsOnboarding -

enum NotificationsOnboarding {
static let title = sharedStrings.notifications_onboarding_title.localized()
static let subtitle = sharedStrings.notifications_onboarding_description.localized()
static let title = sharedStrings.notifications_onboarding_title_new.localized()

static let dailyStudyRemindersIntervalPrefix =
sharedStrings.notifications_onboarding_daily_study_reminders_interval_prefix.localized()

static let buttonPrimary = sharedStrings.notifications_onboarding_button_allow.localized()
static let buttonSecondary = sharedStrings.notifications_onboarding_button_not_now.localized()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ final class NotificationsOnboardingAssembly: UIKitAssembly {
)
notificationsOnboardingViewModel.moduleOutput = moduleOutput

let panModalPresenter = PanModalPresenter()

let notificationsOnboardingView = NotificationsOnboardingView(
viewModel: notificationsOnboardingViewModel
viewModel: notificationsOnboardingViewModel,
panModalPresenter: panModalPresenter
)

let hostingController = StyledHostingController(
rootView: notificationsOnboardingView
)
hostingController.navigationItem.largeTitleDisplayMode = .never

panModalPresenter.rootViewController = hostingController

return hostingController
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ final class NotificationsOnboardingViewModel: FeatureViewModel<
oldState: NotificationsOnboardingFeature.ViewState,
newState: NotificationsOnboardingFeature.ViewState
) -> Bool {
false
!oldState.isEqual(newState)
}

func doPrimaryAction() {
Expand All @@ -33,6 +33,18 @@ final class NotificationsOnboardingViewModel: FeatureViewModel<
onNewMessage(NotificationsOnboardingFeatureMessageNotNowClicked())
}

func doDailyStudyRemindsIntervalButtonAction() {
onNewMessage(NotificationsOnboardingFeatureMessageDailyStudyRemindsIntervalHourClicked())
}

func doDailyStudyRemindsIntervalStartHourSelected(startHour: Int) {
onNewMessage(
NotificationsOnboardingFeatureMessageDailyStudyRemindsIntervalStartHourSelected(
startHour: Int32(startHour)
)
)
}

func doRequestNotificationPermission() {
Task(priority: .userInitiated) {
let isGranted = await notificationsRegistrationService.requestAuthorizationIfNeeded()
Expand All @@ -56,4 +68,16 @@ final class NotificationsOnboardingViewModel: FeatureViewModel<
func logViewedEvent() {
onNewMessage(NotificationsOnboardingFeatureMessageViewedEventMessage())
}

func logDailyStudyRemindersIntervalStartHourPickerModalShownEvent() {
onNewMessage(
NotificationsOnboardingFeatureMessageDailyStudyRemindersIntervalStartHourPickerModalShownEventMessage()
)
}

func logDailyStudyRemindersIntervalStartHourPickerModalHiddenEvent() {
onNewMessage(
NotificationsOnboardingFeatureMessageDailyStudyRemindersIntervalStartHourPickerModalHiddenEventMessage()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extension NotificationsOnboardingContentView {
struct Appearance {
let interitemSpacing = LayoutInsets.smallInset

let illustrationHeight: CGFloat = 320
let illustrationHeight: CGFloat = 230

let maxWidth: CGFloat = DeviceInfo.current.isPad ? 400 : .infinity
}
Expand All @@ -17,6 +17,9 @@ struct NotificationsOnboardingContentView: View {

private let actionButtonsFeedbackGenerator = FeedbackGenerator(feedbackType: .selection)

let formattedDailyStudyRemindersInterval: String
let onDailyStudyRemindsIntervalButtonTap: () -> Void

let onPrimaryButtonTap: () -> Void
let onSecondaryButtonTap: () -> Void

Expand Down Expand Up @@ -56,21 +59,33 @@ struct NotificationsOnboardingContentView: View {
}
}

private var header: some View {
@MainActor private var header: some View {
VStack(alignment: .center, spacing: appearance.interitemSpacing) {
Text(Strings.NotificationsOnboarding.title)
.font(.title).bold()
.font(.title)
.foregroundColor(.newPrimaryText)

Text(Strings.NotificationsOnboarding.subtitle)
.font(.body)
.foregroundColor(.newSecondaryText)
HStack {
Text(Strings.NotificationsOnboarding.dailyStudyRemindersIntervalPrefix)
.font(.body)
.foregroundColor(.newPrimaryText)

Button(
formattedDailyStudyRemindersInterval,
action: {
actionButtonsFeedbackGenerator.triggerFeedback()
onDailyStudyRemindsIntervalButtonTap()
}
)
.buttonStyle(GhostButtonStyle(maxWidth: nil))
.animation(.default, value: formattedDailyStudyRemindersInterval)
}
}
.multilineTextAlignment(.center)
}

private var illustration: some View {
Image(Images.NotificationsOnboarding.illustration)
Image(.notificationsOnboardingIllustration)
.renderingMode(.original)
.resizable()
.aspectRatio(contentMode: .fit)
Expand Down Expand Up @@ -105,12 +120,16 @@ struct NotificationsOnboardingContentView: View {
struct NotificationsOnboardingContentView_Previews: PreviewProvider {
static var previews: some View {
NotificationsOnboardingContentView(
formattedDailyStudyRemindersInterval: "12:00 – 13:00",
onDailyStudyRemindsIntervalButtonTap: {},
onPrimaryButtonTap: {},
onSecondaryButtonTap: {}
)
.previewDevice(PreviewDevice(rawValue: "iPhone 14 Pro"))

NotificationsOnboardingContentView(
formattedDailyStudyRemindersInterval: "12:00 – 13:00",
onDailyStudyRemindsIntervalButtonTap: {},
onPrimaryButtonTap: {},
onSecondaryButtonTap: {}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ struct NotificationsOnboardingView: View {

@StateObject var viewModel: NotificationsOnboardingViewModel

let panModalPresenter: PanModalPresenter

var body: some View {
ZStack {
UIViewControllerEventsWrapper(onViewDidAppear: viewModel.logViewedEvent)

BackgroundView(color: appearance.backgroundColor)

NotificationsOnboardingContentView(
formattedDailyStudyRemindersInterval: viewModel.state.formattedDailyStudyRemindersInterval,
onDailyStudyRemindsIntervalButtonTap: viewModel.doDailyStudyRemindsIntervalButtonAction,
onPrimaryButtonTap: viewModel.doPrimaryAction,
onSecondaryButtonTap: viewModel.doSecondaryAction
)
Expand All @@ -45,18 +49,36 @@ private extension NotificationsOnboardingView {
viewModel.doCompleteOnboarding()
case .requestNotificationPermission:
viewModel.doRequestNotificationPermission()
case .showDailyStudyRemindersIntervalStartHourPickerModal:
#warning("TODO: ALTAPPS-1070 handle this")
case .showDailyStudyRemindersIntervalStartHourPickerModal(let data):
let panModal = ProfileDailyStudyRemindersPickerViewController(
rows: data.intervals,
initialRowIndex: Int(data.dailyStudyRemindersStartHour),
onDidConfirmRow: { [weak viewModel, weak panModalPresenter] selectedIntervalIndex in
guard let viewModel,
let panModalPresenter else {
return
}

viewModel.doDailyStudyRemindsIntervalStartHourSelected(startHour: selectedIntervalIndex)
panModalPresenter.dismissPanModal()
}
)

panModal.onDidAppear = { [weak viewModel] in
viewModel?.logDailyStudyRemindersIntervalStartHourPickerModalShownEvent()
}
panModal.onDidDisappear = { [weak viewModel] in
viewModel?.logDailyStudyRemindersIntervalStartHourPickerModalHiddenEvent()
}

panModalPresenter.presentPanModal(panModal)
}
}
}

// MARK: - NotificationsOnboardingView_Previews: PreviewProvider -
// MARK: - Preview -

struct NotificationsOnboardingView_Previews: PreviewProvider {
static var previews: some View {
UIKitViewControllerPreview {
NotificationsOnboardingAssembly(output: nil).makeModule()
}
}
@available(iOS 17, *)
#Preview {
NotificationsOnboardingAssembly(output: nil).makeModule()
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ struct ProfileView: View {
streakFreezeState: streakFreezeState,
onActionButtonTap: viewModel.doStreakFreezeModalButtonTapped
)
panModal.onDisappear = { [weak viewModel] in
panModal.onDidDisappear = { [weak viewModel] in
viewModel?.logStreakFreezeModalHiddenEvent()
}

Expand Down

0 comments on commit f1e80d7

Please sign in to comment.