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
125 changes: 0 additions & 125 deletions Projects/App/Sources/Components/InteractiveAlbumCarousel.swift

This file was deleted.

82 changes: 0 additions & 82 deletions Projects/App/Sources/Components/SocialLoginButton.swift

This file was deleted.

13 changes: 13 additions & 0 deletions Projects/App/Sources/Features/App/AppFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ComposableArchitecture
import Foundation

// 앱 루트 — 로그인 화면과 메인 탭을 오간다. 탈퇴 신청 뒤 로그아웃하면 로그인 화면으로 돌아온다.
// 「먼저 둘러볼게요」는 메인 탭을 손님으로 열고, 손님이 로그인을 고르면 로그인 화면으로 돌아온다(DEC-032).
// 로그인은 아직 임시(#104): 버튼을 누르면 성공으로 보고 메인 탭으로 들어간다. 실제 연동은 #14.

@Reducer
Expand All @@ -28,6 +29,18 @@ struct AppFeature {
state = .mainTab(MainTabFeature.State())
return .none

case .login(.delegate(.browseAsGuest)):
// 「먼저 둘러볼게요」(DEC-032) — 로그인 없이 메인 탭을 손님으로 연다
var mainTab = MainTabFeature.State()
mainTab.isGuest = true
state = .mainTab(mainTab)
return .none

case .mainTab(.delegate(.loginRequested)):
// 손님이 투표 · 추천 등에서 「로그인하기」를 누르면 로그인 화면으로
state = .login(LoginFeature.State())
return .none

case .mainTab(.delegate(.signedOut)):
// 탈퇴 신청 뒤 로그아웃(UI-7c) — 로그인 화면으로 돌아간다
state = .login(LoginFeature.State())
Expand Down
106 changes: 106 additions & 0 deletions Projects/App/Sources/Features/Login/Components/LoginButtons.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import SwiftUI

// UI-16d 버튼 묶음 — Apple(흰 버튼) · Google(반투명) · 먼저 둘러볼게요(글자 버튼) · 약관 줄
// Apple 버튼만 흰색: Apple 로그인 버튼은 가이드라인상 흰색 · 검은색만 쓸 수 있어 「흰 버튼 금지」의 유일한 예외(16 공통 규칙)

struct LoginButtons: View {
let onApple: () -> Void
let onGoogle: () -> Void
let onBrowse: () -> Void

enum Metric {
static let height: CGFloat = 54
static let browseHeight: CGFloat = 36
}

var body: some View {
VStack(spacing: 12) {
Button(action: self.onApple) {
HStack(spacing: 8) {
Image(systemName: "apple.logo")
.font(.system(size: 19, weight: .medium))
.offset(y: -1.5)
Text("Apple로 계속하기")
.wepliFont(.button)
}
.foregroundStyle(.black)
.frame(maxWidth: .infinity)
.frame(height: Metric.height)
.background(.white, in: Capsule())
.contentShape(Capsule())
}
.buttonStyle(.plain)

Button(action: self.onGoogle) {
HStack(spacing: 10) {
Asset.Assets.google.swiftUIImage
.resizable()
.scaledToFit()
.frame(width: 18, height: 18)
Text("Google로 계속하기")
.wepliFont(.button)
.foregroundStyle(WePLiColor.textPrimary)
}
.frame(maxWidth: .infinity)
.frame(height: Metric.height)
.background(.ultraThinMaterial, in: Capsule())
.background(WePLiColor.secondaryButtonFill, in: Capsule())
.contentShape(Capsule())
}
.buttonStyle(.plain)

Button(action: self.onBrowse) {
Text("먼저 둘러볼게요")
.wepliFont(.body, weight: .medium)
.foregroundStyle(WePLiColor.accent)
.frame(maxWidth: .infinity)
.frame(height: Metric.browseHeight)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
}
}
}

/// 「처음 로그인하면 계정이 생성되며, 서비스 이용약관과 개인정보처리방침에 동의하게 됩니다.」
struct LoginTerms: View {
let onTerms: () -> Void
let onPrivacy: () -> Void

private static let textColor = Color(rgbHex: 0x767676)

var body: some View {
// 한 문장 안의 두 링크 — 글자 단위로 누를 수 없어 링크 URL을 가로채 액션으로 바꾼다
Text(Self.attributed)
.font(.system(size: 12))
.lineSpacing(2)
.multilineTextAlignment(.center)
.foregroundStyle(Self.textColor)
.tint(WePLiColor.textTertiary)
.environment(\.openURL, OpenURLAction { url in
if url.host == "privacy" {
self.onPrivacy()
} else {
self.onTerms()
}
return .handled
})
}

private static var attributed: AttributedString {
var text = AttributedString("처음 로그인하면 계정이 생성되며, ")
text += Self.link("서비스 이용약관", host: "terms")
text += AttributedString("과 ")
text += Self.link("개인정보처리방침", host: "privacy")
text += AttributedString("에 동의하게 됩니다.")
return text
}

private static func link(_ title: String, host: String) -> AttributedString {
var part = AttributedString(title)
part.link = URL(string: "wepli://\(host)")
part.foregroundColor = WePLiColor.textTertiary
part.underlineStyle = .single
return part
}
}
Loading
Loading