Skip to content
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

Feature/event notification #155

Merged
merged 15 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
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
267 changes: 132 additions & 135 deletions NOICommunity.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ private extension AccessNotGrantedViewController {
let detailedAttributedText: NSAttributedString = {
let text = String.localizedStringWithFormat(
.localized("outsider_user_body_format"),
userInfo.email ?? "N/D"
userInfo.email ?? .localized("label_no_value")
)
let mAttributedText = NSMutableAttributedString(string: text,
attributes: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ class BaseCoordinator: NSObject, CoordinatorType {
var childCoordinators: [CoordinatorType] = []

var dependencyContainer: DependencyRepresentable


var topViewController: UIViewController? {
guard let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? UIWindowSceneDelegate
else { return nil }

return sceneDelegate.window??.topViewController
}

@available(*, unavailable)
override init() {
fatalError("\(#function) not available")
Expand Down
199 changes: 140 additions & 59 deletions NOICommunity/Coordinator/Implementations/Custom/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ final class AppCoordinator: BaseNavigationCoordinator {
private var pendingDeepLinkIntent: DeepLinkIntent?

private weak var tabCoordinator: TabCoordinator!

override func start(animated: Bool) {
NotificationCenter
.default
Expand All @@ -46,6 +46,7 @@ final class AppCoordinator: BaseNavigationCoordinator {
} else {
showLoadUserInfo()
}

}

func handle(deepLinkIntent: DeepLinkIntent) {
Expand All @@ -59,6 +60,8 @@ final class AppCoordinator: BaseNavigationCoordinator {
switch deepLinkIntent {
case .showNews(let newsId):
showNewsDetails(newsId: newsId, sender: deepLinkIntent)
case .showEvent(let eventId):
showEventDetails(eventId: eventId, sender: deepLinkIntent)
}
}

Expand Down Expand Up @@ -174,85 +177,163 @@ private extension AppCoordinator {
showAuthCoordinator(animated: animated)
}

func showNewsExternalLink(of news: Article, sender: Any?) {
func showNewsExternalLink(
of news: Article,
from viewController: UIViewController
) {
let author = localizedValue(from: news.languageToAuthor)
let safariVC = SFSafariViewController(url: author!.externalURL!)
navigationController.presentedViewController?.present(
navigationController.presentedViewController?.present(
safariVC,
animated: true
)
}

func showNewsAskAQuestion(for news: Article, sender: Any?) {
func showNewsAskAQuestion(
for news: Article,
from viewController: UIViewController
) {
let author = localizedValue(from: news.languageToAuthor)
navigationController.presentedViewController?.mailTo(
navigationController.presentedViewController?.mailTo(
author!.email!,
delegate: self,
completion: nil
)
}

func showNewsDetails(newsId: String, sender: Any?) {
func configureBindings(
viewModel: NewsDetailsViewModel,
detailsViewController: NewsDetailsViewController
) {
viewModel.showExternalLinkPublisher
.sink { [weak self] (news, sender) in
self?.showNewsExternalLink(of: news, sender: sender)
}
.store(in: &subscriptions)
viewModel.showAskAQuestionPublisher
.sink { [weak self] (news, sender) in
self?.showNewsAskAQuestion(for: news, sender: sender)
}
.store(in: &subscriptions)
viewModel.$result
.sink { [weak detailsViewController] news in
guard let news = news
else { return }

detailsViewController?.navigationItem.title = localizedValue(
from: news.languageToDetails
)?
.title
}
.store(in: &subscriptions)
}
guard let topViewController
else { return }

let viewModel = dependencyContainer.makeNewsDetailsViewModel(
availableNews: nil
newsId: newsId
)

let detailsVC = dependencyContainer.makeNewsDetailsViewController(
newsId: newsId,
viewModel: viewModel
)

configureBindings(
viewModel: viewModel,
detailsViewController: detailsVC
)

detailsVC.navigationItem.title = nil
detailsVC.navigationItem.largeTitleDisplayMode = .never
detailsVC.navigationItem.leftBarButtonItem = UIBarButtonItem(
image: UIImage(systemName: "xmark.circle.fill"),
style: .plain,
target: self,
action: #selector(closeModal(sender:))
)
detailsVC.modalPresentationStyle = .fullScreen

navigationController.present(
NavigationController(rootViewController: detailsVC),
let pageVC = {
let pageVC = dependencyContainer.makeNewsPageViewController(
viewModel: viewModel
)

pageVC.externalLinkActionHandler = { [weak self, weak pageVC] in
guard let pageVC
else { return }

self?.showNewsExternalLink(of: $0, from: pageVC)
}
pageVC.askQuestionActionHandler = { [weak self, weak pageVC] in
guard let pageVC
else { return }

self?.showNewsAskAQuestion(for: $0, from: pageVC)
}

pageVC.navigationItem.leftBarButtonItem = UIBarButtonItem(
image: UIImage(systemName: "xmark.circle.fill"),
primaryAction: UIAction { [weak pageVC] _ in
pageVC?.dismiss(animated: true)
})
pageVC.modalPresentationStyle = .fullScreen

return pageVC
}()

topViewController.present(
NavigationController(rootViewController: pageVC),
animated: true
)
}

@objc func closeModal(sender: Any?) {
navigationController.dismiss(animated: true)
}

func addEventToCalendar(
_ event: Event,
from viewController: UIViewController
) {
EventsCalendarManager.shared.presentCalendarModalToAddEvent(
event: event.toCalendarEvent(),
from: viewController
) { [weak viewController] result in
guard case let .failure(error) = result
else { return }

if let calendarError = error as? CalendarError {
viewController?.showCalendarError(calendarError)
} else {
viewController?.showError(error)
}
}
}

func locateEvent(
_ event: Event,
from viewController: UIViewController
) {
let mapViewController = MapWebViewController()
mapViewController.url = event.mapURL ?? .map
mapViewController.navigationItem.title = event.mapURL != nil ?
event.venue:
.localized("title_generic_noi_techpark_map")
viewController.navigationController?.pushViewController(
mapViewController,
animated: true
)
}

func signupEvent(
_ event: Event,
from viewController: UIViewController
) {
UIApplication.shared.open(
event.signupURL!,
options: [:],
completionHandler: nil
)
}

func showEventDetails(eventId: String, sender: Any?) {
guard let topViewController
else { return }

let viewModel = dependencyContainer.makeEventDetailsViewModel(
eventId: eventId
)
let pageVC = {
let pageVC = dependencyContainer.makeEventPageViewController(
viewModel: viewModel
)

pageVC.addToCalendarActionHandler = { [weak self, weak pageVC] in
guard let pageVC
else { return }

self?.addEventToCalendar($0, from: pageVC)
}
pageVC.locateActionHandler = { [weak self, weak pageVC] in
guard let pageVC
else { return }

self?.locateEvent($0, from: pageVC)
}
pageVC.signupActionHandler = { [weak self, weak pageVC] in
guard let pageVC
else { return }


self?.signupEvent($0, from: pageVC)
}

pageVC.navigationItem.leftBarButtonItem = UIBarButtonItem(
image: UIImage(systemName: "xmark.circle.fill"),
primaryAction: UIAction { [weak pageVC] _ in
pageVC?.dismiss(animated: true)
})
pageVC.modalPresentationStyle = .fullScreen

return pageVC
}()

topViewController.present(
NavigationController(rootViewController: pageVC),
animated: true
)
}

func showAccessNotGrantedCoordinator(animated: Bool) {
let accessNotGrantedCoordinator = AccessNotGrantedCoordinator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import AppPreferencesClient
// MARK: - Refresh News List Notification

let refreshNewsListNotification = Notification.Name("refreshNewsList")
let refreshEventsListNotification = Notification.Name("refreshEventsList")

// MARK: - RootCoordinator

Expand Down Expand Up @@ -52,6 +53,8 @@ final class RootCoordinator: BaseRootCoordinator {
return
case .showNews(newsId: _):
refreshNewsList()
case .showEvent(eventId: _):
refreshEventsList()
}
}

Expand Down Expand Up @@ -108,5 +111,11 @@ private extension RootCoordinator {
.default
.post(name: refreshNewsListNotification, object: self)
}


func refreshEventsList() {
NotificationCenter
.default
.post(name: refreshEventsListNotification, object: self)
}

}
5 changes: 5 additions & 0 deletions NOICommunity/DeepLinking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import UIKit

enum DeepLinkIntent {
case showNews(newsId: String)
case showEvent(eventId: String)
}

// MARK: - DeepLinkManager
Expand Down Expand Up @@ -42,6 +43,9 @@ struct DeepLinkManager {
case (URLConstant.newsDetailsPath, let newsId):
// Matches: <customURLScheme><host>/newsDetails/{newsId}
return .showNews(newsId: newsId)
case (URLConstant.eventDetailsPath, let eventId):
// Matches: <customURLScheme><host>/eventDetails/{eventId}
return .showEvent(eventId: eventId)
default:
return nil
}
Expand Down Expand Up @@ -71,6 +75,7 @@ private extension DeepLinkManager {
static let customURLScheme = "noi-community"
static let host = "it.bz.noi.community"
static let newsDetailsPath = "newsDetails"
static let eventDetailsPath = "eventDetails"
}

enum NotificationConstant {
Expand Down
Loading