Skip to content

Commit

Permalink
이메일 미인증시 CTA 개선 (#207)
Browse files Browse the repository at this point in the history
* improve error handling logic, add confirm action when email not veritifed

* Apply SwiftFormat changes

* improve logout logic

* Apply SwiftFormat changes

Co-authored-by: shp7724 <[email protected]>
  • Loading branch information
shp7724 and shp7724 authored Jan 16, 2023
1 parent 6fbff29 commit 3bd0096
Show file tree
Hide file tree
Showing 14 changed files with 83 additions and 28 deletions.
5 changes: 1 addition & 4 deletions SNUTT-2022/SNUTT/AppState/States/ReviewState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ class WebViewPreloadManager {
private var bag = Set<AnyCancellable>()

func preload(url: URL, accessToken: String) {
if eventSignal != nil && webView != nil {
return
}
eventSignal = eventSignal ?? .init()
webView = webView ?? WKWebView(cookies: NetworkConfiguration.getCookiesFrom(accessToken: accessToken))
webView = WKWebView(cookies: NetworkConfiguration.getCookiesFrom(accessToken: accessToken))
coordinator = coordinator ?? Coordinator(eventSignal: eventSignal!)
webView?.scrollView.bounces = false
webView?.backgroundColor = UIColor(STColor.systemBackground)
Expand Down
2 changes: 2 additions & 0 deletions SNUTT-2022/SNUTT/AppState/States/SystemState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ class SystemState: ObservableObject {
@Published var isErrorAlertPresented = false
@Published var error: STError? = nil
@Published var preferredColorScheme: ColorScheme? = nil

@Published var selectedTab: TabType = .timetable
}
5 changes: 3 additions & 2 deletions SNUTT-2022/SNUTT/Models/ErrorCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,10 @@ enum ErrorCode: Int {
.NO_EMAIL,
.NO_PASSWORD_RESET_REQUEST,
.CANT_CHANGE_OTHERS_THEME,
.EMAIL_NOT_VERIFIED,
.EXPIRED_PASSWORD_RESET_CODE:
return "요청 실패"
case .EMAIL_NOT_VERIFIED:
return "인증 필요"
case .NO_USER_TOKEN,
.WRONG_API_KEY,
.WRONG_USER_TOKEN,
Expand Down Expand Up @@ -276,7 +277,7 @@ enum ErrorCode: Int {
case .WRONG_APPLE_TOKEN:
return "애플 계정으로 로그인하지 못했습니다."
case .EMAIL_NOT_VERIFIED:
return "인증되지 않은 이메일입니다. 강의평 탭에서 이메일 인증을 먼저 진행해주세요."
return "강의평 확인을 위해 이메일 인증이 필요합니다. 이메일 인증을 진행하시겠습니까?"
case .NO_PASSWORD_RESET_REQUEST:
return "비밀번호 재설정을 다시 시도해주세요."
case .EXPIRED_PASSWORD_RESET_CODE:
Expand Down
2 changes: 2 additions & 0 deletions SNUTT-2022/SNUTT/Services/AuthService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ extension UserAuthHandler {
appState.user.accessToken = nil
appState.user.userId = nil
appState.user.current = nil
appState.timetable.current = nil
}
localRepositories.userDefaultsRepository.set(TimetableDto.self, key: .currentTimetable, value: nil)
localRepositories.userDefaultsRepository.set(String.self, key: .accessToken, value: nil)
localRepositories.userDefaultsRepository.set(String.self, key: .userId, value: nil)
localRepositories.userDefaultsRepository.set(UserDto.self, key: .userDto, value: nil)
Expand Down
10 changes: 10 additions & 0 deletions SNUTT-2022/SNUTT/Services/GlobalUIService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ protocol GlobalUIServiceProtocol {
func setColorScheme(_ colorScheme: ColorScheme?)
func loadColorSchemeDuringBootstrap()

func setSelectedTab(_ tab: TabType)
func setIsErrorAlertPresented(_ value: Bool)
func setIsMenuOpen(_ value: Bool)

func openEllipsis(for timetable: TimetableMetadata)
Expand Down Expand Up @@ -58,6 +60,14 @@ struct GlobalUIService: GlobalUIServiceProtocol, UserAuthHandler {
appState.system.preferredColorScheme = colorScheme
}

func setSelectedTab(_ tab: TabType) {
appState.system.selectedTab = tab
}

func setIsErrorAlertPresented(_ value: Bool) {
appState.system.isErrorAlertPresented = value
}

func setIsMenuOpen(_ value: Bool) {
DispatchQueue.main.async {
appState.menu.isOpen = value
Expand Down
2 changes: 2 additions & 0 deletions SNUTT-2022/SNUTT/ViewModels/LectureDetailViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ extension LectureDetailScene {
func fetchReviewId(of lecture: Lecture) async -> String? {
do {
return try await lectureService.fetchReviewId(courseNumber: lecture.courseNumber, instructor: lecture.instructor)
} catch let error as STError where error.code == .EMAIL_NOT_VERIFIED {
// noop
} catch {
services.globalUIService.presentErrorAlert(error: error)
}
Expand Down
14 changes: 14 additions & 0 deletions SNUTT-2022/SNUTT/ViewModels/SearchSceneViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ class SearchSceneViewModel: BaseViewModel, ObservableObject {
@Published private var _selectedLecture: Lecture?
@Published private var _searchText: String = ""
@Published private var _isFilterOpen: Bool = false
@Published private var _selectedTab: TabType = .review
@Published var searchResult: [Lecture]? = nil
@Published var selectedTagList: [SearchTag] = []
@Published var isLoading: Bool = false
@Published var isLectureOverlapped: Bool = false

@Published var emailVerifyError: STError? = nil
@Published var presentEmailVerifyAlert = false

var errorTitle: String = ""
var errorMessage: String = ""

Expand All @@ -36,6 +41,11 @@ class SearchSceneViewModel: BaseViewModel, ObservableObject {
set { services.searchService.setSelectedLecture(newValue) }
}

var selectedTab: TabType {
get { _selectedTab }
set { services.globalUIService.setSelectedTab(newValue) }
}

var currentTimetableWithSelection: Timetable? {
_currentTimetable?.withSelectedLecture(_selectedLecture)
}
Expand All @@ -50,6 +60,7 @@ class SearchSceneViewModel: BaseViewModel, ObservableObject {
appState.timetable.$current.assign(to: &$_currentTimetable)
appState.timetable.$configuration.assign(to: &$_timetableConfig)
appState.search.$selectedLecture.assign(to: &$_selectedLecture)
appState.system.$selectedTab.assign(to: &$_selectedTab)
appState.search.$searchText.assign(to: &$_searchText)
appState.search.$isFilterOpen.assign(to: &$_isFilterOpen)
appState.search.$searchResult.assign(to: &$searchResult)
Expand Down Expand Up @@ -128,6 +139,9 @@ class SearchSceneViewModel: BaseViewModel, ObservableObject {
func fetchReviewId(of lecture: Lecture) async -> String? {
do {
return try await services.lectureService.fetchReviewId(courseNumber: lecture.courseNumber, instructor: lecture.instructor)
} catch let error as STError where error.code == .EMAIL_NOT_VERIFIED {
emailVerifyError = error
presentEmailVerifyAlert = true
} catch {
services.globalUIService.presentErrorAlert(error: error)
}
Expand Down
4 changes: 0 additions & 4 deletions SNUTT-2022/SNUTT/ViewModels/TimetableViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,4 @@ class TimetableViewModel: BaseViewModel, ObservableObject {
private var timetableState: TimetableState {
appState.timetable
}

func preloadWebViews() {
services.globalUIService.preloadWebViews()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ struct SearchLectureCell: View {
/// This `sheet` modifier should be called on `HStack` to prevent animation glitch when `dismiss`ed.
.sheet(isPresented: $showReviewWebView) {
if let container = container {
ReviewScene(viewModel: .init(container: container), detailId: $reviewId)
ReviewScene(viewModel: .init(container: container), isMainWebView: false, detailId: $reviewId)
.id(reviewId)
}
}
Expand Down
38 changes: 29 additions & 9 deletions SNUTT-2022/SNUTT/Views/SNUTTView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,19 @@ import SwiftUI

struct SNUTTView: View {
@ObservedObject var viewModel: ViewModel
@State private var selectedTab: TabType = .timetable

/// Required to synchronize between two navigation bar heights: `TimetableScene` and `SearchLectureScene`.
@State private var navigationBarHeight: CGFloat = 0

private var selected: Binding<TabType> {
Binding<TabType> {
selectedTab
viewModel.selectedTab
} set: {
[previous = selectedTab] current in
[previous = viewModel.selectedTab] current in
if previous == current, current == .review {
viewModel.reloadReviewWebView()
}
selectedTab = current
viewModel.selectedTab = current
}
}

Expand All @@ -48,17 +47,22 @@ struct SNUTTView: View {
SearchLectureScene(viewModel: .init(container: viewModel.container), navigationBarHeight: navigationBarHeight)
}
TabScene(tabType: .review) {
ReviewScene(viewModel: .init(container: viewModel.container))
ReviewScene(viewModel: .init(container: viewModel.container), isMainWebView: true)
}
TabScene(tabType: .settings) {
SettingScene(viewModel: .init(container: viewModel.container))
}
}
if selectedTab == .timetable {
.onAppear {
viewModel.selectedTab = .timetable
viewModel.preloadWebViews()
}

if viewModel.selectedTab == .timetable {
MenuSheetScene(viewModel: .init(container: viewModel.container))
LectureTimeSheetScene(viewModel: .init(container: viewModel.container))
}
if selectedTab == .search {
if viewModel.selectedTab == .search {
FilterSheetScene(viewModel: .init(container: viewModel.container))
}
PopupScene(viewModel: .init(container: viewModel.container))
Expand Down Expand Up @@ -97,11 +101,22 @@ struct SNUTTView: View {

extension SNUTTView {
class ViewModel: BaseViewModel, ObservableObject {
@Published var isErrorAlertPresented = false
@Published var accessToken: String? = nil
@Published var preferredColorScheme: ColorScheme? = nil
@Published private var error: STError? = nil

@Published private var _isErrorAlertPresented = false
var isErrorAlertPresented: Bool {
get { _isErrorAlertPresented }
set { services.globalUIService.setIsErrorAlertPresented(newValue) }
}

@Published private var _selectedTab: TabType = .review
var selectedTab: TabType {
get { _selectedTab }
set { services.globalUIService.setSelectedTab(newValue) }
}

var isAuthenticated: Bool {
guard let accessToken = accessToken else { return false }
return !accessToken.isEmpty
Expand All @@ -110,9 +125,10 @@ extension SNUTTView {
override init(container: DIContainer) {
super.init(container: container)
appState.system.$error.assign(to: &$error)
appState.system.$isErrorAlertPresented.assign(to: &$isErrorAlertPresented)
appState.system.$isErrorAlertPresented.assign(to: &$_isErrorAlertPresented)
appState.user.$accessToken.assign(to: &$accessToken)
appState.system.$preferredColorScheme.assign(to: &$preferredColorScheme)
appState.system.$selectedTab.assign(to: &$_selectedTab)
}

var errorTitle: String {
Expand All @@ -126,6 +142,10 @@ extension SNUTTView {
func reloadReviewWebView() {
services.globalUIService.sendMainWebViewReloadSignal()
}

func preloadWebViews() {
services.globalUIService.preloadWebViews()
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion SNUTT-2022/SNUTT/Views/Scenes/LectureDetailScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ struct LectureDetailScene: View {
viewModel.reloadDetailWebView(detailId: reviewId)
}
.sheet(isPresented: $showReviewWebView) {
ReviewScene(viewModel: .init(container: viewModel.container), detailId: $reviewId)
ReviewScene(viewModel: .init(container: viewModel.container), isMainWebView: false, detailId: $reviewId)
.id(colorScheme)
.id(reviewId)
}
Expand Down
12 changes: 7 additions & 5 deletions SNUTT-2022/SNUTT/Views/Scenes/ReviewScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ import SwiftUI
struct ReviewScene: View {
@ObservedObject var viewModel: ViewModel
@Binding var detailId: String?
private var isMainWebView: Bool

@Environment(\.colorScheme) var colorScheme
@Environment(\.dismiss) var dismiss

init(viewModel: ViewModel, detailId: Binding<String?> = .constant(nil)) {
init(viewModel: ViewModel, isMainWebView: Bool, detailId: Binding<String?> = .constant(nil)) {
self.viewModel = viewModel
_detailId = detailId
self.isMainWebView = isMainWebView
}

private var eventSignal: PassthroughSubject<WebViewEventType, Never>? {
viewModel.getPreloadedWebView(detailId: detailId).eventSignal
viewModel.getPreloadedWebView(isMain: isMainWebView).eventSignal
}

private var reviewUrl: URL {
Expand All @@ -34,7 +36,7 @@ struct ReviewScene: View {

var body: some View {
ZStack {
ReviewWebView(preloadedWebView: viewModel.getPreloadedWebView(detailId: detailId))
ReviewWebView(preloadedWebView: viewModel.getPreloadedWebView(isMain: isMainWebView))
.navigationBarHidden(true)
.edgesIgnoringSafeArea(.bottom)
.background(STColor.systemBackground)
Expand Down Expand Up @@ -92,8 +94,8 @@ extension ReviewScene {
}.store(in: &bag)
}

func getPreloadedWebView(detailId: String?) -> WebViewPreloadManager {
if detailId == nil {
func getPreloadedWebView(isMain: Bool) -> WebViewPreloadManager {
if isMain {
return appState.review.preloadedMain
} else {
return appState.review.preloadedDetail
Expand Down
11 changes: 11 additions & 0 deletions SNUTT-2022/SNUTT/Views/Scenes/SearchLectureScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ struct SearchLectureScene: View {
.task {
await viewModel.fetchTags()
}
.alert(viewModel.emailVerifyError?.title ?? "", isPresented: $viewModel.presentEmailVerifyAlert, actions: {
Button("확인") {
viewModel.emailVerifyError = nil
viewModel.selectedTab = .review
}
Button("취소", role: .cancel) {
viewModel.emailVerifyError = nil
}
}, message: {
Text(viewModel.emailVerifyError?.content ?? "")
})
.navigationBarHidden(true)
.animation(.customSpring, value: viewModel.searchResult?.count)
.animation(.customSpring, value: viewModel.isLoading)
Expand Down
2 changes: 0 additions & 2 deletions SNUTT-2022/SNUTT/Views/Scenes/TimetableScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ struct TimetableScene: View {
ActivityViewController(activityItems: [screenshot, linkMetadata])
}
.onLoad {
viewModel.preloadWebViews()

await withTaskGroup(of: Void.self, body: { group in
group.addTask {
await viewModel.fetchTimetableList()
Expand Down

0 comments on commit 3bd0096

Please sign in to comment.