콘텐츠 메인, 라이브 메인, 오디션 메인, 마이페이지

- 로그인 하지 않고 페이지 조회가 되도록 수정
This commit is contained in:
Yu Sung 2025-03-24 18:32:27 +09:00
parent fa94c5447f
commit 80cb19a1c7
32 changed files with 874 additions and 595 deletions

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_logo_circle_gray.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -12,6 +12,8 @@ enum AppStep {
case main case main
case login
case signUp case signUp
case findPassword case findPassword

View File

@ -89,6 +89,12 @@ extension AuditionApi: TargetType {
} }
var headers: [String : String]? { var headers: [String : String]? {
return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"] switch self {
case .getAuditionList:
return nil
default:
return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"]
}
} }
} }

View File

@ -11,15 +11,18 @@ struct AuditionView: View {
@StateObject var viewModel = AuditionViewModel() @StateObject var viewModel = AuditionViewModel()
@AppStorage("isAuditionNotification") private var isAuditionNotification: Bool = UserDefaults.bool(forKey: .isAuditionNotification) @AppStorage("isAuditionNotification") private var isAuditionNotification: Bool = UserDefaults.bool(forKey: .isAuditionNotification)
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
BaseView(isLoading: $viewModel.isLoading) { BaseView(isLoading: $viewModel.isLoading) {
VStack(spacing: 0) { VStack(spacing: 0) {
HomeNavigationBar(title: "오디션") { HomeNavigationBar(title: "오디션") {
Image(isAuditionNotification ? "btn_audition_notification_selected" : "btn_audition_notification_normal") if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.onTapGesture { Image(isAuditionNotification ? "btn_audition_notification_selected" : "btn_audition_notification_normal")
viewModel.updateNotificationSettings() .onTapGesture {
} viewModel.updateNotificationSettings()
}
}
} }
HStack(spacing: 0) { HStack(spacing: 0) {
@ -70,11 +73,16 @@ struct AuditionView: View {
AuditionItemView(item: item) AuditionItemView(item: item)
.onTapGesture { .onTapGesture {
if !item.isOff { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if !item.isOff {
AppState.shared
.setAppStep(
step: .auditionDetail(auditionId: item.id)
)
}
} else {
AppState.shared AppState.shared
.setAppStep( .setAppStep(step: .login)
step: .auditionDetail(auditionId: item.id)
)
} }
} }
} }
@ -104,23 +112,20 @@ struct AuditionView: View {
AuditionItemView(item: item) AuditionItemView(item: item)
.padding(.top, 25) .padding(.top, 25)
.onTapGesture { }
} else {
AuditionItemView(item: item)
.onTapGesture {
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if !item.isOff { if !item.isOff {
AppState.shared AppState.shared
.setAppStep( .setAppStep(
step: .auditionDetail(auditionId: item.id) step: .auditionDetail(auditionId: item.id)
) )
} }
} } else {
}
} else {
AuditionItemView(item: item)
.onTapGesture {
if !item.isOff {
AppState.shared AppState.shared
.setAppStep( .setAppStep(step: .login)
step: .auditionDetail(auditionId: item.id)
)
} }
} }
} }

View File

@ -41,6 +41,7 @@ enum ContentApi {
case getContentMainHome(isAdultContentVisible: Bool, contentType: ContentType) case getContentMainHome(isAdultContentVisible: Bool, contentType: ContentType)
case getPopularContentByCreator(creatorId: Int, isAdultContentVisible: Bool, contentType: ContentType) case getPopularContentByCreator(creatorId: Int, isAdultContentVisible: Bool, contentType: ContentType)
case getContentMainHomeContentRanking(isAdultContentVisible: Bool, contentType: ContentType, sortType: String)
case getContentMainSeries(isAdultContentVisible: Bool, contentType: ContentType) case getContentMainSeries(isAdultContentVisible: Bool, contentType: ContentType)
case getRecommendSeriesListByGenre(genreId: Int, isAdultContentVisible: Bool, contentType: ContentType) case getRecommendSeriesListByGenre(genreId: Int, isAdultContentVisible: Bool, contentType: ContentType)
@ -168,6 +169,9 @@ extension ContentApi: TargetType {
case .getPopularContentByCreator: case .getPopularContentByCreator:
return "/v2/audio-content/main/home/popular-content-by-creator" return "/v2/audio-content/main/home/popular-content-by-creator"
case .getContentMainHomeContentRanking:
return "/v2/audio-content/main/home/content/ranking"
case .getContentMainSeries: case .getContentMainSeries:
return "/v2/audio-content/main/series" return "/v2/audio-content/main/series"
@ -238,7 +242,7 @@ extension ContentApi: TargetType {
case .getMainBannerList, .getMainOrderList, .getNewContentUploadCreatorList, .getCurationList, .getAudioContentByTheme: case .getMainBannerList, .getMainOrderList, .getNewContentUploadCreatorList, .getCurationList, .getAudioContentByTheme:
return .get return .get
case .getContentMainHome, .getPopularContentByCreator, .getContentMainSeries, .getRecommendSeriesListByGenre, .getRecommendSeriesByCreator, .getContentMainContent, case .getContentMainHome, .getPopularContentByCreator, .getContentMainHomeContentRanking, .getContentMainSeries, .getRecommendSeriesListByGenre, .getRecommendSeriesByCreator, .getContentMainContent,
.getContentMainNewContentOfTheme, .getDailyContentRanking, .getRecommendContentByTag, .getContentMainAlarm, .getContentMainAlarmAll, .getContentMainNewContentOfTheme, .getDailyContentRanking, .getRecommendContentByTag, .getContentMainAlarm, .getContentMainAlarmAll,
.getContentMainAsmr, .getPopularAsmrContentByCreator, .getContentMainReplay, .getPopularReplayContentByCreator, .getContentMainAsmr, .getPopularAsmrContentByCreator, .getContentMainReplay, .getPopularReplayContentByCreator,
.getContentMainFree, .getIntroduceCreatorList, .getNewFreeContentOfTheme, .getPopularFreeContentByCreator, .getCompletedSeries, .getContentMainContentPopularContentByCreator: .getContentMainFree, .getIntroduceCreatorList, .getNewFreeContentOfTheme, .getPopularFreeContentByCreator, .getCompletedSeries, .getContentMainContentPopularContentByCreator:
@ -439,6 +443,15 @@ extension ContentApi: TargetType {
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
case .getContentMainHomeContentRanking(let isAdultContentVisible, let contentType, let sortType):
let parameters = [
"sort-type": sortType,
"isAdultContentVisible": isAdultContentVisible,
"contentType": contentType
] as [String : Any]
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
case .getRecommendSeriesByCreator(let creatorId, let isAdultContentVisible, let contentType): case .getRecommendSeriesByCreator(let creatorId, let isAdultContentVisible, let contentType):
let parameters = [ let parameters = [
"creatorId": creatorId, "creatorId": creatorId,

View File

@ -11,6 +11,8 @@ import Kingfisher
struct ContentMainTabRankContentView: View { struct ContentMainTabRankContentView: View {
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
let rows = [ let rows = [
GridItem(.fixed(60), alignment: .leading), GridItem(.fixed(60), alignment: .leading),
GridItem(.fixed(60), alignment: .leading), GridItem(.fixed(60), alignment: .leading),
@ -92,9 +94,13 @@ struct ContentMainTabRankContentView: View {
.frame(width: screenSize().width * 0.66, alignment: .leading) .frame(width: screenSize().width * 0.66, alignment: .leading)
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
AppState if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.shared AppState.shared
.setAppStep(step: .contentDetail(contentId: content.contentId)) .setAppStep(step: .contentDetail(contentId: content.contentId))
} else {
AppState.shared
.setAppStep(step: .login)
}
} }
} }
} }

View File

@ -10,6 +10,8 @@ import Kingfisher
struct ContentByChannelView: View { struct ContentByChannelView: View {
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
let title: String let title: String
let creatorList: [ContentCreatorResponse] let creatorList: [ContentCreatorResponse]
let contentList: [GetAudioContentRankingItem] let contentList: [GetAudioContentRankingItem]
@ -111,15 +113,23 @@ struct ContentByChannelView: View {
.foregroundColor(.gray77) .foregroundColor(.gray77)
} }
.onTapGesture { .onTapGesture {
AppState if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.shared AppState.shared
.setAppStep(step: .creatorDetail(userId: content.creatorId)) .setAppStep(step: .creatorDetail(userId: content.creatorId))
} else {
AppState.shared
.setAppStep(step: .login)
}
} }
} }
.onTapGesture { .onTapGesture {
AppState if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.shared AppState.shared
.setAppStep(step: .contentDetail(contentId: content.contentId)) .setAppStep(step: .contentDetail(contentId: content.contentId))
} else {
AppState.shared
.setAppStep(step: .login)
}
} }
} }
} }

View File

@ -31,4 +31,14 @@ class ContentMainTabHomeRepository {
) )
) )
} }
func getContentRanking(sortType: String = "매출") -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(
.getContentMainHomeContentRanking(
isAdultContentVisible: UserDefaults.isAdultContentVisible(),
contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL,
sortType: sortType
)
)
}
} }

View File

@ -10,6 +10,7 @@ import SwiftUI
struct ContentMainTabHomeView: View { struct ContentMainTabHomeView: View {
@StateObject var viewModel = ContentMainTabHomeViewModel() @StateObject var viewModel = ContentMainTabHomeViewModel()
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
BaseView(isLoading: $viewModel.isLoading) { BaseView(isLoading: $viewModel.isLoading) {
@ -29,10 +30,12 @@ struct ContentMainTabHomeView: View {
Spacer() Spacer()
Image("ic_content_keep") if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.onTapGesture { Image("ic_content_keep")
AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist)) .onTapGesture {
} AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist))
}
}
} }
.padding(.horizontal, 13.3) .padding(.horizontal, 13.3)
@ -45,153 +48,156 @@ struct ContentMainTabHomeView: View {
.padding(.horizontal, 13.3) .padding(.horizontal, 13.3)
} }
if viewModel.bannerList.count > 0 { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
viewModel.bannerList.count > 0 {
ContentMainBannerViewV2(bannerList: viewModel.bannerList) ContentMainBannerViewV2(bannerList: viewModel.bannerList)
.padding(.top, 30) .padding(.top, 30)
.padding(.horizontal, 13.3) .padding(.horizontal, 13.3)
} }
HStack(spacing: 0) { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
Image("ic_title_search_black")
Text("채널명을 입력해 보세요")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.gray55)
.keyboardType(.default)
.padding(.horizontal, 13.3)
Spacer()
}
.padding(.horizontal, 21.3)
.frame(height: 50)
.frame(maxWidth: .infinity)
.background(Color.gray22)
.overlay(
RoundedRectangle(cornerRadius: 6.7)
.strokeBorder(lineWidth: 1)
.foregroundColor(Color.graybb)
)
.padding(.top, 30)
.padding(.horizontal, 13.3)
.onTapGesture {
UserDefaults.set("", forKey: .searchChannel)
AppState.shared.setAppStep(step: .searchChannel)
}
VStack(spacing: 13.3) {
HStack(spacing: 0) { HStack(spacing: 0) {
ContentMainTabCategoryView( Image("ic_title_search_black")
imageName: "ic_category_series",
title: "시리즈",
onClick: {
AppState.shared
.setAppStep(
step: .contentMain(
startTab: .SERIES
)
)
}
)
.frame(maxWidth: .infinity)
ContentMainTabCategoryView( Text("채널명을 입력해 보세요")
imageName: "ic_category_content", .font(.custom(Font.medium.rawValue, size: 13.3))
title: "단편", .foregroundColor(Color.gray55)
onClick: { .keyboardType(.default)
AppState.shared .padding(.horizontal, 13.3)
.setAppStep(
step: .contentMain(
startTab: .CONTENT
)
)
}
)
.frame(maxWidth: .infinity)
ContentMainTabCategoryView( Spacer()
imageName: "ic_category_alarm", }
title: "모닝콜", .padding(.horizontal, 21.3)
onClick: { .frame(height: 50)
AppState.shared .frame(maxWidth: .infinity)
.setAppStep( .background(Color.gray22)
step: .contentMain( .overlay(
startTab: .ALARM RoundedRectangle(cornerRadius: 6.7)
) .strokeBorder(lineWidth: 1)
) .foregroundColor(Color.graybb)
} )
) .padding(.top, 30)
.frame(maxWidth: .infinity) .padding(.horizontal, 13.3)
.onTapGesture {
ContentMainTabCategoryView( UserDefaults.set("", forKey: .searchChannel)
imageName: "ic_category_asmr", AppState.shared.setAppStep(step: .searchChannel)
title: "ASMR",
onClick: {
AppState.shared
.setAppStep(
step: .contentMain(
startTab: .ASMR
)
)
}
)
.frame(maxWidth: .infinity)
} }
HStack(spacing: 0) { VStack(spacing: 13.3) {
ContentMainTabCategoryView( HStack(spacing: 0) {
imageName: "ic_category_replay", ContentMainTabCategoryView(
title: "다시듣기", imageName: "ic_category_series",
onClick: { title: "시리즈",
AppState.shared onClick: {
.setAppStep( AppState.shared
step: .contentMain( .setAppStep(
startTab: .REPLAY step: .contentMain(
startTab: .SERIES
)
) )
) }
} )
) .frame(maxWidth: .infinity)
.frame(maxWidth: .infinity)
ContentMainTabCategoryView(
ContentMainTabCategoryView( imageName: "ic_category_content",
imageName: "ic_category_free", title: "단편",
title: "무료", onClick: {
onClick: { AppState.shared
AppState.shared .setAppStep(
.setAppStep( step: .contentMain(
step: .contentMain( startTab: .CONTENT
startTab: .FREE )
) )
) }
} )
) .frame(maxWidth: .infinity)
.frame(maxWidth: .infinity)
ContentMainTabCategoryView(
imageName: "ic_category_alarm",
title: "모닝콜",
onClick: {
AppState.shared
.setAppStep(
step: .contentMain(
startTab: .ALARM
)
)
}
)
.frame(maxWidth: .infinity)
ContentMainTabCategoryView(
imageName: "ic_category_asmr",
title: "ASMR",
onClick: {
AppState.shared
.setAppStep(
step: .contentMain(
startTab: .ASMR
)
)
}
)
.frame(maxWidth: .infinity)
}
ContentMainTabCategoryView( HStack(spacing: 0) {
imageName: "ic_category_audio_book", ContentMainTabCategoryView(
title: "오디오북", imageName: "ic_category_replay",
onClick: { title: "다시듣기",
viewModel.errorMessage = "준비중입니다." onClick: {
viewModel.isShowPopup = true AppState.shared
} .setAppStep(
) step: .contentMain(
.frame(maxWidth: .infinity) startTab: .REPLAY
)
ContentMainTabCategoryView( )
imageName: "ic_category_audio_toon", }
title: "오디오툰", )
onClick: { .frame(maxWidth: .infinity)
viewModel.errorMessage = "준비중입니다."
viewModel.isShowPopup = true ContentMainTabCategoryView(
} imageName: "ic_category_free",
) title: "무료",
.frame(maxWidth: .infinity) onClick: {
AppState.shared
.setAppStep(
step: .contentMain(
startTab: .FREE
)
)
}
)
.frame(maxWidth: .infinity)
ContentMainTabCategoryView(
imageName: "ic_category_audio_book",
title: "오디오북",
onClick: {
viewModel.errorMessage = "준비중입니다."
viewModel.isShowPopup = true
}
)
.frame(maxWidth: .infinity)
ContentMainTabCategoryView(
imageName: "ic_category_audio_toon",
title: "오디오툰",
onClick: {
viewModel.errorMessage = "준비중입니다."
viewModel.isShowPopup = true
}
)
.frame(maxWidth: .infinity)
}
} }
.padding(.vertical, 13.3)
.background(Color.gray22)
.cornerRadius(5.3)
.padding(.top, 30)
.padding(.horizontal, 13.3)
} }
.padding(.vertical, 13.3)
.background(Color.gray22)
.cornerRadius(5.3)
.padding(.top, 30)
.padding(.horizontal, 13.3)
if let response = viewModel.rankCreatorResponse { if let response = viewModel.rankCreatorResponse {
ContentMainTabHomeRankCreatorView(response: response) ContentMainTabHomeRankCreatorView(response: response)
@ -210,7 +216,11 @@ struct ContentMainTabHomeView: View {
title: "인기 단편", title: "인기 단편",
isMore: true, isMore: true,
onClickMore: { onClickMore: {
AppState.shared.setAppStep(step: .contentRankingAll) if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .contentRankingAll)
} else {
AppState.shared.setAppStep(step: .login)
}
}, },
sortList: !viewModel.rankSortTypeList.isEmpty ? sortList: !viewModel.rankSortTypeList.isEmpty ?
viewModel.rankSortTypeList : viewModel.rankSortTypeList :
@ -221,7 +231,8 @@ struct ContentMainTabHomeView: View {
.padding(.top, 30) .padding(.top, 30)
} }
if viewModel.eventBannerList.count > 0 { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
viewModel.eventBannerList.count > 0 {
SectionEventBannerView(items: viewModel.eventBannerList) SectionEventBannerView(items: viewModel.eventBannerList)
.padding(.top, 30) .padding(.top, 30)
} }

View File

@ -11,7 +11,6 @@ import Combine
final class ContentMainTabHomeViewModel: ObservableObject { final class ContentMainTabHomeViewModel: ObservableObject {
private let repository = ContentMainTabHomeRepository() private let repository = ContentMainTabHomeRepository()
private let contentRepository = ContentRepository()
private var subscription = Set<AnyCancellable>() private var subscription = Set<AnyCancellable>()
@Published var errorMessage = "" @Published var errorMessage = ""
@ -77,7 +76,7 @@ final class ContentMainTabHomeViewModel: ObservableObject {
func getContentRanking(sort: String = "매출") { func getContentRanking(sort: String = "매출") {
isLoading = true isLoading = true
contentRepository.getContentRanking(page: 1, size: 12, sortType: sort) repository.getContentRanking(sortType: sort)
.sink { result in .sink { result in
switch result { switch result {
case .finished: case .finished:
@ -90,10 +89,10 @@ final class ContentMainTabHomeViewModel: ObservableObject {
do { do {
let jsonDecoder = JSONDecoder() let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<GetAudioContentRanking>.self, from: responseData) let decoded = try jsonDecoder.decode(ApiResponse<[GetAudioContentRankingItem]>.self, from: responseData)
if let data = decoded.data, decoded.success { if let data = decoded.data, decoded.success {
self.rankContentList = data.items self.rankContentList = data
} else { } else {
if let message = decoded.message { if let message = decoded.message {
self.errorMessage = message self.errorMessage = message

View File

@ -10,6 +10,8 @@ import Kingfisher
struct ContentMainTabHomeRankCreatorView: View { struct ContentMainTabHomeRankCreatorView: View {
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
let response: GetExplorerSectionResponse let response: GetExplorerSectionResponse
let rankingCrawns = ["ic_crown_1", "ic_crown_2", "ic_crown_3"] let rankingCrawns = ["ic_crown_1", "ic_crown_2", "ic_crown_3"]
@ -121,8 +123,13 @@ struct ContentMainTabHomeRankCreatorView: View {
} }
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
AppState.shared if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.setAppStep(step: .creatorDetail(userId: creator.id)) AppState.shared
.setAppStep(step: .creatorDetail(userId: creator.id))
} else {
AppState.shared
.setAppStep(step: .login)
}
} }
} }
} }

View File

@ -17,13 +17,16 @@ struct ContentView: View {
if appState.isChangeAdultContentVisible { if appState.isChangeAdultContentVisible {
EmptyView() EmptyView()
} else { } else {
MainView() HomeView()
} }
switch appState.appStep { switch appState.appStep {
case .splash: case .splash:
SplashView() SplashView()
case .login:
LoginView()
case .signUp: case .signUp:
SignUpView() SignUpView()

View File

@ -12,6 +12,7 @@ struct SectionEventBannerView: View {
@State private var currentIndex = 0 @State private var currentIndex = 0
@State private var timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect() @State private var timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
let items: [EventItem] let items: [EventItem]
@ -38,10 +39,14 @@ struct SectionEventBannerView: View {
) )
.tag(index) .tag(index)
.onTapGesture { .onTapGesture {
if let _ = item.detailImageUrl { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .eventDetail(event: item)) if let _ = item.detailImageUrl {
} else if let link = item.link, link.trimmingCharacters(in: .whitespaces).count > 0, let url = URL(string: link), UIApplication.shared.canOpenURL(url) { AppState.shared.setAppStep(step: .eventDetail(event: item))
UIApplication.shared.open(url) } else if let link = item.link, link.trimmingCharacters(in: .whitespaces).count > 0, let url = URL(string: link), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
} else {
AppState.shared.setAppStep(step: .login)
} }
} }
} else { } else {
@ -62,10 +67,14 @@ struct SectionEventBannerView: View {
) )
.tag(index) .tag(index)
.onTapGesture { .onTapGesture {
if let _ = item.detailImageUrl { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .eventDetail(event: item)) if let _ = item.detailImageUrl {
} else if let link = item.link, link.trimmingCharacters(in: .whitespaces).count > 0, let url = URL(string: link), UIApplication.shared.canOpenURL(url) { AppState.shared.setAppStep(step: .eventDetail(event: item))
UIApplication.shared.open(url) } else if let link = item.link, link.trimmingCharacters(in: .whitespaces).count > 0, let url = URL(string: link), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
} else {
AppState.shared.setAppStep(step: .login)
} }
} }
} }

View File

@ -12,6 +12,7 @@ struct LiveView: View {
@StateObject var viewModel = LiveViewModel() @StateObject var viewModel = LiveViewModel()
@StateObject var appState = AppState.shared @StateObject var appState = AppState.shared
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
ZStack { ZStack {
@ -44,9 +45,13 @@ struct LiveView: View {
} }
.padding(.horizontal, 13.3) .padding(.horizontal, 13.3)
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep( if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
step: .contentAllByTheme(themeId: 7) AppState.shared.setAppStep(
) step: .contentAllByTheme(themeId: 7)
)
} else {
AppState.shared.setAppStep(step: .login)
}
} }
if viewModel.recommendChannelItems.count > 0 { if viewModel.recommendChannelItems.count > 0 {
@ -61,10 +66,18 @@ struct LiveView: View {
SectionLiveNowView( SectionLiveNowView(
items: viewModel.liveNowItems, items: viewModel.liveNowItems,
onClickParticipant: { onClickParticipant: {
viewModel.enterLiveRoom(roomId: $0) if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
viewModel.enterLiveRoom(roomId: $0)
} else {
AppState.shared.setAppStep(step: .login)
}
}, },
onTapCreateLive: { onTapCreateLive: {
AppState.shared.setAppStep(step: .createLive(timeSettingMode: .NOW, onSuccess: onCreateSuccess)) if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .createLive(timeSettingMode: .NOW, onSuccess: onCreateSuccess))
} else {
AppState.shared.setAppStep(step: .login)
}
}, },
onClickRefresh: { onClickRefresh: {
viewModel.getSummary() viewModel.getSummary()
@ -82,12 +95,26 @@ struct LiveView: View {
SectionLiveReservationView( SectionLiveReservationView(
items: viewModel.liveReservationItems, items: viewModel.liveReservationItems,
onClickCancel: { viewModel.getSummary() }, onClickCancel: { viewModel.getSummary() },
onClickStart: { roomId in processStart(roomId: roomId) }, onClickStart: { roomId in
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
processStart(roomId: roomId)
} else {
AppState.shared.setAppStep(step: .login)
}
},
onClickReservation: { roomId in onClickReservation: { roomId in
viewModel.reservationLiveRoom(roomId: roomId) if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
viewModel.reservationLiveRoom(roomId: roomId)
} else {
AppState.shared.setAppStep(step: .login)
}
}, },
onTapCreateLive: { onTapCreateLive: {
AppState.shared.setAppStep(step: .createLive(timeSettingMode: .RESERVATION, onSuccess: onCreateSuccess)) if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .createLive(timeSettingMode: .RESERVATION, onSuccess: onCreateSuccess))
} else {
AppState.shared.setAppStep(step: .login)
}
} }
) )
} }
@ -102,7 +129,11 @@ struct LiveView: View {
.padding(.trailing, 16) .padding(.trailing, 16)
.padding(.bottom, 16) .padding(.bottom, 16)
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep(step: .createLive(timeSettingMode: .NOW, onSuccess: onCreateSuccess)) if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .createLive(timeSettingMode: .NOW, onSuccess: onCreateSuccess))
} else {
AppState.shared.setAppStep(step: .login)
}
} }
} }
} }

View File

@ -84,10 +84,17 @@ final class LiveViewModel: ObservableObject {
} }
func getSummary() { func getSummary() {
getFollowedChannelList() if !UserDefaults.string(forKey: UserDefaultsKey.token).trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
getFollowedChannelList()
}
getRecommendChannelList() getRecommendChannelList()
getRecommendLive() getRecommendLive()
getLatestPostListFromCreatorsYouFollow()
if !UserDefaults.string(forKey: UserDefaultsKey.token).trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
getLatestPostListFromCreatorsYouFollow()
}
isLoading = true isLoading = true
eventBannerItems.removeAll() eventBannerItems.removeAll()

View File

@ -15,6 +15,8 @@ struct SectionLiveNowView: View {
let onTapCreateLive: () -> Void let onTapCreateLive: () -> Void
let onClickRefresh: () -> Void let onClickRefresh: () -> Void
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
LazyVStack(spacing: 13.3) { LazyVStack(spacing: 13.3) {
HStack(spacing: 0) { HStack(spacing: 0) {
@ -45,20 +47,24 @@ struct SectionLiveNowView: View {
LiveNowItemView(item: item) LiveNowItemView(item: item)
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep( if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
step: .liveDetail( AppState.shared.setAppStep(
roomId: item.roomId, step: .liveDetail(
onClickParticipant: { roomId: item.roomId,
AppState.shared.isShowPlayer = false onClickParticipant: {
onClickParticipant(item.roomId) AppState.shared.isShowPlayer = false
}, onClickParticipant(item.roomId)
onClickReservation: {}, },
onClickStart: { onClickReservation: {},
}, onClickStart: {
onClickCancel: { },
} onClickCancel: {
}
)
) )
) } else {
AppState.shared.setAppStep(step: .login)
}
} }
} }
} }

View File

@ -13,6 +13,7 @@ struct SectionRecommendLiveView: View {
let items: [GetRecommendLiveResponse] let items: [GetRecommendLiveResponse]
@State private var currentIndex = 0 @State private var currentIndex = 0
@State private var timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect() @State private var timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
@ -27,10 +28,12 @@ struct SectionRecommendLiveView: View {
Spacer() Spacer()
Image("ic_message") if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.onTapGesture { Image("ic_message")
AppState.shared.setAppStep(step: .message) .onTapGesture {
} AppState.shared.setAppStep(step: .message)
}
}
} }
.frame(width: screenSize().width - 26.7, alignment: .leading) .frame(width: screenSize().width - 26.7, alignment: .leading)
@ -53,7 +56,11 @@ struct SectionRecommendLiveView: View {
height: (screenSize().width - 26.7) * 0.53 height: (screenSize().width - 26.7) * 0.53
) )
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep(step: .creatorDetail(userId: item.creatorId)) if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .creatorDetail(userId: item.creatorId))
} else {
AppState.shared.setAppStep(step: .login)
}
} }
.cornerRadius(4.7) .cornerRadius(4.7)
} else { } else {
@ -72,7 +79,11 @@ struct SectionRecommendLiveView: View {
height: (screenSize().width - 26.7) * 0.53 height: (screenSize().width - 26.7) * 0.53
) )
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep(step: .creatorDetail(userId: item.creatorId)) if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .creatorDetail(userId: item.creatorId))
} else {
AppState.shared.setAppStep(step: .login)
}
} }
.cornerRadius(4.7) .cornerRadius(4.7)
} }

View File

@ -13,6 +13,7 @@ struct SectionRecommendChannelView: View {
let items: [GetRecommendChannelResponse] let items: [GetRecommendChannelResponse]
@Binding var isFollowingList: Bool @Binding var isFollowingList: Bool
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
VStack(spacing: 21.3) { VStack(spacing: 21.3) {
@ -20,30 +21,32 @@ struct SectionRecommendChannelView: View {
if isFollowingList { if isFollowingList {
Text("팔로잉 ") Text("팔로잉 ")
.font(.custom(Font.bold.rawValue, size: 18.3)) .font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(Color(hex: "eeeeee")) .foregroundColor(.grayee)
} else { } else {
Text("추천 ") Text("추천 ")
.font(.custom(Font.bold.rawValue, size: 18.3)) .font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(Color(hex: "eeeeee")) .foregroundColor(.grayee)
} }
Text("채널") Text("채널")
.font(.custom(Font.bold.rawValue, size: 18.3)) .font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(Color(hex: "3bb9f1")) .foregroundColor(.button)
Spacer() Spacer()
Text("팔로잉 채널") if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.font(.custom(Font.medium.rawValue, size: 13.3)) Text("팔로잉 채널")
.foregroundColor(Color(hex: "777777")) .font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(.gray77)
Image(isFollowingList ? "btn_toggle_on_big" : "btn_toggle_off_big")
.resizable() Image(isFollowingList ? "btn_toggle_on_big" : "btn_toggle_off_big")
.frame(width: 33.3, height: 20) .resizable()
.padding(.leading, 6.7) .frame(width: 33.3, height: 20)
.onTapGesture { .padding(.leading, 6.7)
isFollowingList.toggle() .onTapGesture {
} isFollowingList.toggle()
}
}
} }
.frame(width: screenSize().width - 26.7, alignment: .leading) .frame(width: screenSize().width - 26.7, alignment: .leading)
@ -70,7 +73,7 @@ struct SectionRecommendChannelView: View {
Circle() Circle()
.strokeBorder(lineWidth: 3) .strokeBorder(lineWidth: 3)
.foregroundColor( .foregroundColor(
Color(hex: "3bb9f1") .button
.opacity(item.isOnAir ? 1 : 0) .opacity(item.isOnAir ? 1 : 0)
) )
) )
@ -81,21 +84,25 @@ struct SectionRecommendChannelView: View {
.foregroundColor(.white) .foregroundColor(.white)
.padding(.vertical, 2.7) .padding(.vertical, 2.7)
.padding(.horizontal, 5.7) .padding(.horizontal, 5.7)
.background(Color(hex: "3bb9f1")) .background(Color.button)
.cornerRadius(6.7) .cornerRadius(6.7)
} }
} }
Text(item.nickname) Text(item.nickname)
.font(.custom(Font.medium.rawValue, size: 11.3)) .font(.custom(Font.medium.rawValue, size: 11.3))
.foregroundColor(Color(hex: "bbbbbb")) .foregroundColor(Color.graybb)
.frame(width: screenSize().width * 0.18) .frame(width: screenSize().width * 0.18)
.lineLimit(1) .lineLimit(1)
} }
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep( if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
step: .creatorDetail(userId: item.creatorId) AppState.shared.setAppStep(
) step: .creatorDetail(userId: item.creatorId)
)
} else {
AppState.shared.setAppStep(step: .login)
}
} }
} }
@ -112,10 +119,15 @@ struct SectionRecommendChannelView: View {
.lineLimit(1) .lineLimit(1)
} }
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep(step: .followingList) if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .followingList)
} else {
AppState.shared.setAppStep(step: .login)
}
} }
} }
} }
.padding(.horizontal, 13.3)
} }
} }
} }

View File

@ -15,6 +15,7 @@ struct SectionLiveReservationView: View {
let onClickStart: (Int) -> Void let onClickStart: (Int) -> Void
let onClickReservation: (Int) -> Void let onClickReservation: (Int) -> Void
let onTapCreateLive: () -> Void let onTapCreateLive: () -> Void
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
VStack(spacing: 13.3) { VStack(spacing: 13.3) {
@ -34,14 +35,18 @@ struct SectionLiveReservationView: View {
.font(.custom(Font.light.rawValue, size: 11.3)) .font(.custom(Font.light.rawValue, size: 11.3))
.foregroundColor(Color(hex: "bbbbbb")) .foregroundColor(Color(hex: "bbbbbb"))
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep( if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
step: .liveReservationAll( AppState.shared.setAppStep(
onClickReservation: onClickReservation, step: .liveReservationAll(
onClickStart: onClickStart, onClickReservation: onClickReservation,
onClickCancel: onClickCancel, onClickStart: onClickStart,
onTapCreateLive: onTapCreateLive onClickCancel: onClickCancel,
onTapCreateLive: onTapCreateLive
)
) )
) } else {
AppState.shared.setAppStep(step: .login)
}
} }
} }
} }
@ -57,29 +62,37 @@ struct SectionLiveReservationView: View {
MyLiveReservationItemView(item: item, index: index) MyLiveReservationItemView(item: item, index: index)
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep( if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
step: .liveDetail( AppState.shared.setAppStep(
roomId: item.roomId, step: .liveDetail(
onClickParticipant: {}, roomId: item.roomId,
onClickReservation: {}, onClickParticipant: {},
onClickStart: { onClickStart(item.roomId) }, onClickReservation: {},
onClickCancel: onClickCancel onClickStart: { onClickStart(item.roomId) },
onClickCancel: onClickCancel
)
) )
) } else {
AppState.shared.setAppStep(step: .login)
}
} }
} else { } else {
LiveReservationItemView(item: item) LiveReservationItemView(item: item)
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep( if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
step: .liveDetail( AppState.shared.setAppStep(
roomId: item.roomId, step: .liveDetail(
onClickParticipant: {}, roomId: item.roomId,
onClickReservation: { onClickReservation(item.roomId) }, onClickParticipant: {},
onClickStart: { onClickStart(item.roomId) }, onClickReservation: { onClickReservation(item.roomId) },
onClickCancel: onClickCancel onClickStart: { onClickStart(item.roomId) },
onClickCancel: onClickCancel
)
) )
) } else {
AppState.shared.setAppStep(step: .login)
}
} }
} }
} }

View File

@ -25,6 +25,7 @@ struct HomeView: View {
private let contentView = ContentMainTabHomeView() private let contentView = ContentMainTabHomeView()
@State private var isShowPlayer = false @State private var isShowPlayer = false
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
GeometryReader { proxy in GeometryReader { proxy in
@ -168,10 +169,12 @@ struct HomeView: View {
} }
} }
.onAppear { .onAppear {
pushTokenUpdate() if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
viewModel.getMemberInfo() pushTokenUpdate()
viewModel.getEventPopup() viewModel.getMemberInfo()
viewModel.addAllPlaybackTracking() viewModel.getEventPopup()
viewModel.addAllPlaybackTracking()
}
} }
if appState.isShowPlayer { if appState.isShowPlayer {

View File

@ -1,27 +0,0 @@
//
// MainView.swift
// SodaLive
//
// Created by klaus on 2023/08/09.
//
import SwiftUI
struct MainView: View {
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View {
if token.count > 0 {
HomeView()
} else {
LoginView()
}
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView()
}
}

View File

@ -17,10 +17,12 @@ struct MyPageView: View {
@StateObject var viewModel = MyPageViewModel() @StateObject var viewModel = MyPageViewModel()
@State private var payload = Payload() @State private var payload = Payload()
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
BaseView(isLoading: $viewModel.isLoading) { BaseView(isLoading: $viewModel.isLoading) {
if viewModel.isShowAuthView { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
viewModel.isShowAuthView {
BootpayUI(payload: payload, requestType: BootpayRequest.TYPE_AUTHENTICATION) BootpayUI(payload: payload, requestType: BootpayRequest.TYPE_AUTHENTICATION)
.onConfirm { .onConfirm {
DEBUG_LOG("onConfirm: \($0)") DEBUG_LOG("onConfirm: \($0)")
@ -47,100 +49,45 @@ struct MyPageView: View {
GeometryReader { geo in GeometryReader { geo in
VStack { VStack {
HomeNavigationBar(title: "마이 페이지") { HomeNavigationBar(title: "마이 페이지") {
Image("ic_settings") if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.resizable() Image("ic_settings")
.frame(width: 20, height: 20) .resizable()
.onTapGesture { .frame(width: 20, height: 20)
AppState.shared.setAppStep(step: .settings) .onTapGesture {
} AppState.shared.setAppStep(step: .settings)
} }
RefreshableScrollView(
refreshing: $viewModel.isLoading,
action: {
viewModel.getMypage()
} }
) { }
VStack(spacing: 0) { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if let data = viewModel.myPageResponse { RefreshableScrollView(
MyInfoCardView(data: data) { refreshing: $viewModel.isLoading,
viewModel.getMypage() action: {
} viewModel.getMypage()
.frame(width: screenSize().width - 26.7) }
.padding(.top, 13.3) ) {
VStack(spacing: 0) {
if UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue { if let data = viewModel.myPageResponse {
Text("내 채널 보기") MyInfoCardView(data: data) {
.frame(width: screenSize().width - 26.7, height: 46.7)
.font(.custom(Font.bold.rawValue, size: 15.3))
.foregroundColor(Color.grayee)
.background(Color.button)
.cornerRadius(6.7)
.padding(.top, 26.7)
.onTapGesture {
AppState.shared.setAppStep(step: .creatorDetail(userId: UserDefaults.int(forKey: .userId)))
}
}
HStack(spacing: 10.7) {
Text("팔로잉 리스트")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.button)
.frame(maxWidth: .infinity)
.padding(.vertical, 13.3)
.background(Color.bg)
.cornerRadius(6.7)
.overlay(
RoundedRectangle(cornerRadius: 6.7)
.stroke(Color.button, lineWidth: 1.3)
)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .followingList)
}
Text("차단 리스트")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.button)
.frame(maxWidth: .infinity)
.padding(.vertical, 13.3)
.background(Color.bg)
.cornerRadius(6.7)
.overlay(
RoundedRectangle(cornerRadius: 6.7)
.stroke(Color.button, lineWidth: 1.3)
)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .blockList)
}
}
.padding(.top, 26.7)
.padding(.horizontal, 13.3)
if UserDefaults.int(forKey: .userId) != 17958 {
CanCardView(data: data) {
viewModel.getMypage() viewModel.getMypage()
} }
.frame(width: screenSize().width - 26.7) .frame(width: screenSize().width - 26.7)
.padding(.top, 26.7) .padding(.top, 13.3)
}
if UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue {
if data.isAuth { Text("내 채널 보기")
CanChargeCouponButtonView() .frame(width: screenSize().width - 26.7, height: 46.7)
.frame(width: screenSize().width - 26.7) .font(.custom(Font.bold.rawValue, size: 15.3))
.padding(.top, 13.3) .foregroundColor(Color.grayee)
.onTapGesture { .background(Color.button)
AppState.shared.setAppStep(step: .canCoupon) .cornerRadius(6.7)
} .padding(.top, 26.7)
} .onTapGesture {
AppState.shared.setAppStep(step: .creatorDetail(userId: UserDefaults.int(forKey: .userId)))
VStack(alignment: .leading, spacing: 13.3) { }
Text("내 보관함") }
.font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(Color.grayee)
HStack(spacing: 10.7) { HStack(spacing: 10.7) {
Text("구매목록") Text("팔로잉 리스트")
.font(.custom(Font.bold.rawValue, size: 14.7)) .font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.button) .foregroundColor(Color.button)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@ -153,10 +100,10 @@ struct MyPageView: View {
) )
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist)) AppState.shared.setAppStep(step: .followingList)
} }
Text("재생목록") Text("차단 리스트")
.font(.custom(Font.bold.rawValue, size: 14.7)) .font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.button) .foregroundColor(Color.button)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@ -169,26 +116,175 @@ struct MyPageView: View {
) )
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep(step: .myBox(currentTab: .playlist)) AppState.shared.setAppStep(step: .blockList)
}
}
.padding(.top, 26.7)
.padding(.horizontal, 13.3)
if UserDefaults.int(forKey: .userId) != 17958 {
CanCardView(data: data) {
viewModel.getMypage()
}
.frame(width: screenSize().width - 26.7)
.padding(.top, 26.7)
}
if data.isAuth {
CanChargeCouponButtonView()
.frame(width: screenSize().width - 26.7)
.padding(.top, 13.3)
.onTapGesture {
AppState.shared.setAppStep(step: .canCoupon)
}
}
VStack(alignment: .leading, spacing: 13.3) {
Text("내 보관함")
.font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(Color.grayee)
HStack(spacing: 10.7) {
Text("구매목록")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.button)
.frame(maxWidth: .infinity)
.padding(.vertical, 13.3)
.background(Color.bg)
.cornerRadius(6.7)
.overlay(
RoundedRectangle(cornerRadius: 6.7)
.stroke(Color.button, lineWidth: 1.3)
)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist))
}
Text("재생목록")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.button)
.frame(maxWidth: .infinity)
.padding(.vertical, 13.3)
.background(Color.bg)
.cornerRadius(6.7)
.overlay(
RoundedRectangle(cornerRadius: 6.7)
.stroke(Color.button, lineWidth: 1.3)
)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .myBox(currentTab: .playlist))
}
}
}
.padding(.top, 33)
.padding(.horizontal, 13.3)
ReservationStatusView(data: data)
.padding(.top, 40)
ServiceCenterButtonView()
.padding(.top, 40)
if !data.isAuth {
AuthButtonView()
.padding(.top, 40)
.onTapGesture {
viewModel.isShowAuthView = true
}
}
if let url = URL(string: "https://blog.naver.com/sodalive_official"),
UIApplication.shared.canOpenURL(url) {
Image("img_how_to_use")
.resizable()
.frame(
width: screenSize().width,
height: (200 * screenSize().width) / 1080
)
.padding(.vertical, 40)
.onTapGesture {
UIApplication.shared.open(url)
} }
} }
} }
.padding(.top, 33) }
}
} else {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 24) {
HStack(spacing: 6.7) {
Image("ic_logo_circle_gray")
Text("Login")
.font(.custom(Font.medium.rawValue, size: 32))
.foregroundColor(.gray90)
}
.padding(20)
.frame(maxWidth: .infinity)
.background(Color.bg)
.cornerRadius(6.7)
.contentShape(Rectangle())
.padding(.horizontal, 13.3)
.padding(.top, 24)
.onTapGesture {
AppState.shared
.setAppStep(step: .login)
}
HStack(spacing: 0) {
HStack(spacing: 6.7) {
Text("0")
.font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(.grayee)
Image("ic_can")
.resizable()
.frame(width: 20, height: 20)
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.onTapGesture {
AppState.shared
.setAppStep(step: .login)
}
Spacer()
HStack(spacing: 7) {
Image("ic_coin_w")
.resizable()
.frame(width: 26.7, height: 26.7)
Text("충전")
.font(.custom(Font.bold.rawValue, size: 12))
.foregroundColor(Color(hex: "b38fff"))
}
.padding(.horizontal, 11.3)
.padding(.vertical, 7)
.overlay(
RoundedRectangle(cornerRadius: CGFloat(16.7))
.stroke(lineWidth: 1)
.foregroundColor(Color(hex: "b38fff"))
)
.cornerRadius(16.7)
.onTapGesture {
AppState.shared
.setAppStep(step: .login)
}
}
.frame(maxWidth: .infinity)
.padding(.horizontal, 13.3)
.padding(.vertical, 10)
.background(Color.gray22)
.cornerRadius(6.7)
.padding(.horizontal, 13.3) .padding(.horizontal, 13.3)
ReservationStatusView(data: data)
.padding(.top, 40)
ServiceCenterButtonView() ServiceCenterButtonView()
.padding(.top, 40) .padding(.horizontal, 13.3)
if !data.isAuth {
AuthButtonView()
.padding(.top, 40)
.onTapGesture {
viewModel.isShowAuthView = true
}
}
if let url = URL(string: "https://blog.naver.com/sodalive_official"), if let url = URL(string: "https://blog.naver.com/sodalive_official"),
UIApplication.shared.canOpenURL(url) { UIApplication.shared.canOpenURL(url) {
@ -198,7 +294,6 @@ struct MyPageView: View {
width: screenSize().width, width: screenSize().width,
height: (200 * screenSize().width) / 1080 height: (200 * screenSize().width) / 1080
) )
.padding(.vertical, 40)
.onTapGesture { .onTapGesture {
UIApplication.shared.open(url) UIApplication.shared.open(url)
} }
@ -210,7 +305,9 @@ struct MyPageView: View {
.frame(width: geo.size.width, height: geo.size.height) .frame(width: geo.size.width, height: geo.size.height)
} }
.onAppear { .onAppear {
viewModel.getMypage() if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
viewModel.getMypage()
}
} }
} }
} }

View File

@ -43,6 +43,6 @@ extension FaqApi: TargetType {
} }
var headers: [String : String]? { var headers: [String : String]? {
return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"] return nil
} }
} }

View File

@ -25,8 +25,9 @@ struct ServiceCenterButtonView: View {
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
} }
.padding(.horizontal, 13.3) .padding(.horizontal, 13.3)
.frame(width: screenSize().width - 26.7, height: 66.7) .frame(height: 66.7)
.background(Color(hex: "13181b")) .frame(maxWidth: .infinity)
.background(Color.bg)
.cornerRadius(6.7) .cornerRadius(6.7)
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep(step: .serviceCenter) AppState.shared.setAppStep(step: .serviceCenter)

View File

@ -25,7 +25,7 @@ struct ServiceCenterView: View {
Text("고객센터") Text("고객센터")
.font(.custom(Font.bold.rawValue, size: 20)) .font(.custom(Font.bold.rawValue, size: 20))
.foregroundColor(Color(hex: "eeeeee")) .foregroundColor(.grayee)
HStack(spacing: 13.3) { HStack(spacing: 13.3) {
Image("ic_service_center_kakao") Image("ic_service_center_kakao")
@ -48,13 +48,14 @@ struct ServiceCenterView: View {
Rectangle() Rectangle()
.frame(width: screenSize().width, height: 6.7) .frame(width: screenSize().width, height: 6.7)
.foregroundColor(Color(hex: "232323")) .foregroundColor(.gray23)
.padding(.vertical, 20) .padding(.vertical, 20)
Text("자주 묻는 질문") Text("자주 묻는 질문")
.font(.custom(Font.bold.rawValue, size: 18.3)) .font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(Color(hex: "eeeeee")) .foregroundColor(.grayee)
.frame(width: screenSize().width - 26.7, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 13.3)
ServiceCenterCategoryView( ServiceCenterCategoryView(
categories: viewModel.categories, categories: viewModel.categories,

View File

@ -20,165 +20,168 @@ struct SettingsView: View {
GeometryReader { geo in GeometryReader { geo in
VStack(spacing: 0) { VStack(spacing: 0) {
DetailNavigationBar(title: "설정") DetailNavigationBar(title: "설정")
VStack(spacing: 0) {
HStack(spacing: 0) {
Text("공지사항")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 3.3)
.frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .notices)
}
Rectangle()
.frame(width: cardWidth - 26.7, height: 0.3)
.foregroundColor(Color.gray90)
HStack(spacing: 0) {
Text("이벤트")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 3.3)
.frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .events)
}
Rectangle()
.frame(width: cardWidth - 26.7, height: 0.3)
.foregroundColor(Color.gray90)
HStack(spacing: 0) {
Text("알림 설정")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 3.3)
.frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .notificationSettings)
}
}
.padding(.horizontal, 13.3)
.frame(width: cardWidth)
.background(Color.gray22)
.cornerRadius(6.7)
.padding(.top, 26.7)
if UserDefaults.bool(forKey: .auth) { ScrollView(.vertical, showsIndicators: false) {
HStack(spacing: 0) { VStack(spacing: 0) {
Text("콘텐츠 보기 설정") VStack(spacing: 0) {
.font(.custom(Font.bold.rawValue, size: 14.7)) HStack(spacing: 0) {
.foregroundColor(Color.grayee) Text("공지사항")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 3.3)
.frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .notices)
}
Rectangle()
.frame(width: cardWidth - 26.7, height: 0.3)
.foregroundColor(Color.gray90)
HStack(spacing: 0) {
Text("이벤트")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 3.3)
.frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .events)
}
Rectangle()
.frame(width: cardWidth - 26.7, height: 0.3)
.foregroundColor(Color.gray90)
HStack(spacing: 0) {
Text("알림 설정")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 3.3)
.frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .notificationSettings)
}
}
.padding(.horizontal, 13.3)
.frame(width: cardWidth)
.background(Color.gray22)
.cornerRadius(6.7)
.padding(.top, 26.7)
Spacer() if UserDefaults.bool(forKey: .auth) {
HStack(spacing: 0) {
Text("콘텐츠 보기 설정")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 16.7)
.frame(width: cardWidth, height: 50)
.background(Color.gray22)
.cornerRadius(6.7)
.contentShape(Rectangle())
.padding(.top, 13.3)
.onTapGesture {
AppState.shared.setAppStep(step: .contentViewSettings)
}
}
Image("ic_forward") VStack(spacing: 0) {
.resizable() HStack(spacing: 0) {
.frame(width: 20, height: 20) Text("이용약관")
} .font(.custom(Font.bold.rawValue, size: 14.7))
.padding(.horizontal, 16.7) .foregroundColor(Color.grayee)
.frame(width: cardWidth, height: 50)
.background(Color.gray22) Spacer()
.cornerRadius(6.7)
.contentShape(Rectangle()) Image("ic_forward")
.padding(.top, 13.3) .resizable()
.onTapGesture { .frame(width: 20, height: 20)
AppState.shared.setAppStep(step: .contentViewSettings) }
} .padding(.horizontal, 3.3)
} .frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
VStack(spacing: 0) { .onTapGesture {
HStack(spacing: 0) { AppState.shared.setAppStep(step: .terms)
Text("이용약관") }
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee) Rectangle()
.frame(width: cardWidth - 26.7, height: 0.3)
.foregroundColor(Color.gray90)
HStack(spacing: 0) {
Text("개인정보처리방침")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 3.3)
.frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .privacy)
}
}
.padding(.horizontal, 13.3)
.frame(width: cardWidth)
.background(Color.gray22)
.cornerRadius(6.7)
.padding(.top, 13.3)
Spacer() HStack(spacing: 0) {
Text("앱 버전 정보")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
Text("Ver \(version!)")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
}
.padding(.horizontal, 16.7)
.frame(width: cardWidth, height: 50)
.background(Color.gray22)
.cornerRadius(6.7)
.padding(.top, 13.3)
Image("ic_forward") Text("""
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 3.3)
.frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .terms)
}
Rectangle()
.frame(width: cardWidth - 26.7, height: 0.3)
.foregroundColor(Color.gray90)
HStack(spacing: 0) {
Text("개인정보처리방침")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
Image("ic_forward")
.resizable()
.frame(width: 20, height: 20)
}
.padding(.horizontal, 3.3)
.frame(width: cardWidth - 26.7, height: 50)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .privacy)
}
}
.padding(.horizontal, 13.3)
.frame(width: cardWidth)
.background(Color.gray22)
.cornerRadius(6.7)
.padding(.top, 13.3)
HStack(spacing: 0) {
Text("앱 버전 정보")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
Spacer()
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
Text("Ver \(version!)")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
}
.padding(.horizontal, 16.7)
.frame(width: cardWidth, height: 50)
.background(Color.gray22)
.cornerRadius(6.7)
.padding(.top, 13.3)
Text("""
- : - :
- : - :
@ -193,39 +196,40 @@ struct SettingsView: View {
- : sodalive.official@gmail.com - : sodalive.official@gmail.com
""") """)
.font(.custom(Font.medium.rawValue, size: 11)) .font(.custom(Font.medium.rawValue, size: 11))
.foregroundColor(Color.gray77) .foregroundColor(Color.gray77)
.frame(width: cardWidth, alignment: .leading) .frame(width: cardWidth, alignment: .leading)
.padding(.top, 13.3) .padding(.top, 13.3)
Spacer() Text("로그아웃")
.font(.custom(Font.bold.rawValue, size: 14.7))
Text("로그아웃") .foregroundColor(Color.grayee)
.font(.custom(Font.bold.rawValue, size: 14.7)) .frame(width: cardWidth, height: 50)
.foregroundColor(Color.grayee) .background(Color.gray22)
.frame(width: cardWidth, height: 50) .cornerRadius(6.7)
.background(Color.gray22) .onTapGesture {
.cornerRadius(6.7) isShowLogoutDialog = true
.onTapGesture { }
isShowLogoutDialog = true .padding(.top, 46.7)
}
Text("모든 기기에서 로그아웃")
Text("모든 기기에서 로그아웃") .font(.custom(Font.medium.rawValue, size: 14.7))
.font(.custom(Font.medium.rawValue, size: 14.7)) .foregroundColor(Color.gray77)
.foregroundColor(Color.gray77) .padding(.top, 13.3)
.padding(.top, 13.3) .onTapGesture {
.onTapGesture { isShowLogoutAllDeviceDialog = true
isShowLogoutAllDeviceDialog = true }
}
Text("회원탈퇴")
Text("회원탈퇴") .font(.custom(Font.medium.rawValue, size: 14.7))
.font(.custom(Font.medium.rawValue, size: 14.7)) .foregroundColor(Color.gray77)
.foregroundColor(Color.gray77) .underline()
.underline() .padding(.vertical, 26.7)
.padding(.vertical, 26.7) .onTapGesture {
.onTapGesture { AppState.shared.setAppStep(step: .signOut)
AppState.shared.setAppStep(step: .signOut) }
} }
}
} }
} }
@ -239,8 +243,9 @@ struct SettingsView: View {
ContentPlayerPlayManager.shared.resetPlayer() ContentPlayerPlayManager.shared.resetPlayer()
viewModel.logout { viewModel.logout {
self.isShowLogoutDialog = false self.isShowLogoutDialog = false
AppState.shared.setAppStep(step: .main)
UserDefaults.reset() UserDefaults.reset()
AppState.shared.isChangeAdultContentVisible = true
AppState.shared.setAppStep(step: .splash)
} }
}, },
cancelButtonTitle: "취소", cancelButtonTitle: "취소",
@ -260,8 +265,9 @@ struct SettingsView: View {
ContentPlayerPlayManager.shared.resetPlayer() ContentPlayerPlayManager.shared.resetPlayer()
viewModel.logoutAllDevice { viewModel.logoutAllDevice {
self.isShowLogoutAllDeviceDialog = false self.isShowLogoutAllDeviceDialog = false
AppState.shared.setAppStep(step: .main)
UserDefaults.reset() UserDefaults.reset()
AppState.shared.isChangeAdultContentVisible = true
AppState.shared.setAppStep(step: .splash)
} }
}, },
cancelButtonTitle: "취소", cancelButtonTitle: "취소",

View File

@ -56,6 +56,7 @@ final class SignOutViewModel: ObservableObject {
if decoded.success { if decoded.success {
UserDefaults.reset() UserDefaults.reset()
AppState.shared.isChangeAdultContentVisible = true
AppState.shared.setAppStep(step: .splash) AppState.shared.setAppStep(step: .splash)
} else { } else {
if let message = decoded.message { if let message = decoded.message {

View File

@ -11,6 +11,8 @@ import Kingfisher
struct SeriesListBigItemView: View { struct SeriesListBigItemView: View {
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
let item: SeriesListItem let item: SeriesListItem
let isVisibleCreator: Bool let isVisibleCreator: Bool
@ -26,8 +28,13 @@ struct SeriesListBigItemView: View {
.cornerRadius(5) .cornerRadius(5)
.clipped() .clipped()
.onTapGesture { .onTapGesture {
AppState.shared if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.setAppStep(step: .seriesDetail(seriesId: item.seriesId)) AppState.shared
.setAppStep(step: .seriesDetail(seriesId: item.seriesId))
} else {
AppState.shared
.setAppStep(step: .login)
}
} }
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
@ -80,8 +87,13 @@ struct SeriesListBigItemView: View {
.lineLimit(1) .lineLimit(1)
} }
.onTapGesture { .onTapGesture {
AppState.shared if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
.setAppStep(step: .creatorDetail(userId: item.creator.creatorId)) AppState.shared
.setAppStep(step: .creatorDetail(userId: item.creator.creatorId))
} else {
AppState.shared
.setAppStep(step: .login)
}
} }
} }

View File

@ -17,7 +17,7 @@ struct LoginView: View {
var body: some View { var body: some View {
BaseView(isLoading: $viewModel.isLoading) { BaseView(isLoading: $viewModel.isLoading) {
VStack(spacing: 0) { VStack(spacing: 0) {
HomeNavigationBar(title: "로그인") {} DetailNavigationBar(title: "로그인")
Spacer() Spacer()

View File

@ -55,6 +55,8 @@ final class LoginViewModel: ObservableObject {
UserDefaults.set(data.userId, forKey: .userId) UserDefaults.set(data.userId, forKey: .userId)
UserDefaults.set(data.email, forKey: .email) UserDefaults.set(data.email, forKey: .email)
UserDefaults.set(data.token, forKey: .token) UserDefaults.set(data.token, forKey: .token)
AppState.shared.isChangeAdultContentVisible = true
AppState.shared.setAppStep(step: .splash)
} else { } else {
if let message = decoded.message { if let message = decoded.message {
self.errorMessage = message self.errorMessage = message

View File

@ -58,7 +58,8 @@ final class SignUpViewModel: ObservableObject {
UserDefaults.set(data.userId, forKey: .userId) UserDefaults.set(data.userId, forKey: .userId)
UserDefaults.set(data.email, forKey: .email) UserDefaults.set(data.email, forKey: .email)
UserDefaults.set(data.token, forKey: .token) UserDefaults.set(data.token, forKey: .token)
AppState.shared.back() AppState.shared.isChangeAdultContentVisible = true
AppState.shared.setAppStep(step: .splash)
} else { } else {
if let message = decoded.message { if let message = decoded.message {
self.errorMessage = message self.errorMessage = message