콘텐츠 상세 - 콘텐츠 고정/해제 기능 추가
This commit is contained in:
parent
bd818918f3
commit
12d2c09434
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ic_pin.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ic_pin_cancel.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_pin_cancel.imageset/ic_pin_cancel.png
vendored
Normal file
BIN
SodaLive/Resources/Assets.xcassets/ic_pin_cancel.imageset/ic_pin_cancel.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ic_trash_can.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 787 B |
|
@ -34,6 +34,8 @@ enum ContentApi {
|
|||
case getAudioContentListByCurationId(curationId: Int, page: Int, size: Int, sort: ContentCurationViewModel.Sort)
|
||||
case getContentRanking(page: Int, size: Int, sortType: String)
|
||||
case getContentRankingSortType
|
||||
case pinContent(contentId: Int)
|
||||
case unpinContent(contentId: Int)
|
||||
}
|
||||
|
||||
extension ContentApi: TargetType {
|
||||
|
@ -117,6 +119,12 @@ extension ContentApi: TargetType {
|
|||
|
||||
case .getContentRankingSortType:
|
||||
return "/audio-content/ranking-sort-type"
|
||||
|
||||
case .pinContent(let contentId):
|
||||
return "/audio-content/pin-to-the-top/\(contentId)"
|
||||
|
||||
case .unpinContent(let contentId):
|
||||
return "/audio-content/unpin-at-the-top/\(contentId)"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,10 +139,10 @@ extension ContentApi: TargetType {
|
|||
case .getMainBannerList, .getMainOrderList, .getNewContentUploadCreatorList, .getCurationList:
|
||||
return .get
|
||||
|
||||
case .likeContent, .modifyAudioContent, .modifyComment:
|
||||
case .likeContent, .modifyAudioContent, .modifyComment, .unpinContent:
|
||||
return .put
|
||||
|
||||
case .registerComment, .orderAudioContent, .addAllPlaybackTracking, .uploadAudioContent, .donation:
|
||||
case .registerComment, .orderAudioContent, .addAllPlaybackTracking, .uploadAudioContent, .donation, .pinContent:
|
||||
return .post
|
||||
|
||||
case .deleteAudioContent:
|
||||
|
@ -259,6 +267,9 @@ extension ContentApi: TargetType {
|
|||
] as [String : Any]
|
||||
|
||||
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
|
||||
|
||||
case .pinContent, .unpinContent:
|
||||
return .requestPlain
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -112,4 +112,12 @@ final class ContentRepository {
|
|||
func getContentRanking(page: Int, size: Int, sortType: String = "매출") -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.getContentRanking(page: page, size: size, sortType: sortType))
|
||||
}
|
||||
|
||||
func pinContent(contentId: Int) -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.pinContent(contentId: contentId))
|
||||
}
|
||||
|
||||
func unpinContent(contentId: Int) -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.unpinContent(contentId: contentId))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,10 @@ struct ContentDetailMenuView: View {
|
|||
|
||||
@Binding var isShowing: Bool
|
||||
|
||||
let isPin: Bool
|
||||
let isShowCreatorMenu: Bool
|
||||
|
||||
let pinAction: () -> Void
|
||||
let modifyAction: () -> Void
|
||||
let deleteAction: () -> Void
|
||||
let reportAction: () -> Void
|
||||
|
@ -28,7 +31,26 @@ struct ContentDetailMenuView: View {
|
|||
|
||||
VStack(spacing: 13.3) {
|
||||
if isShowCreatorMenu {
|
||||
HStack(spacing: 0) {
|
||||
HStack(spacing: 13.3) {
|
||||
Image(isPin ? "ic_pin_cancel" : "ic_pin")
|
||||
|
||||
Text(isPin ? "내 채널에 고정 취소" : "내 채널에 고정")
|
||||
.font(.custom(Font.medium.rawValue, size: 16.7))
|
||||
.foregroundColor(.white)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 26.7)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
isShowing = false
|
||||
pinAction()
|
||||
}
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
Image("ic_make_message")
|
||||
|
||||
Text("수정")
|
||||
.font(.custom(Font.medium.rawValue, size: 16.7))
|
||||
.foregroundColor(.white)
|
||||
|
@ -43,7 +65,9 @@ struct ContentDetailMenuView: View {
|
|||
modifyAction()
|
||||
}
|
||||
|
||||
HStack(spacing: 0) {
|
||||
HStack(spacing: 13.3) {
|
||||
Image("ic_trash_can")
|
||||
|
||||
Text("삭제")
|
||||
.font(.custom(Font.medium.rawValue, size: 16.7))
|
||||
.foregroundColor(.white)
|
||||
|
|
|
@ -219,7 +219,19 @@ struct ContentDetailView: View {
|
|||
VStack(spacing: 0) {
|
||||
ContentDetailMenuView(
|
||||
isShowing: $viewModel.isShowReportMenu,
|
||||
isPin: viewModel.audioContent!.isPin,
|
||||
isShowCreatorMenu: viewModel.audioContent!.creator.creatorId == UserDefaults.int(forKey: .userId),
|
||||
pinAction: {
|
||||
if viewModel.audioContent!.isPin {
|
||||
viewModel.unpinContent(contentId: contentId)
|
||||
} else {
|
||||
if viewModel.audioContent!.isAvailablePin {
|
||||
viewModel.pinContent(contentId: contentId)
|
||||
} else {
|
||||
viewModel.isShowNoticePinContentPopup = true
|
||||
}
|
||||
}
|
||||
},
|
||||
modifyAction: {
|
||||
if viewModel.audioContent!.creator.creatorId == UserDefaults.int(forKey: .userId) {
|
||||
AppState
|
||||
|
@ -284,6 +296,21 @@ struct ContentDetailView: View {
|
|||
viewModel.donation(can: can, comment: comment)
|
||||
}
|
||||
}
|
||||
|
||||
if viewModel.isShowNoticePinContentPopup {
|
||||
SodaDialog(
|
||||
title: "고정 한도 도달",
|
||||
desc: "이 콘텐츠를 고정하시겠어요? " +
|
||||
"채널에 콘텐츠를 최대 3개까지 고정할 수 있습니다." +
|
||||
"이 콘텐츠를 고정하면 가장 오래된 콘텐츠가 대체됩니다.",
|
||||
confirmButtonTitle: "확인",
|
||||
confirmButtonAction: {
|
||||
viewModel.pinContent(contentId: contentId)
|
||||
},
|
||||
cancelButtonTitle: "취소",
|
||||
cancelButtonAction: {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(
|
||||
|
|
|
@ -34,6 +34,7 @@ final class ContentDetailViewModel: ObservableObject {
|
|||
@Published var isShowReportMenu = false
|
||||
@Published var isShowReportView = false
|
||||
@Published var isShowDeleteConfirm = false
|
||||
@Published var isShowNoticePinContentPopup = false
|
||||
|
||||
var contentId: Int = 0 {
|
||||
didSet {
|
||||
|
@ -448,4 +449,84 @@ final class ContentDetailViewModel: ObservableObject {
|
|||
.store(in: &subscription)
|
||||
}
|
||||
}
|
||||
|
||||
func pinContent(contentId: Int) {
|
||||
isLoading = true
|
||||
repository.pinContent(contentId: contentId)
|
||||
.sink { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
DEBUG_LOG("finish")
|
||||
case .failure(let error):
|
||||
ERROR_LOG(error.localizedDescription)
|
||||
}
|
||||
} receiveValue: { [unowned self] response in
|
||||
self.isLoading = false
|
||||
let responseData = response.data
|
||||
|
||||
do {
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||
|
||||
if decoded.success {
|
||||
self.errorMessage = "고정되었습니다"
|
||||
self.isShowPopup = true
|
||||
|
||||
self.getAudioContentDetail()
|
||||
} else {
|
||||
if let message = decoded.message {
|
||||
self.errorMessage = message
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
|
||||
func unpinContent(contentId: Int) {
|
||||
isLoading = true
|
||||
repository.unpinContent(contentId: contentId)
|
||||
.sink { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
DEBUG_LOG("finish")
|
||||
case .failure(let error):
|
||||
ERROR_LOG(error.localizedDescription)
|
||||
}
|
||||
} receiveValue: { [unowned self] response in
|
||||
self.isLoading = false
|
||||
let responseData = response.data
|
||||
|
||||
do {
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||
|
||||
if decoded.success {
|
||||
self.errorMessage = "해제되었습니다"
|
||||
self.isShowPopup = true
|
||||
|
||||
self.getAudioContentDetail()
|
||||
} else {
|
||||
if let message = decoded.message {
|
||||
self.errorMessage = message
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ struct GetAudioContentDetailResponse: Decodable {
|
|||
let likeCount: Int
|
||||
let commentList: [GetAudioContentCommentListItem]
|
||||
let commentCount: Int
|
||||
let isPin: Bool
|
||||
let isAvailablePin: Bool
|
||||
let creator: AudioContentCreator
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ struct CreatorCommunityAllView: View {
|
|||
ZStack {
|
||||
if viewModel.isShowReportMenu {
|
||||
VStack(spacing: 0) {
|
||||
ContentDetailMenuView(
|
||||
CreatorCommunityMenuView(
|
||||
isShowing: $viewModel.isShowReportMenu,
|
||||
isShowCreatorMenu: creatorId == UserDefaults.int(forKey: .userId),
|
||||
modifyAction: {
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// CreatorCommunityMenuView.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 1/29/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CreatorCommunityMenuView: View {
|
||||
@Binding var isShowing: Bool
|
||||
|
||||
let isShowCreatorMenu: Bool
|
||||
|
||||
let modifyAction: () -> Void
|
||||
let deleteAction: () -> Void
|
||||
let reportAction: () -> Void
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Color.black
|
||||
.opacity(0.7)
|
||||
.ignoresSafeArea()
|
||||
.onTapGesture { isShowing = false }
|
||||
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
|
||||
VStack(spacing: 13.3) {
|
||||
if isShowCreatorMenu {
|
||||
HStack(spacing: 13.3) {
|
||||
Image("ic_make_message")
|
||||
|
||||
Text("수정")
|
||||
.font(.custom(Font.medium.rawValue, size: 16.7))
|
||||
.foregroundColor(.white)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 26.7)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
isShowing = false
|
||||
modifyAction()
|
||||
}
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
Image("ic_trash_can")
|
||||
|
||||
Text("삭제")
|
||||
.font(.custom(Font.medium.rawValue, size: 16.7))
|
||||
.foregroundColor(.white)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 26.7)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
isShowing = false
|
||||
deleteAction()
|
||||
}
|
||||
} else {
|
||||
HStack(spacing: 0) {
|
||||
Text("신고")
|
||||
.font(.custom(Font.medium.rawValue, size: 16.7))
|
||||
.foregroundColor(.white)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 26.7)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
isShowing = false
|
||||
reportAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(24)
|
||||
.background(Color(hex: "222222"))
|
||||
.cornerRadius(13.3, corners: [.topLeft, .topRight])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
CreatorCommunityMenuView(
|
||||
isShowing: .constant(true),
|
||||
isShowCreatorMenu: true,
|
||||
modifyAction: {},
|
||||
deleteAction: {},
|
||||
reportAction: {}
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue