diff --git a/Projects/Core/PPACUtil/Sources/User/UserInfo.swift b/Projects/Core/PPACUtil/Sources/User/UserInfo.swift index c66e36c..3449ece 100644 --- a/Projects/Core/PPACUtil/Sources/User/UserInfo.swift +++ b/Projects/Core/PPACUtil/Sources/User/UserInfo.swift @@ -19,5 +19,5 @@ public class UserInfo { @UserDefault(key:"deviceId", defaultValue: "") public var deviceId: String - public let testDeviceId: String = "abcdefgh" + public let testDeviceId: String = "uni-test" } diff --git a/Projects/Features/Recommend/Sources/Presentation/RecommendMemeButtonView.swift b/Projects/Features/Recommend/Sources/Presentation/RecommendMemeButtonView.swift index d69188f..0902902 100644 --- a/Projects/Features/Recommend/Sources/Presentation/RecommendMemeButtonView.swift +++ b/Projects/Features/Recommend/Sources/Presentation/RecommendMemeButtonView.swift @@ -10,12 +10,14 @@ import SwiftUI import ResourceKit import DesignSystem +import PPACModels + import Lottie struct RecommendMemeButtonView : View { @State var playbackMode: LottiePlaybackMode = .paused(at: .progress(100)) - @Binding var reactionCount: Int? + @Binding var meme: MemeDetail? let reactionButtonTapped: () -> Void let copyButtonTapped: () -> Void @@ -25,8 +27,13 @@ struct RecommendMemeButtonView : View { public var body: some View { HStack { LikeButton( - reactionCount: $reactionCount, - didTapped: reactionButtonTapped + reactionCount: meme?.reaction, + didTapped: { + playbackMode = .playing( + .fromProgress(0, toProgress: 1, loopMode: .playOnce) + ) + self.reactionButtonTapped() + } ) .overlay(content: { LottieView(animation: AnimationAsset.kkEffect.animation) @@ -84,11 +91,11 @@ func saveButton(_ saveAction: @escaping () -> Void) -> some View { } #Preview { - @State var count: Int? = 1 + @State var meme: MemeDetail? = .mock return RecommendMemeButtonView( - reactionCount: $count, - reactionButtonTapped: { print("reaction~~") }, + meme: $meme, + reactionButtonTapped: { meme?.reaction += 1 }, copyButtonTapped: { print("copy~~") }, shareButtonTapped: { print("share~~") }, saveButtonTapped: { print("save!!") } diff --git a/Projects/Features/Recommend/Sources/Presentation/RecommendMemeImageView.swift b/Projects/Features/Recommend/Sources/Presentation/RecommendMemeImageView.swift index cf98a9b..77ec278 100644 --- a/Projects/Features/Recommend/Sources/Presentation/RecommendMemeImageView.swift +++ b/Projects/Features/Recommend/Sources/Presentation/RecommendMemeImageView.swift @@ -14,10 +14,7 @@ import ResourceKit import PPACModels struct RecommendMemeImagesView: View { - @State private var currentViewingMeme: MemeDetail? - - @Binding var currentViewingMemeId: String? - @Binding var currentViewingMemeReaction: Int? + @Binding var currentMeme: MemeDetail? var memes: [MemeDetail] var isTagHidden: Bool = false @@ -27,19 +24,16 @@ struct RecommendMemeImagesView: View { ScrollView(.horizontal) { LazyHStack { ForEach(memes, id: \.self) { meme in - MemeImageView(imageUrl: meme.imageUrlString) - .overlay { - if let currentViewingMeme, currentViewingMeme != meme { - RoundedRectangle(cornerRadius: 20) - .foregroundStyle(Color.Background.dimmer) - } - } - .animation(.smooth, value: currentViewingMeme) - .scrollTransition { content, phase in - content - .scaleEffect(phase.isIdentity ? 1.0 : 0.9) - .blur(radius: phase.isIdentity ? 0 : 1) - } + MemeImageView( + imageUrl: meme.imageUrlString, + isDimmed: meme.id != currentMeme?.id + ) + .animation(.smooth, value: meme) + .scrollTransition { content, phase in + content + .scaleEffect(phase.isIdentity ? 1.0 : 0.9) + .blur(radius: phase.isIdentity ? 0 : 1) + } } } .frame(height: 310) @@ -47,33 +41,41 @@ struct RecommendMemeImagesView: View { } .scrollIndicators(.never) .scrollTargetBehavior(.viewAligned) - .scrollPosition(id: $currentViewingMeme) + .scrollPosition(id: $currentMeme) .contentMargins(.horizontal, 60.0) .padding(.top, 36) .padding(.bottom, 20) - .onChange(of: currentViewingMeme) { oldValue, newValue in - if let newValue { - self.currentViewingMemeId = newValue.id - self.currentViewingMemeReaction = newValue.reaction - } - } - if let currentViewingMeme, isTagHidden == false { - HashTagView(keywords: currentViewingMeme.keywords) + if let currentMeme, isTagHidden == false { + HashTagView(keywords: currentMeme.keywords) } } .onAppear { - self.currentViewingMeme = self.memes.first - self.currentViewingMemeId = self.currentViewingMeme?.id - self.currentViewingMemeReaction = self.currentViewingMeme?.reaction + currentMeme = memes.first + } + .onChange(of: memes) { _, value in + let current = value.first(where: { + $0.id == currentMeme?.id + }) + currentMeme = current } } } #Preview { RecommendMemeImagesView( - currentViewingMemeId: .constant("668a44950289555e368174a6"), - currentViewingMemeReaction: .constant(0), + currentMeme: .constant( + MemeDetail( + id: "668a44950289555e368174a6", + title: "심란한 명수옹", + keywords: ["공부", "학생", "시험기간"], + imageUrlString: "https://avatars.githubusercontent.com/u/26344479?s=64&v=4", + source: "깃허브", + isTodayMeme: true, + reaction: 4, + isFarmemed: false + ) + ), memes: [ MemeDetail( id: "668a44950289555e368174a6", @@ -82,7 +84,7 @@ struct RecommendMemeImagesView: View { imageUrlString: "https://avatars.githubusercontent.com/u/26344479?s=64&v=4", source: "깃허브", isTodayMeme: true, - reaction: 4, + reaction: 4, isFarmemed: false ), MemeDetail( diff --git a/Projects/Features/Recommend/Sources/Presentation/RecommendView.swift b/Projects/Features/Recommend/Sources/Presentation/RecommendView.swift index b8da0a8..77080e1 100644 --- a/Projects/Features/Recommend/Sources/Presentation/RecommendView.swift +++ b/Projects/Features/Recommend/Sources/Presentation/RecommendView.swift @@ -20,9 +20,7 @@ public struct RecommendView: View { @State private var memeImageHeight: CGFloat = 0 @State private var zstackHeight: CGFloat = 0 @State private var buttonHeight: CGFloat = 0 - - @State private var currentViewingMemeId: String? - @State private var currentViewingMemeReaction: Int? + @State private var currentMeme: MemeDetail? public init( _ viewModel: RecommendViewModel @@ -45,8 +43,7 @@ public struct RecommendView: View { if viewModel.state.recommendMemes.count > 0 { RecommendMemeImagesView( - currentViewingMemeId: $currentViewingMemeId, - currentViewingMemeReaction: $currentViewingMemeReaction, + currentMeme: $currentMeme, memes: viewModel.state.recommendMemes, isTagHidden: isOverlapView ) @@ -63,25 +60,25 @@ public struct RecommendView: View { Spacer() RecommendMemeButtonView( - reactionCount: $currentViewingMemeReaction, + meme: $currentMeme, reactionButtonTapped: { viewModel.dispatch( - type: .likeButtonTapped(memeId: currentViewingMemeId) + type: .likeButtonTapped(memeId: currentMeme?.id) ) }, copyButtonTapped: { viewModel.dispatch( - type: .copyButtonTapped(memeId: currentViewingMemeId) + type: .copyButtonTapped(memeImageUrl: currentMeme?.imageUrlString) ) }, shareButtonTapped : { viewModel.dispatch( - type: .shareButtonTapped(memeId: currentViewingMemeId) + type: .shareButtonTapped(memeImageUrl: currentMeme?.imageUrlString) ) }, saveButtonTapped : { viewModel.dispatch( - type: .farmemeButtonTapped(memeId: currentViewingMemeId) + type: .farmemeButtonTapped(memeId: currentMeme?.id) ) } ) @@ -106,6 +103,11 @@ public struct RecommendView: View { endPoint: .bottom ) ) + .onChange(of: currentMeme) { + if let currentMeme { + viewModel.dispatch(type: .showRecommendMeme(memeId: currentMeme.id)) + } + } } } diff --git a/Projects/Features/Recommend/Sources/Presentation/RecommendViewModel.swift b/Projects/Features/Recommend/Sources/Presentation/RecommendViewModel.swift index ed683ce..fd313af 100644 --- a/Projects/Features/Recommend/Sources/Presentation/RecommendViewModel.swift +++ b/Projects/Features/Recommend/Sources/Presentation/RecommendViewModel.swift @@ -22,8 +22,8 @@ public final class RecommendViewModel: ViewModelType, ObservableObject { case initializeView case showRecommendMeme(memeId: String?) case likeButtonTapped(memeId: String?) - case copyButtonTapped(memeId: String?) - case shareButtonTapped(memeId: String?) + case copyButtonTapped(memeImageUrl: String?) + case shareButtonTapped(memeImageUrl: String?) case farmemeButtonTapped(memeId: String?) } @@ -72,10 +72,10 @@ public final class RecommendViewModel: ViewModelType, ObservableObject { await postShownMeme(memeId: memeId) case .likeButtonTapped(let memeId): await postReaction(memeId: memeId) - case .copyButtonTapped(let memeId): - await copyImage(memeId: memeId) - case .shareButtonTapped(let memeId): - await showShareSheet(memeId: memeId) + case .copyButtonTapped(let memeImageUrl): + await copyImage(memeImageUrl: memeImageUrl) + case .shareButtonTapped(let memeImageUrl): + await showShareSheet(memeImageUrl: memeImageUrl) case .farmemeButtonTapped(let memeId): await saveMeme(memeId: memeId) } @@ -101,7 +101,10 @@ private extension RecommendViewModel { func postShownMeme(memeId: String?) async { guard let memeId else { return } do { - try await watchMemeUseCase.execute(memeId: memeId, type: "reommend") + try await watchMemeUseCase.execute(memeId: memeId, type: "recommend") + let user = try await getUserInfoUseCase.get() + self.state.userLevel = user.level + self.state.memeRecommendWatchCount = user.memeRecommendWatchCount } catch { print("Failed show recommnedMeme : \(error)") } @@ -120,15 +123,10 @@ private extension RecommendViewModel { } } - func copyImage(memeId: String?) async { - guard let memeId else { return } - guard let index = self.state.recommendMemes.firstIndex(where: { $0.id == memeId }) else { - return - } + func copyImage(memeImageUrl: String?) async { + guard let memeImageUrl else { return } - guard let url = URL(string: self.state.recommendMemes[index].imageUrlString) else { - return - } + guard let url = URL(string: memeImageUrl) else { return } do { let (data, _) = try await URLSession.shared.data(from: url) @@ -142,13 +140,10 @@ private extension RecommendViewModel { } } - func showShareSheet(memeId: String?) async { - guard let memeId else { return } - guard let index = self.state.recommendMemes.firstIndex(where: { $0.id == memeId }) else { - return - } + func showShareSheet(memeImageUrl: String?) async { + guard let memeImageUrl else { return } - guard let url = URL(string: self.state.recommendMemes[index].imageUrlString) else { + guard let url = URL(string: memeImageUrl) else { print("invalid url") return } diff --git a/Projects/Features/Recommend/Sources/Presentation/View/LikeButton.swift b/Projects/Features/Recommend/Sources/Presentation/View/LikeButton.swift index 1649657..760182b 100644 --- a/Projects/Features/Recommend/Sources/Presentation/View/LikeButton.swift +++ b/Projects/Features/Recommend/Sources/Presentation/View/LikeButton.swift @@ -14,18 +14,18 @@ import Lottie public struct LikeButton: View { // MARK: - Properties - @Binding var reactionCount: Int? @State var playbackMode: LottiePlaybackMode = .paused(at: .progress(100)) + private let reactionCount: Int private let didTapped: () -> Void // MARK: - Initializers public init( - reactionCount: Binding, + reactionCount: Int?, didTapped: @escaping () -> Void ) { - self._reactionCount = reactionCount + self.reactionCount = reactionCount ?? 0 self.didTapped = didTapped } @@ -36,20 +36,22 @@ public struct LikeButton: View { iconView countLabel } - .onTapGesture(perform: { - playbackMode = .playing(.fromProgress(0, toProgress: 1, loopMode: .playOnce)) - - self.didTapped() - }) .frame(width: 156 ,height: 50, alignment: .center) .background(Color.Background.white) .cornerRadius(40) .clipped(antialiased: true) + .onTapGesture { + playbackMode = .playing( + .fromProgress(0, toProgress: 1, loopMode: .playOnce) + ) + + self.didTapped() + } } @ViewBuilder var iconView: some View { - if reactionCount ?? 0 <= 0 { + if reactionCount <= 0 { ResourceKitAsset.Icon.ㅋ.swiftUIImage } else { LottieView(animation: AnimationAsset.kkButtonActive.animation) @@ -63,13 +65,18 @@ public struct LikeButton: View { @ViewBuilder var countLabel: some View { - Text("\((reactionCount ?? 0 > 0) ? "+\(reactionCount)" : "개웃겨")") - .font((reactionCount ?? 0 > 0) ? Font.Heading.Medium.bold : Font.Family2.outLine) - .foregroundColor((reactionCount ?? 0 > 0) ? Color.Text.brand : Color.Text.primary) + Text("\((reactionCount > 0) ? "+\(reactionCount)" : "개웃겨")") + .font( + (reactionCount > 0) ? Font.Heading.Medium.bold :Font.Family2.outLine + ) + .foregroundColor( + (reactionCount > 0) ? Color.Text.brand : Color.Text.primary + ) } } #Preview { - @State var count: Int? = 0 - return LikeButton(reactionCount: $count, didTapped: {}) + var count: Int = 0 + + return LikeButton(reactionCount: count, didTapped: { }) } diff --git a/Projects/Features/Recommend/Sources/Presentation/View/MemeImageView.swift b/Projects/Features/Recommend/Sources/Presentation/View/MemeImageView.swift index 30d5ef9..fdb4a29 100644 --- a/Projects/Features/Recommend/Sources/Presentation/View/MemeImageView.swift +++ b/Projects/Features/Recommend/Sources/Presentation/View/MemeImageView.swift @@ -10,13 +10,8 @@ import Kingfisher import ResourceKit struct MemeImageView: View { - private let imageUrl: String - - public init( - imageUrl: String - ) { - self.imageUrl = imageUrl - } + let imageUrl: String + let isDimmed: Bool public var body: some View { KFImage(URL(string: imageUrl)) @@ -24,6 +19,10 @@ struct MemeImageView: View { .frame(width: 270, height: 310) .cornerRadius(20) .overlay { + if isDimmed { + RoundedRectangle(cornerRadius: 20) + .foregroundStyle(Color.Background.dimmer) + } RoundedRectangle(cornerRadius: 20) .inset(by: 1) .stroke(Color.Border.primary, lineWidth: 2)