Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Keychy/Keychy/Core/Keyring/Scene/KeyringScale.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ enum KeyringScale {
"ClearSketch": CGSize(width: 210, height: 210),
"PixelKeyring": CGSize(width: 277, height: 257),
"SpeechBubble": CGSize(width: 360, height: 249),
"WishHorse26": CGSize(width: 269, height: 269),
"WishHorse26": CGSize(width: 269, height: 310),
"DuZzonKu": CGSize(width: 376, height: 376)
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extension CollectionKeyringDetailView {
basicInfo

// 메모 있으면
if let memo = keyring.memo, !memo.isEmpty {
if keyring.selectedTemplate == "WishHorse26" || (keyring.memo != nil && !keyring.memo!.isEmpty) {
memoSection
}

Expand Down Expand Up @@ -112,6 +112,11 @@ extension CollectionKeyringDetailView {
.typography(.notosans13M)
.foregroundColor(.mainOpacity70)

Text("·")
.typography(.notosans13M)
.foregroundColor(.mainOpacity70)
.padding(.horizontal, 2)

if let receivedAt = keyring.receivedAt {
Text(formattedReceiveDate(date: receivedAt))
.typography(.notosans13M)
Expand All @@ -122,15 +127,32 @@ extension CollectionKeyringDetailView {
}

private var basicInfo: some View {
VStack(spacing: 0) {
if keyring.senderId != nil && keyring.receivedAt != nil {
let hasReceiveInfo = keyring.senderId != nil && keyring.receivedAt != nil
let isWishHorse = keyring.selectedTemplate == "WishHorse26"

return VStack(spacing: 2) {
if hasReceiveInfo {
receiveInfo
.padding(.top, 10)
}

// WishHorse 템플릿일 때 배너 이미지 표시
if isWishHorse {
Image(.wishHorseBanner)
.padding(.top, 10)
}

Text(keyring.name)
.typography(.notosans24M)
.padding(.top, (keyring.senderId != nil && keyring.receivedAt != nil) ? 10 : 30)
.padding(.top, {
if isWishHorse {
return 2
} else if hasReceiveInfo {
return 10
} else {
return 20
}
}())

Text(formattedDate(date: keyring.createdAt))
.typography(.suit14M)
Expand All @@ -144,12 +166,35 @@ extension CollectionKeyringDetailView {

private var memoSection: some View {
ZStack {
MemoView(memo: keyring.memo ?? "", sheetDetent: $sheetDetent)
// WishHorse 템플릿일 때 특별 메시지 표시
if keyring.selectedTemplate == "WishHorse26" {
LockedMemoView()
} else {
MemoView(memo: keyring.memo ?? "", sheetDetent: $sheetDetent)
}
}
.padding(.top, 15)

}

// WishHorse 템플릿용 잠긴 메모 뷰
private struct LockedMemoView: View {
var body: some View {
Text("작성된 메모는 2027년 1월 1일에 확인할 수 있어요.")
.typography(.notosans14R)
.foregroundColor(.gray400)
.multilineTextAlignment(.center)
.padding(.horizontal, 16)
.padding(.vertical, 12)
.frame(maxWidth: .infinity, alignment: .center)
.frame(minHeight: 60)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(.gray100, lineWidth: 1)
)
}
}

private struct MemoView: View {
let memo: String
@State private var textHeight: CGFloat = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ struct KeyringEditView: View {
return keyring.isEditable && keyring.authorId == currentUserId
}

// WishHorse 템플릿일 때는 이름과 메모 수정 불가
private var canEditName: Bool {
return canEdit && keyring.selectedTemplate != "WishHorse26"
}

enum Field: Hashable {
case name
case memo
Expand Down Expand Up @@ -239,7 +244,7 @@ extension KeyringEditView {
VStack(spacing: 25) {
nameInputField

if canEdit || !(keyring.memo?.isEmpty ?? true) {
if canEdit || !(keyring.memo?.isEmpty ?? true) || keyring.selectedTemplate == "WishHorse26" {
memoInputField
}
}
Expand All @@ -261,8 +266,13 @@ extension KeyringEditView {
.tint(.main500)
.submitLabel(.done)
.focused($focusedField, equals: .name)
.disabled(!canEdit)
.disabled(!canEditName)
.onChange(of: editedName) { newValue in
// WishHorse 템플릿일 때는 수정 불가
if keyring.selectedTemplate == "WishHorse26" {
return
}

// 글자수 제한만 적용 (특수문자 허용)
var sanitized = newValue

Expand Down Expand Up @@ -301,20 +311,20 @@ extension KeyringEditView {
.buttonStyle(PlainButtonStyle())
.padding(.trailing, 16)
.padding(.leading, 8)
.opacity(canEdit ? 1 : 0)
.opacity(canEditName ? 1 : 0)
}
}
.frame(height: 52)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(canEdit ? .gray50 : .white100)
.fill(canEditName ? .gray50 : .white100)
)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(canEdit ? .clear : .gray100, lineWidth: 1)
.stroke(canEditName ? .clear : .gray100, lineWidth: 1)
)
.onTapGesture {
if canEdit {
if canEditName {
focusedField = .name
}
}
Expand All @@ -334,53 +344,65 @@ extension KeyringEditView {
.typography(.suit16B)

ZStack(alignment: .topLeading) {
// Placeholder
if editedMemo.isEmpty {
Text("메모를 입력해주세요")
.typography(.notosans16R25)
.foregroundColor(.gray300)
.padding(.horizontal, 19)
.padding(.vertical, 18)
.allowsHitTesting(false)
}

if canEdit {
// 편집 가능
TextEditor(text: $editedMemo)
.typography(.notosans16R25)
.foregroundColor(.black100)
.scrollContentBackground(.hidden)
.background(Color.clear)
.padding(.horizontal, 14)
.padding(.vertical, 10)
// WishHorse 템플릿일 때 특별 메시지 표시
if keyring.selectedTemplate == "WishHorse26" {
Text("작성된 메모는 2027년 1월 1일에 확인할 수 있어요.".byCharWrapping)
.typography(.notosans14R)
.foregroundColor(.gray400)
.frame(maxWidth: .infinity, alignment: .center)
.scrollIndicators(.hidden)
.focused($focusedField, equals: .memo)
.tint(.main500)
.padding(.horizontal, 16)
.padding(.vertical, 14)
} else {
// 편집 불가 : 스크롤만 가능
ScrollView {
Text(editedMemo.byCharWrapping)
// 일반 메모 입력 UI
// Placeholder
if editedMemo.isEmpty {
Text("메모를 입력해주세요")
.typography(.notosans16R25)
.foregroundColor(.gray300)
.padding(.horizontal, 19)
.padding(.vertical, 18)
.allowsHitTesting(false)
}

if canEdit {
// 편집 가능
TextEditor(text: $editedMemo)
.typography(.notosans16R25)
.foregroundColor(.black100)
.frame(maxWidth: .infinity, alignment: .leading)
.scrollContentBackground(.hidden)
.background(Color.clear)
.padding(.horizontal, 14)
.padding(.vertical, 10)
.scrollIndicators(.hidden)
.focused($focusedField, equals: .memo)
.tint(.main500)
} else {
// 편집 불가 : 스크롤만 가능
ScrollView {
Text(editedMemo.byCharWrapping)
.typography(.notosans16R25)
.foregroundColor(.black100)
.frame(maxWidth: .infinity, alignment: .leading)

}
.scrollIndicators(.hidden)
.padding(.horizontal, 16)
.padding(.vertical, 14)
}
.scrollIndicators(.hidden)
.padding(.horizontal, 16)
.padding(.vertical, 14)
}
}
.frame(height: 140)
.frame(height: (keyring.selectedTemplate == "WishHorse26") ? 60 : 140)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(canEdit ? .gray50 : .white100)
.fill(keyring.selectedTemplate == "WishHorse26" ? .white100 : (canEdit ? .gray50 : .white100))
)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(canEdit ? .clear : .gray100, lineWidth: 1)
.stroke(keyring.selectedTemplate == "WishHorse26" ? .gray100 : (canEdit ? .clear : .gray100), lineWidth: 1)
)
.onTapGesture {
if canEdit {
if canEdit && keyring.selectedTemplate != "WishHorse26" {
focusedField = .memo
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@ extension KeyringInfoInputView {
var sceneYOffset: CGFloat {
isSheetExpanded ? -120 : 0
}

/// 다음 버튼 활성화 조건
var isNextButtonEnabled: Bool {
// 이름이 비어있거나 욕설이 포함되어 있으면 비활성화
guard !viewModel.nameText.isEmpty && !hasProfanity else {
return false
}

// WishHorse26 템플릿일 때는 메모도 필수
if viewModel.templateId == "WishHorse26" {
return !viewModel.memoText.isEmpty
}

return true
}
}

// MARK: - KeyringScene Section
Expand Down Expand Up @@ -72,12 +87,12 @@ extension KeyringInfoInputView {
} label: {
Text("다음")
.typography(.suit17B)
.foregroundStyle(viewModel.nameText.isEmpty || hasProfanity ? .gray300 : .main500)
.foregroundStyle(isNextButtonEnabled ? .main500 : .gray300)
.padding(5)
}
.buttonStyle(.glassProminent)
.tint(viewModel.nameText.isEmpty || hasProfanity ? .clear : .white100)
.allowsHitTesting(!viewModel.nameText.isEmpty && !isSavingToFirebase && !hasProfanity)
.tint(isNextButtonEnabled ? .white100 : .clear)
.allowsHitTesting(isNextButtonEnabled && !isSavingToFirebase)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,17 @@ extension KeyringInfoInputView {
extension KeyringInfoInputView {
var textMemoView: some View {
VStack(alignment: .leading, spacing: 10) {
Text("메모")
.typography(.suit16B)
.foregroundStyle(.black100)
HStack(spacing: 3) {
Text("메모")
.typography(.suit16B)
.foregroundStyle(.black100)

if viewModel.templateId == "WishHorse26" {
Text("(필수)")
.typography(.suit16B)
.foregroundStyle(.black100)
}
}

ZStack(alignment: .topLeading) {
TextEditor(text: $viewModel.memoText)
Expand All @@ -169,18 +177,34 @@ extension KeyringInfoInputView {
}

if viewModel.memoText.isEmpty {
Text("메모(선택)")
.typography(.notosans15M)
.foregroundColor(.gray300)
.padding(.top, 18)
.padding(.leading, 17)
.allowsHitTesting(false)
HStack(spacing: 2) {
Text("메모")
.typography(.notosans15M)
.foregroundColor(.gray200)

if viewModel.templateId != "WishHorse26" {
Text("(선택)")
.typography(.notosans15M)
.foregroundColor(.gray200)
}
}
.padding(.top, 18)
.padding(.leading, 17)
.allowsHitTesting(false)
}
}
.background(
RoundedRectangle(cornerRadius: 12)
.fill(.gray50)
)

// WishHorse26 전용 안내 문구
if viewModel.templateId == "WishHorse26" {
Text("[2026년을 말해봐 키링]에 작성한 메모는 수정이 불가해요.\n메모는 2027년 1월 1일에 자동으로 활성화되어 확인할 수 있어요.")
.typography(.suit13M)
.foregroundColor(.main500)
.multilineTextAlignment(.leading)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class DuZzonKuVM: KeyringViewModelProtocol {
func bottomViewHeightRatio(for mode: CustomizingMode) -> CGFloat {
switch mode {
case .frame:
return 0.26 // 프레임 모드는 더 낮은 높이
return 0.28 // 프레임 모드는 더 낮은 높이
case .effect:
return 0.3 // 이펙트 모드도 같은 높이
default:
Expand Down
Loading