크리에이터 채널 - 팔로우와 알림설정
- 팔로잉 상태에서 알림 켜기/끄기 상태 추가
| Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.6 KiB | 
							
								
								
									
										21
									
								
								SodaLive/Resources/Assets.xcassets/btn_following_no_alarm_big.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | |||||||
|  | { | ||||||
|  |   "images" : [ | ||||||
|  |     { | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "1x" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "2x" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename" : "btn_following_no_alarm_big.png", | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "3x" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "info" : { | ||||||
|  |     "author" : "xcode", | ||||||
|  |     "version" : 1 | ||||||
|  |   } | ||||||
|  | } | ||||||
| After Width: | Height: | Size: 5.1 KiB | 
							
								
								
									
										21
									
								
								SodaLive/Resources/Assets.xcassets/ic_avatar_unfollow.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | |||||||
|  | { | ||||||
|  |   "images" : [ | ||||||
|  |     { | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "1x" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename" : "ic_avatar_unfollow.png", | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "2x" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "3x" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "info" : { | ||||||
|  |     "author" : "xcode", | ||||||
|  |     "version" : 1 | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								SodaLive/Resources/Assets.xcassets/ic_avatar_unfollow.imageset/ic_avatar_unfollow.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 502 B | 
							
								
								
									
										21
									
								
								SodaLive/Resources/Assets.xcassets/ic_notify_all.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | |||||||
|  | { | ||||||
|  |   "images" : [ | ||||||
|  |     { | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "1x" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename" : "ic_notify_all.png", | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "2x" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "3x" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "info" : { | ||||||
|  |     "author" : "xcode", | ||||||
|  |     "version" : 1 | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								SodaLive/Resources/Assets.xcassets/ic_notify_all.imageset/ic_notify_all.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 793 B | 
							
								
								
									
										21
									
								
								SodaLive/Resources/Assets.xcassets/ic_notify_none.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | |||||||
|  | { | ||||||
|  |   "images" : [ | ||||||
|  |     { | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "1x" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename" : "ic_notify_none.png", | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "2x" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "idiom" : "universal", | ||||||
|  |       "scale" : "3x" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "info" : { | ||||||
|  |     "author" : "xcode", | ||||||
|  |     "version" : 1 | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								SodaLive/Resources/Assets.xcassets/ic_notify_none.imageset/ic_notify_none.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										107
									
								
								SodaLive/Sources/Dialog/CreatorFollowNotifyDialog.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,107 @@ | |||||||
|  | // | ||||||
|  | //  CreatorFollowNotifyDialog.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 9/23/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | import SwiftUI | ||||||
|  |  | ||||||
|  | struct CreatorFollowNotifyDialog: View { | ||||||
|  |      | ||||||
|  |     @Binding var isShowing: Bool | ||||||
|  |     let onClickNotifyAll: () -> Void | ||||||
|  |     let onClickNotifyNone: () -> Void | ||||||
|  |     let onClickUnFollow: () -> Void | ||||||
|  |      | ||||||
|  |     @State private var isShow: Bool = false | ||||||
|  |      | ||||||
|  |     var body: some View { | ||||||
|  |         ZStack { | ||||||
|  |             Color.black.opacity(0.7).ignoresSafeArea() | ||||||
|  |                 .onTapGesture { | ||||||
|  |                     isShowing = false | ||||||
|  |                 } | ||||||
|  |              | ||||||
|  |             VStack(spacing: 0) { | ||||||
|  |                 Spacer() | ||||||
|  |                  | ||||||
|  |                 if isShow { | ||||||
|  |                     VStack(alignment: .leading, spacing: 24) { | ||||||
|  |                         Text("알림") | ||||||
|  |                             .font(.custom(Font.bold.rawValue, size: 18.3)) | ||||||
|  |                             .foregroundColor(Color.grayee) | ||||||
|  |                          | ||||||
|  |                         CreatorFollowNotifyItem( | ||||||
|  |                             image: "ic_notify_all", | ||||||
|  |                             title: "전체", | ||||||
|  |                             onTapGesture: { | ||||||
|  |                                 isShowing = false | ||||||
|  |                                 onClickNotifyAll() | ||||||
|  |                             } | ||||||
|  |                         ) | ||||||
|  |                         CreatorFollowNotifyItem( | ||||||
|  |                             image: "ic_notify_none", | ||||||
|  |                             title: "없음", | ||||||
|  |                             onTapGesture: { | ||||||
|  |                                 isShowing = false | ||||||
|  |                                 onClickNotifyNone() | ||||||
|  |                             } | ||||||
|  |                         ) | ||||||
|  |                         CreatorFollowNotifyItem( | ||||||
|  |                             image: "ic_avatar_unfollow", | ||||||
|  |                             title: "팔로우 취소", | ||||||
|  |                             onTapGesture: { | ||||||
|  |                                 isShowing = false | ||||||
|  |                                 onClickUnFollow() | ||||||
|  |                             } | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                     .frame(maxWidth:.infinity) | ||||||
|  |                     .padding(.horizontal, 16) | ||||||
|  |                     .padding(.top, 16) | ||||||
|  |                     .padding(.bottom, 32) | ||||||
|  |                     .background(Color.gray22) | ||||||
|  |                     .cornerRadius(10, corners: [.topLeft, .topRight]) | ||||||
|  |                     .transition(.move(edge: .bottom)) | ||||||
|  |                     .animation(.easeInOut(duration: 0.5), value: isShow) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             .ignoresSafeArea() | ||||||
|  |         } | ||||||
|  |         .onAppear { | ||||||
|  |             withAnimation { | ||||||
|  |                 isShow = true | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct CreatorFollowNotifyItem: View { | ||||||
|  |      | ||||||
|  |     let image: String | ||||||
|  |     let title: String | ||||||
|  |     let onTapGesture: () -> Void | ||||||
|  |      | ||||||
|  |     var body: some View { | ||||||
|  |         HStack(spacing: 16) { | ||||||
|  |             Image(image) | ||||||
|  |              | ||||||
|  |             Text(title) | ||||||
|  |                 .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|  |                 .foregroundColor(Color.grayee) | ||||||
|  |              | ||||||
|  |             Spacer() | ||||||
|  |         } | ||||||
|  |         .onTapGesture(perform: onTapGesture) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #Preview { | ||||||
|  |     CreatorFollowNotifyDialog( | ||||||
|  |         isShowing: .constant(true), | ||||||
|  |         onClickNotifyAll: {}, | ||||||
|  |         onClickNotifyNone: {}, | ||||||
|  |         onClickUnFollow: {} | ||||||
|  |     ) | ||||||
|  | } | ||||||
| @@ -31,6 +31,8 @@ struct CreatorResponse: Decodable { | |||||||
|     let youtubeUrl: String? |     let youtubeUrl: String? | ||||||
|     let websiteUrl: String? |     let websiteUrl: String? | ||||||
|     let blogUrl: String? |     let blogUrl: String? | ||||||
|  |     let isFollow: Bool | ||||||
|  |     let isNotify: Bool | ||||||
|     let isNotification: Bool |     let isNotification: Bool | ||||||
|     let notificationRecipientCount: Int |     let notificationRecipientCount: Int | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ struct UserProfileCreatorView: View { | |||||||
|     let creator: CreatorResponse |     let creator: CreatorResponse | ||||||
|      |      | ||||||
|     let creatorFollow: () -> Void |     let creatorFollow: () -> Void | ||||||
|     let creatorUnFollow: () -> Void |     let showCreatorFollowNotifyDialog: () -> Void | ||||||
|     let shareChannel: () -> Void |     let shareChannel: () -> Void | ||||||
|      |      | ||||||
|     var body: some View { |     var body: some View { | ||||||
| @@ -57,12 +57,16 @@ struct UserProfileCreatorView: View { | |||||||
|                             } |                             } | ||||||
|                     } else { |                     } else { | ||||||
|                         VStack(alignment: .leading, spacing: 9.3) { |                         VStack(alignment: .leading, spacing: 9.3) { | ||||||
|                             Image(creator.isNotification ? "btn_following_big" : "btn_follow_big") |                             Image( | ||||||
|  |                                 creator.isFollow ? | ||||||
|  |                                 creator.isNotify ? "btn_following_big": "btn_following_no_alarm_big" | ||||||
|  |                                 : "btn_follow_big" | ||||||
|  |                             ) | ||||||
|                                 .resizable() |                                 .resizable() | ||||||
|                                 .frame(width: 83.3, height: 26.7) |                                 .frame(width: 83.3, height: 26.7) | ||||||
|                                 .onTapGesture { |                                 .onTapGesture { | ||||||
|                                     if creator.isNotification { |                                     if creator.isFollow { | ||||||
|                                         creatorUnFollow() |                                         showCreatorFollowNotifyDialog() | ||||||
|                                     } else { |                                     } else { | ||||||
|                                         creatorFollow() |                                         creatorFollow() | ||||||
|                                     } |                                     } | ||||||
| @@ -143,11 +147,13 @@ struct UserProfileCreatorView_Previews: PreviewProvider { | |||||||
|                 youtubeUrl: Optional("https://www.youtube.com/watch?v=3x2tfZnfLRo"), |                 youtubeUrl: Optional("https://www.youtube.com/watch?v=3x2tfZnfLRo"), | ||||||
|                 websiteUrl: Optional("https://instagram.com/dear.zia"), |                 websiteUrl: Optional("https://instagram.com/dear.zia"), | ||||||
|                 blogUrl: Optional("dear.zia"), |                 blogUrl: Optional("dear.zia"), | ||||||
|  |                 isFollow: false, | ||||||
|  |                 isNotify: false, | ||||||
|                 isNotification: false, |                 isNotification: false, | ||||||
|                 notificationRecipientCount: 2 |                 notificationRecipientCount: 2 | ||||||
|             ) |             ) | ||||||
|         ) { |         ) { | ||||||
|         } creatorUnFollow: { |         } showCreatorFollowNotifyDialog: { | ||||||
|         } shareChannel: { |         } shareChannel: { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ struct UserProfileView: View { | |||||||
|      |      | ||||||
|     @State private var memberId: Int = 0 |     @State private var memberId: Int = 0 | ||||||
|     @State private var isShowMemberProfilePopup: Bool = false |     @State private var isShowMemberProfilePopup: Bool = false | ||||||
|  |     @State private var isShowFollowNotifyDialog: Bool = false | ||||||
|      |      | ||||||
|     var body: some View { |     var body: some View { | ||||||
|         GeometryReader { proxy in |         GeometryReader { proxy in | ||||||
| @@ -52,8 +53,8 @@ struct UserProfileView: View { | |||||||
|                                     UserProfileCreatorView( |                                     UserProfileCreatorView( | ||||||
|                                         creator: creatorProfile.creator) { |                                         creator: creatorProfile.creator) { | ||||||
|                                             viewModel.creatorFollow() |                                             viewModel.creatorFollow() | ||||||
|                                         } creatorUnFollow: { |                                         } showCreatorFollowNotifyDialog: { | ||||||
|                                             viewModel.creatorUnFollow() |                                             isShowFollowNotifyDialog = true | ||||||
|                                         } shareChannel: { |                                         } shareChannel: { | ||||||
|                                             viewModel.shareChannel(userId: userId) |                                             viewModel.shareChannel(userId: userId) | ||||||
|                                         } |                                         } | ||||||
| @@ -323,6 +324,21 @@ struct UserProfileView: View { | |||||||
|                         MemberProfileDialog(isShowing: $isShowMemberProfilePopup, memberId: memberId) |                         MemberProfileDialog(isShowing: $isShowMemberProfilePopup, memberId: memberId) | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |                  | ||||||
|  |                 if isShowFollowNotifyDialog { | ||||||
|  |                     CreatorFollowNotifyDialog( | ||||||
|  |                         isShowing: $isShowFollowNotifyDialog, | ||||||
|  |                         onClickNotifyAll: { | ||||||
|  |                             viewModel.creatorFollow(follow: true, notify: true) | ||||||
|  |                         }, | ||||||
|  |                         onClickNotifyNone: { | ||||||
|  |                             viewModel.creatorFollow(follow: true, notify: false) | ||||||
|  |                         }, | ||||||
|  |                         onClickUnFollow: { | ||||||
|  |                             viewModel.creatorFollow(follow: false, notify: false) | ||||||
|  |                         } | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             .sheet( |             .sheet( | ||||||
|                 isPresented: $viewModel.isShowShareView, |                 isPresented: $viewModel.isShowShareView, | ||||||
|   | |||||||
| @@ -282,11 +282,11 @@ final class UserProfileViewModel: ObservableObject { | |||||||
|             .store(in: &subscription) |             .store(in: &subscription) | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     func creatorFollow() { |     func creatorFollow(follow: Bool = true, notify: Bool = true) { | ||||||
|         if let creator = creatorProfile { |         if let creator = creatorProfile { | ||||||
|             isLoading = true |             isLoading = true | ||||||
|              |              | ||||||
|             userRepository.creatorFollow(creatorId: creator.creator.creatorId) |             userRepository.creatorFollow(creatorId: creator.creator.creatorId, follow: follow, notify: notify) | ||||||
|                 .sink { result in |                 .sink { result in | ||||||
|                     switch result { |                     switch result { | ||||||
|                     case .finished: |                     case .finished: | ||||||
|   | |||||||
| @@ -9,4 +9,6 @@ import Foundation | |||||||
|  |  | ||||||
| struct CreatorFollowRequest: Encodable { | struct CreatorFollowRequest: Encodable { | ||||||
|     let creatorId: Int |     let creatorId: Int | ||||||
|  |     var isNotify: Bool = true | ||||||
|  |     var isActive: Bool = true | ||||||
| } | } | ||||||
|   | |||||||
| @@ -69,8 +69,8 @@ final class UserRepository { | |||||||
|         return api.requestPublisher(.updatePushToken(request: PushTokenUpdateRequest(pushToken: pushToken))) |         return api.requestPublisher(.updatePushToken(request: PushTokenUpdateRequest(pushToken: pushToken))) | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     func creatorFollow(creatorId: Int) -> AnyPublisher<Response, MoyaError> { |     func creatorFollow(creatorId: Int, follow: Bool = true, notify: Bool = true) -> AnyPublisher<Response, MoyaError> { | ||||||
|         return api.requestPublisher(.creatorFollow(request: CreatorFollowRequest(creatorId: creatorId))) |         return api.requestPublisher(.creatorFollow(request: CreatorFollowRequest(creatorId: creatorId, isNotify: notify, isActive: follow))) | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     func creatorUnFollow(creatorId: Int) -> AnyPublisher<Response, MoyaError> { |     func creatorUnFollow(creatorId: Int) -> AnyPublisher<Response, MoyaError> { | ||||||
|   | |||||||
 Yu Sung
					Yu Sung