메인 - 회원정보 가져오기, 푸시토큰 업데이트, 이벤트 팝업 추가
This commit is contained in:
		| @@ -34,6 +34,8 @@ class AppState: ObservableObject { | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     @Published var eventPopup: EventItem? = nil | ||||
|      | ||||
|     func setAppStep(step: AppStep) { | ||||
|         switch step { | ||||
|         case .splash, .main: | ||||
|   | ||||
							
								
								
									
										69
									
								
								SodaLive/Sources/Main/EventPopupDialogView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								SodaLive/Sources/Main/EventPopupDialogView.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| // | ||||
| //  EventPopupDialogView.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2023/08/11. | ||||
| // | ||||
|  | ||||
| import SwiftUI | ||||
| import Kingfisher | ||||
|  | ||||
| struct EventPopupDialogView: View { | ||||
|      | ||||
|     let eventPopup: EventItem | ||||
|      | ||||
|     var body: some View { | ||||
|         VStack(spacing: 13.3) { | ||||
|             KFImage(URL(string: eventPopup.popupImageUrl!)) | ||||
|                 .resizable() | ||||
|                 .frame(width: screenSize().width, height: screenSize().width) | ||||
|                 .scaledToFill() | ||||
|                 .cornerRadius(16.7, corners: [.topLeft, .topRight]) | ||||
|                 .onTapGesture { | ||||
|                     AppState | ||||
|                         .shared | ||||
|                         .setAppStep( | ||||
|                             step: .eventDetail( | ||||
|                                 event: eventPopup | ||||
|                             ) | ||||
|                         ) | ||||
|                     AppState.shared.eventPopup = nil | ||||
|                 } | ||||
|              | ||||
|             HStack(spacing: 0) { | ||||
|                 Text("다시보지 않기") | ||||
|                     .font(.custom(Font.medium.rawValue, size: 14.7)) | ||||
|                     .foregroundColor(Color(hex: "eeeeee")) | ||||
|                     .onTapGesture { | ||||
|                         UserDefaults.set(eventPopup.id, forKey: .notShowingEventPopupId) | ||||
|                         AppState.shared.eventPopup = nil | ||||
|                     } | ||||
|                  | ||||
|                 Spacer() | ||||
|                  | ||||
|                 Text("닫기") | ||||
|                     .font(.custom(Font.medium.rawValue, size: 14.7)) | ||||
|                     .foregroundColor(Color(hex: "eeeeee")) | ||||
|                     .onTapGesture { AppState.shared.eventPopup = nil } | ||||
|             } | ||||
|             .padding(.horizontal, 26.7) | ||||
|             .padding(.bottom, 13.3) | ||||
|         } | ||||
|         .background(Color(hex: "222222")) | ||||
|         .cornerRadius(16.7, corners: [.topLeft, .topRight]) | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct EventPopupDialogView_Previews: PreviewProvider { | ||||
|     static var previews: some View { | ||||
|         EventPopupDialogView( | ||||
|             eventPopup: EventItem( | ||||
|                 id: 1, | ||||
|                 thumbnailImageUrl: "", | ||||
|                 detailImageUrl: "", | ||||
|                 popupImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", | ||||
|                 link: "" | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -58,12 +58,49 @@ struct HomeView: View { | ||||
|                     } | ||||
|                 } | ||||
|                 .onAppear { | ||||
|                      | ||||
|                     pushTokenUpdate() | ||||
|                     viewModel.getMemberInfo() | ||||
|                     viewModel.getEventPopup() | ||||
|                 } | ||||
|                  | ||||
|                 if appState.isShowNotificationSettingsDialog { | ||||
|                     NotificationSettingsDialog() | ||||
|                 } | ||||
|                  | ||||
|                 if let eventItem = appState.eventPopup { | ||||
|                     VStack(spacing: 0) { | ||||
|                         Spacer() | ||||
|                          | ||||
|                         EventPopupDialogView(eventPopup: eventItem) | ||||
|                          | ||||
|                         if proxy.safeAreaInsets.bottom > 0 { | ||||
|                             Rectangle() | ||||
|                                 .foregroundColor(Color(hex: "222222")) | ||||
|                                 .frame(width: proxy.size.width, height: 15.3) | ||||
|                         } | ||||
|                     } | ||||
|                     .background(Color(hex: "222222").opacity(0.7)) | ||||
|                     .onTapGesture { | ||||
|                         AppState.shared.eventPopup = nil | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .edgesIgnoringSafeArea(.bottom) | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     private func pushTokenUpdate() { | ||||
|         Messaging.messaging().token { token, error in | ||||
|             if let error = error { | ||||
|                 DEBUG_LOG(error.localizedDescription) | ||||
|             } else { | ||||
|                 if let token = token { | ||||
|                     UserDefaults.set(token, forKey: .pushToken) | ||||
|                     self.viewModel.pushTokenUpdate(pushToken: token) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct HomeView_Previews: PreviewProvider { | ||||
|   | ||||
| @@ -13,10 +13,83 @@ final class HomeViewModel: ObservableObject { | ||||
|     private var subscription = Set<AnyCancellable>() | ||||
|      | ||||
|     private let userRepository = UserRepository() | ||||
|     private let eventRepository = EventRepository() | ||||
|      | ||||
|     enum CurrentTab: String { | ||||
|         case content, live, explorer, message, mypage | ||||
|     } | ||||
|      | ||||
|     @Published var currentTab: CurrentTab = .live | ||||
|      | ||||
|     func pushTokenUpdate(pushToken: String) { | ||||
|         userRepository.updatePushToken(pushToken: pushToken) | ||||
|             .sink { result in | ||||
|                 switch result { | ||||
|                 case .finished: | ||||
|                     DEBUG_LOG("finish") | ||||
|                 case .failure(let error): | ||||
|                     ERROR_LOG(error.localizedDescription) | ||||
|                 } | ||||
|             } receiveValue: { _ in | ||||
|             } | ||||
|             .store(in: &subscription) | ||||
|     } | ||||
|      | ||||
|     func getMemberInfo() { | ||||
|         userRepository.getMemberInfo() | ||||
|             .sink { result in | ||||
|                 switch result { | ||||
|                 case .finished: | ||||
|                     DEBUG_LOG("finish") | ||||
|                 case .failure(let error): | ||||
|                     ERROR_LOG(error.localizedDescription) | ||||
|                 } | ||||
|             } receiveValue: { response in | ||||
|                 let responseData = response.data | ||||
|                  | ||||
|                 do { | ||||
|                     let jsonDecoder = JSONDecoder() | ||||
|                     let decoded = try jsonDecoder.decode(ApiResponse<GetMemberInfoResponse>.self, from: responseData) | ||||
|                      | ||||
|                     if let data = decoded.data, decoded.success { | ||||
|                         UserDefaults.set(data.can, forKey: .can) | ||||
|                         UserDefaults.set(data.isAuth, forKey: .auth) | ||||
|                         UserDefaults.set(data.role.rawValue, forKey: .role) | ||||
|                         if data.followingChannelLiveNotice == nil && data.followingChannelUploadContentNotice == nil && data.messageNotice == nil { | ||||
|                             AppState.shared.isShowNotificationSettingsDialog = true | ||||
|                         } | ||||
|                     } | ||||
|                 } catch { | ||||
|                     print(error) | ||||
|                 } | ||||
|             } | ||||
|             .store(in: &subscription) | ||||
|     } | ||||
|      | ||||
|     func getEventPopup() { | ||||
|         eventRepository.getEventPopup() | ||||
|             .sink { result in | ||||
|                 switch result { | ||||
|                 case .finished: | ||||
|                     DEBUG_LOG("finish") | ||||
|                 case .failure(let error): | ||||
|                     ERROR_LOG(error.localizedDescription) | ||||
|                 } | ||||
|             } receiveValue: { response in | ||||
|                 let responseData = response.data | ||||
|                  | ||||
|                 do { | ||||
|                     let jsonDecoder = JSONDecoder() | ||||
|                     let decoded = try jsonDecoder.decode(ApiResponse<EventItem>.self, from: responseData) | ||||
|                      | ||||
|                     if let data = decoded.data, decoded.success { | ||||
|                         if data.id != UserDefaults.int(forKey: .notShowingEventPopupId) { | ||||
|                             AppState.shared.eventPopup = data | ||||
|                         } | ||||
|                     } | ||||
|                 } catch { | ||||
|                 } | ||||
|             } | ||||
|             .store(in: &subscription) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -173,6 +173,25 @@ struct CanPaymentView: View { | ||||
|                             .frame(width: proxy.size.width, height: 15.3) | ||||
|                     } | ||||
|                 } | ||||
|                 .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { | ||||
|                     GeometryReader { geo in | ||||
|                         HStack { | ||||
|                             Spacer() | ||||
|                             Text(viewModel.errorMessage) | ||||
|                                 .padding(.vertical, 13.3) | ||||
|                                 .padding(.horizontal, 6.7) | ||||
|                                 .frame(width: geo.size.width - 66.7, alignment: .center) | ||||
|                                 .font(.custom(Font.medium.rawValue, size: 12)) | ||||
|                                 .background(Color(hex: "9970ff")) | ||||
|                                 .foregroundColor(Color.white) | ||||
|                                 .multilineTextAlignment(.leading) | ||||
|                                 .fixedSize(horizontal: false, vertical: true) | ||||
|                                 .cornerRadius(20) | ||||
|                                 .padding(.top, 66.7) | ||||
|                             Spacer() | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 .edgesIgnoringSafeArea(.bottom) | ||||
|             } | ||||
|              | ||||
|   | ||||
| @@ -269,6 +269,25 @@ struct CanPgPaymentView: View { | ||||
|                                 .frame(width: proxy.size.width, height: 15.3) | ||||
|                         } | ||||
|                     } | ||||
|                     .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { | ||||
|                         GeometryReader { geo in | ||||
|                             HStack { | ||||
|                                 Spacer() | ||||
|                                 Text(viewModel.errorMessage) | ||||
|                                     .padding(.vertical, 13.3) | ||||
|                                     .padding(.horizontal, 6.7) | ||||
|                                     .frame(width: geo.size.width - 66.7, alignment: .center) | ||||
|                                     .font(.custom(Font.medium.rawValue, size: 12)) | ||||
|                                     .background(Color(hex: "9970ff")) | ||||
|                                     .foregroundColor(Color.white) | ||||
|                                     .multilineTextAlignment(.leading) | ||||
|                                     .fixedSize(horizontal: false, vertical: true) | ||||
|                                     .cornerRadius(20) | ||||
|                                     .padding(.top, 66.7) | ||||
|                                 Spacer() | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     .edgesIgnoringSafeArea(.bottom) | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -0,0 +1,102 @@ | ||||
| // | ||||
| //  NotificationSettingsDialog.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2023/08/11. | ||||
| // | ||||
|  | ||||
| import SwiftUI | ||||
|  | ||||
| struct NotificationSettingsDialog: View { | ||||
|      | ||||
|     @StateObject var viewModel = NotificationSettingsDialogViewModel() | ||||
|      | ||||
|     var body: some View { | ||||
|         GeometryReader { geo in | ||||
|             ZStack { | ||||
|                 Color.black | ||||
|                     .opacity(0.5) | ||||
|                     .frame(width: geo.size.width, height: geo.size.height) | ||||
|                  | ||||
|                 VStack(spacing: 0) { | ||||
|                     HStack(spacing: 0) { | ||||
|                         Text("라이브 알림") | ||||
|                             .font(.custom(Font.bold.rawValue, size: 15)) | ||||
|                             .foregroundColor(Color(hex: "eeeeee")) | ||||
|                          | ||||
|                         Spacer() | ||||
|                          | ||||
|                         Image(viewModel.live ? "btn_toggle_on_big" : "btn_toggle_off_big") | ||||
|                             .resizable() | ||||
|                             .frame(width: 44, height: 27) | ||||
|                             .onTapGesture { | ||||
|                                 viewModel.live.toggle() | ||||
|                             } | ||||
|                     } | ||||
|                      | ||||
|                     Rectangle() | ||||
|                         .frame(height: 1) | ||||
|                         .foregroundColor(Color(hex: "909090")) | ||||
|                         .padding(.vertical, 13) | ||||
|                      | ||||
|                     HStack(spacing: 0) { | ||||
|                         Text("콘텐츠 업로드 알림") | ||||
|                             .font(.custom(Font.bold.rawValue, size: 15)) | ||||
|                             .foregroundColor(Color(hex: "eeeeee")) | ||||
|                          | ||||
|                         Spacer() | ||||
|                          | ||||
|                         Image(viewModel.uploadContent ? "btn_toggle_on_big" : "btn_toggle_off_big") | ||||
|                             .resizable() | ||||
|                             .frame(width: 44, height: 27) | ||||
|                             .onTapGesture { | ||||
|                                 viewModel.uploadContent.toggle() | ||||
|                             } | ||||
|                     } | ||||
|                      | ||||
|                     Rectangle() | ||||
|                         .frame(height: 1) | ||||
|                         .foregroundColor(Color(hex: "909090")) | ||||
|                         .padding(.vertical, 13) | ||||
|                      | ||||
|                     HStack(spacing: 0) { | ||||
|                         Text("메시지 알림") | ||||
|                             .font(.custom(Font.bold.rawValue, size: 15)) | ||||
|                             .foregroundColor(Color(hex: "eeeeee")) | ||||
|                          | ||||
|                         Spacer() | ||||
|                          | ||||
|                         Image(viewModel.message ? "btn_toggle_on_big" : "btn_toggle_off_big") | ||||
|                             .resizable() | ||||
|                             .frame(width: 44, height: 27) | ||||
|                             .onTapGesture { | ||||
|                                 viewModel.message.toggle() | ||||
|                             } | ||||
|                     } | ||||
|                      | ||||
|                     Text("확인") | ||||
|                         .font(.custom(Font.bold.rawValue, size: 15.3)) | ||||
|                         .foregroundColor(Color(hex: "ffffff")) | ||||
|                         .padding(.vertical, 16) | ||||
|                         .frame(width: (geo.size.width - 66.7) * 2 / 3) | ||||
|                         .background(Color(hex: "9970ff")) | ||||
|                         .cornerRadius(8) | ||||
|                         .padding(.top, 33.3) | ||||
|                         .onTapGesture { | ||||
|                             viewModel.submit() | ||||
|                         } | ||||
|                 } | ||||
|                 .padding(26.7) | ||||
|                 .frame(width: geo.size.width - 26.7, alignment: .center) | ||||
|                 .background(Color(hex: "222222")) | ||||
|                 .cornerRadius(10) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct NotificationSettingsDialog_Previews: PreviewProvider { | ||||
|     static var previews: some View { | ||||
|         NotificationSettingsDialog() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| // | ||||
| //  NotificationSettingsDialogViewModel.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2023/08/11. | ||||
| // | ||||
|  | ||||
| import Foundation | ||||
| import Combine | ||||
|  | ||||
| final class NotificationSettingsDialogViewModel: ObservableObject { | ||||
|      | ||||
|     private let userRepository = UserRepository() | ||||
|     private var subscription = Set<AnyCancellable>() | ||||
|      | ||||
|     @Published var live = true | ||||
|     @Published var uploadContent = true | ||||
|     @Published var message = true | ||||
|      | ||||
|     func submit() { | ||||
|         userRepository | ||||
|             .updateNotificationSettings(live: live, uploadContent: uploadContent, message: message) | ||||
|             .sink { result in | ||||
|                 switch result { | ||||
|                 case .finished: | ||||
|                     DEBUG_LOG("finish") | ||||
|                 case .failure(let error): | ||||
|                     ERROR_LOG(error.localizedDescription) | ||||
|                 } | ||||
|             } receiveValue: { _ in | ||||
|                 AppState.shared.isShowNotificationSettingsDialog = false | ||||
|             } | ||||
|             .store(in: &subscription) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13
									
								
								SodaLive/Sources/User/PushTokenUpdateRequest.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								SodaLive/Sources/User/PushTokenUpdateRequest.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| // | ||||
| //  PushTokenUpdateRequest.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2023/08/11. | ||||
| // | ||||
|  | ||||
| import Foundation | ||||
|  | ||||
| struct PushTokenUpdateRequest: Encodable { | ||||
|     let pushToken: String | ||||
|     let container: String = "ios" | ||||
| } | ||||
| @@ -19,6 +19,7 @@ enum UserApi { | ||||
|     case logout | ||||
|     case logoutAllDevice | ||||
|     case signOut(request: SignOutRequest) | ||||
|     case updatePushToken(request: PushTokenUpdateRequest) | ||||
| } | ||||
|  | ||||
| extension UserApi: TargetType { | ||||
| @@ -57,6 +58,9 @@ extension UserApi: TargetType { | ||||
|              | ||||
|         case .signOut: | ||||
|             return "/member/sign_out" | ||||
|              | ||||
|         case .updatePushToken: | ||||
|             return "/member/push-token/update" | ||||
|         } | ||||
|     } | ||||
|      | ||||
| @@ -67,6 +71,9 @@ extension UserApi: TargetType { | ||||
|              | ||||
|         case .searchUser, .getMypage, .getMemberInfo: | ||||
|             return .get | ||||
|              | ||||
|         case .updatePushToken: | ||||
|             return .put | ||||
|         } | ||||
|     } | ||||
|      | ||||
| @@ -95,6 +102,9 @@ extension UserApi: TargetType { | ||||
|              | ||||
|         case .signOut(let request): | ||||
|             return .requestJSONEncodable(request) | ||||
|              | ||||
|         case .updatePushToken(let request): | ||||
|             return .requestJSONEncodable(request) | ||||
|         } | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -60,4 +60,8 @@ final class UserRepository { | ||||
|     func signOut(reason: String, password: String) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.signOut(request: SignOutRequest(reason: reason, password: password))) | ||||
|     } | ||||
|      | ||||
|     func updatePushToken(pushToken: String) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.updatePushToken(request: PushTokenUpdateRequest(pushToken: pushToken))) | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung