272 lines
11 KiB
Swift
272 lines
11 KiB
Swift
//
|
|
// OriginalWorkDetailView.swift
|
|
// SodaLive
|
|
//
|
|
// Created by klaus on 9/15/25.
|
|
//
|
|
|
|
import SwiftUI
|
|
import Kingfisher
|
|
|
|
struct OriginalWorkDetailView: View {
|
|
|
|
@StateObject var viewModel = OriginalWorkDetailViewModel()
|
|
|
|
let originalId: Int
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
BaseView(isLoading: $viewModel.isLoading) {
|
|
ZStack(alignment: .top) {
|
|
if let imageUrl = viewModel.response?.imageUrl {
|
|
KFImage(URL(string: imageUrl))
|
|
.cancelOnDisappear(true)
|
|
.resizable()
|
|
.scaledToFill()
|
|
.frame(width: screenSize().width, height: (168 * 288 / 306) + 56)
|
|
.clipped()
|
|
.blur(radius: 25)
|
|
}
|
|
|
|
Color.black.opacity(0.5).ignoresSafeArea()
|
|
|
|
VStack(spacing: 0) {
|
|
HStack(spacing: 0) {
|
|
Image("ic_back")
|
|
.resizable()
|
|
.frame(width: 24, height: 24)
|
|
.onTapGesture {
|
|
AppState.shared.back()
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
.padding(.horizontal, 24)
|
|
.frame(height: 56)
|
|
|
|
if let response = viewModel.response {
|
|
ScrollView(.vertical, showsIndicators: false) {
|
|
VStack(spacing: 0) {
|
|
OriginalWorkDetailHeaderView(item: response)
|
|
.padding(.horizontal, 24)
|
|
.padding(.bottom, 24)
|
|
|
|
HStack(spacing: 0) {
|
|
SeriesDetailTabView(
|
|
title: "캐릭터",
|
|
width: screenSize().width / 2,
|
|
isSelected: viewModel.currentTab == .character
|
|
) {
|
|
if viewModel.currentTab != .character {
|
|
viewModel.currentTab = .character
|
|
}
|
|
}
|
|
|
|
SeriesDetailTabView(
|
|
title: "작품정보",
|
|
width: screenSize().width / 2,
|
|
isSelected: viewModel.currentTab == .info
|
|
) {
|
|
if viewModel.currentTab != .info {
|
|
viewModel.currentTab = .info
|
|
}
|
|
}
|
|
}
|
|
.background(Color.black)
|
|
|
|
Rectangle()
|
|
.foregroundColor(Color.gray90.opacity(0.5))
|
|
.frame(height: 1)
|
|
.frame(maxWidth: .infinity)
|
|
|
|
switch(viewModel.currentTab) {
|
|
case .info:
|
|
OriginalWorkInfoView(response: response)
|
|
default:
|
|
OriginalWorkCharacterView(characters: viewModel.characters)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.onAppear {
|
|
if viewModel.response == nil {
|
|
viewModel.originalId = originalId
|
|
}
|
|
}
|
|
.navigationDestination(for: Int.self) { characterId in
|
|
CharacterDetailView(characterId: characterId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct OriginalWorkCharacterView: View {
|
|
|
|
private let horizontalPadding: CGFloat = 12
|
|
private let gridSpacing: CGFloat = 12
|
|
|
|
let characters: [Character]
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
let width = (screenSize().width - (horizontalPadding * 2) - gridSpacing) / 2
|
|
|
|
LazyVGrid(
|
|
columns: Array(
|
|
repeating: GridItem(
|
|
.flexible(),
|
|
spacing: gridSpacing,
|
|
alignment: .topLeading
|
|
),
|
|
count: 2
|
|
),
|
|
alignment: .leading,
|
|
spacing: gridSpacing
|
|
) {
|
|
ForEach(characters.indices, id: \.self) { idx in
|
|
let item = characters[idx]
|
|
|
|
NavigationLink(value: item.characterId) {
|
|
CharacterItemView(
|
|
character: item,
|
|
size: width,
|
|
rank: 0,
|
|
isShowRank: false
|
|
)
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, horizontalPadding)
|
|
}
|
|
.padding(.top, 24)
|
|
.background(Color.black)
|
|
}
|
|
}
|
|
|
|
struct OriginalWorkInfoView: View {
|
|
|
|
let response: OriginalWorkDetailResponse
|
|
|
|
@State private var isExpandDesc = false
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
VStack(spacing: 16) {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("작품 소개")
|
|
.font(.custom(Font.preBold.rawValue, size: 16))
|
|
.foregroundColor(.white)
|
|
|
|
Text(response.description)
|
|
.font(.custom(Font.preRegular.rawValue, size: 14))
|
|
.foregroundColor(Color(hex: "B0BEC5"))
|
|
.lineLimit(isExpandDesc ? Int.max : 3)
|
|
.truncationMode(.tail)
|
|
.onTapGesture {
|
|
isExpandDesc.toggle()
|
|
}
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(Color(hex: "263238"))
|
|
.cornerRadius(16)
|
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("원작 보러 가기")
|
|
.font(.custom(Font.preBold.rawValue, size: 16))
|
|
.foregroundColor(Color(hex: "B0BEC5"))
|
|
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 8) {
|
|
ForEach(0..<response.originalLinks.count, id: \.self) {
|
|
let link = response.originalLinks[$0]
|
|
|
|
Text(link)
|
|
.font(.custom(Font.preRegular.rawValue, size: 14))
|
|
.foregroundColor(.white)
|
|
.onTapGesture {
|
|
if let url = URL(string: link) {
|
|
UIApplication.shared.open(url)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(Color(hex: "263238"))
|
|
.cornerRadius(16)
|
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("상세 정보")
|
|
.font(.custom(Font.preBold.rawValue, size: 16))
|
|
.foregroundColor(.white)
|
|
|
|
HStack(spacing: 16) {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
if let _ = response.writer {
|
|
Text("작가")
|
|
.font(.custom(Font.preRegular.rawValue, size: 14))
|
|
.foregroundColor(Color(hex: "B0BEC5"))
|
|
}
|
|
|
|
if let _ = response.studio {
|
|
Text("제작사")
|
|
.font(.custom(Font.preRegular.rawValue, size: 14))
|
|
.foregroundColor(Color(hex: "B0BEC5"))
|
|
}
|
|
|
|
if let _ = response.originalWork {
|
|
Text("원작")
|
|
.font(.custom(Font.preRegular.rawValue, size: 14))
|
|
.foregroundColor(Color(hex: "B0BEC5"))
|
|
}
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
if let writer = response.writer {
|
|
Text(writer)
|
|
.font(.custom(Font.preRegular.rawValue, size: 14))
|
|
.foregroundColor(.white)
|
|
}
|
|
|
|
if let studio = response.studio {
|
|
Text(studio)
|
|
.font(.custom(Font.preRegular.rawValue, size: 14))
|
|
.foregroundColor(.white)
|
|
}
|
|
|
|
if let originalWork = response.originalWork {
|
|
Text(originalWork)
|
|
.font(.custom(Font.preRegular.rawValue, size: 14))
|
|
.foregroundColor(.white)
|
|
.underline(response.originalLink != nil ? true : false)
|
|
.onTapGesture {
|
|
if let link = response.originalLink, let url = URL(string: link) {
|
|
UIApplication.shared.open(url)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(Color(hex: "263238"))
|
|
.cornerRadius(16)
|
|
}
|
|
}
|
|
.padding(24)
|
|
.frame(maxWidth: .infinity)
|
|
.background(Color.black)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
OriginalWorkDetailView(originalId: 0)
|
|
}
|