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
6 changes: 6 additions & 0 deletions GAMSS/Resources/Assets.xcassets/Onboarding/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "thought_notes.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "thought_notes@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "thought_notes@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "thought_notes_discard.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "thought_notes_discard@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "thought_notes_discard@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions GAMSS/Sources/Presentation/Onboarding/OnboardingPage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// OnboardingPage.swift
// GAMSS
//
// Created by LEEGEONJUN on 8/14/26.
//

import SwiftUI

enum OnboardingPage: Int, CaseIterable, Identifiable {
case talk
case emotions
case empty

var id: Int { rawValue }

var title: String {
switch self {
case .talk:
"누구한테 말 못할 말이 있다면\n이야기해보세요"
case .emotions:
"6가지의 감정 친구들과\n이야기를 이어가보세요"
case .empty:
"안좋았던 감정을\n비워보세요"
}
}

var buttonTitle: String {
switch self {
case .talk, .emotions:
"다음"
case .empty:
"시작하기"
}
}

var buttonColor: Color {
switch self {
case .talk, .emotions:
.colorGray950
case .empty:
.colorApricot
}
}

var showsBackButton: Bool {
self != .talk
}

var showsSkipButton: Bool {
self != .empty
}

var backgroundImage: Image {
switch self {
case .talk:
return Image(.thoughtNotes)
case .emotions:
return Image(.emotions)
case .empty:
return Image(.thoughtNotesDiscard)
}
}
}
122 changes: 122 additions & 0 deletions GAMSS/Sources/Presentation/Onboarding/OnboardingView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//
// OnboardingView.swift
// GAMSS
//
// Created by LEEGEONJUN on 8/14/26.
//

import SwiftUI

struct OnboardingView: View {
var onFinish: () -> Void

@State private var currentPage: OnboardingPage = .talk

var body: some View {
VStack(spacing: 0) {
header
.padding(.horizontal, 18)
.padding(.vertical, Spacing.spacing400)

Spacer()

pageContent(currentPage)
.id(currentPage)
.padding(.horizontal, 31)

Spacer()

pageIndicator
.padding(.bottom, Spacing.spacing400)

actionButton
.padding(.horizontal, 18)
.padding(.bottom, Spacing.spacing400)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.colorWhite)
.animation(.easeInOut(duration: 0.25), value: currentPage)
}

private var header: some View {
HStack {
if currentPage.showsBackButton {
Button {
goToPreviousPage()
} label: {
Image(systemName: "chevron.left")
.foregroundStyle(Color.colorGray900)
}
}

Spacer()

if currentPage.showsSkipButton {
Button {
onFinish()
} label: {
Text("건너뛰기")
.typography(.body5Medium)
.foregroundStyle(Color.colorGray500)
}
}
}
.frame(height: 24)
}

private func pageContent(_ page: OnboardingPage) -> some View {
VStack(spacing: Spacing.spacing600) {
page.backgroundImage
.resizable()
.scaledToFit()

Text(page.title)
.typography(.subtitle2)
.foregroundStyle(Color.colorGray900)
.multilineTextAlignment(.center)
}
}

private var pageIndicator: some View {
HStack(spacing: Spacing.spacing100) {
ForEach(OnboardingPage.allCases) { page in
Circle()
.fill(page == currentPage ? Color.colorGray700 : Color.colorGray200)
.frame(width: 8, height: 8)
}
}
}

private var actionButton: some View {
Button {
goToNextPage()
} label: {
Text(currentPage.buttonTitle)
.typography(.subtitle3)
.foregroundStyle(Color.colorWhite)
.frame(maxWidth: .infinity)
.frame(height: 54)
}
.background(currentPage.buttonColor)
.clipShape(RoundedRectangle(cornerRadius: Radius.radius200))
}

private func goToNextPage() {
guard let next = OnboardingPage(rawValue: currentPage.rawValue + 1) else {
onFinish()
return
}
currentPage = next
}

private func goToPreviousPage() {
guard let previous = OnboardingPage(rawValue: currentPage.rawValue - 1) else {
return
}
currentPage = previous
}
}

#Preview {
OnboardingView(onFinish: {})
}
9 changes: 8 additions & 1 deletion GAMSS/Sources/Presentation/Root/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ final class LoginSession {

struct RootView: View {
@State private var loginSession = LoginSession()
@AppStorage("hasCompletedOnboarding") private var hasCompletedOnboarding = false

var body: some View {
Group {
Expand All @@ -30,7 +31,13 @@ struct RootView: View {
)
)
case .autoLoginPending, .loggedIn:
MainTabView()
if hasCompletedOnboarding {
MainTabView()
} else {
OnboardingView {
hasCompletedOnboarding = true
}
}
}
}
.environment(loginSession)
Expand Down
Loading