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
10 changes: 9 additions & 1 deletion Mark-In/Sources/Feature/Main/MainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import DesignSystem
struct MainView: View {
@State private var viewModel = MainViewModel()
@State private var searchText: String = ""

@State private var isPresentedMyPage: Bool = false

var body: some View {
ZStack {
Expand Down Expand Up @@ -93,12 +95,18 @@ struct MainView: View {

Spacer()
Button {
// TODO: 구현 예정
isPresentedMyPage = true
} label: {
Image(systemName: "person.circle.fill")
.resizable()
.frame(width: 22, height: 22)
}
.popover(
isPresented: $isPresentedMyPage,
arrowEdge: .bottom
) {
MyPageView()
}
}
}

Expand Down
81 changes: 81 additions & 0 deletions Mark-In/Sources/Feature/MyPage/MyPageView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// MyPageView.swift
// Mark-In
//
// Created by 이정동 on 5/13/25.
//

import SwiftUI

import DesignSystem

private enum ViewConstants {
static let minContentWidth: CGFloat = 164
}

struct MyPageView: View {
@State private var viewModel = MyPageViewModel()
@State private var contentWidth = ViewConstants.minContentWidth

var body: some View {
VStack(spacing: 30) {
headerTitle
.padding(.top, 16)

footerButtons
.padding(.bottom, 12)
}
.frame(width: contentWidth)
}

private var headerTitle: some View {
VStack(spacing: 2) {
Text("User Name")
.font(.pretendard(size: 14, weight: .semiBold))
.foregroundStyle(.markBlack)

Text("asdfasdfasdf012345@gmail.com")
.font(.pretendard(size: 10, weight: .regular))
.tint(.markBlack)
.fixedSize()
.padding(.horizontal, 8)
.background(
GeometryReader { geo in
Color.clear
.onAppear {
let width = geo.size.width
contentWidth = max(width, ViewConstants.minContentWidth)
}
}
)
}
}

private var footerButtons: some View {
VStack(spacing: 8) {
Button {
viewModel.send(.logoutButtonTapped)
} label: {
Text("로그아웃")
.font(.pretendard(size: 12, weight: .regular))
.foregroundStyle(.markBlack)
}

Divider()
.padding(.horizontal, 8)

Button {
viewModel.send(.withdrawalButtonTapped)
} label: {
Text("회원 탈퇴")
.font(.pretendard(size: 12, weight: .regular))
.foregroundStyle(.markRed)
}
}
.buttonStyle(.plain)
}
}

#Preview {
MyPageView()
}
115 changes: 115 additions & 0 deletions Mark-In/Sources/Feature/MyPage/MyPageViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// MyPageViewModel.swift
// Mark-In
//
// Created by 이정동 on 5/13/25.
//

import Foundation

import FirebaseAuth
import GoogleSignIn

@Observable
final class MyPageViewModel: Reducer {
struct State {
var userState: AuthUserState = .init()
}

enum Action {
case logoutButtonTapped
case withdrawalButtonTapped

case didSuccessLogout
case didFailLogout

case didSuccessWithdrawal
case didFailWithdrawal
}

private let authUserManager: AuthUserManager

private(set) var state: State = .init()

init() {
self.authUserManager = DIContainer.shared.resolve()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DIContainer를 shared로 사용하는건가요?

}

func send(_ action: Action) {
let effect = reduce(state: &state, action: action)
handleEffect(effect)
}

func reduce(state: inout State, action: Action) -> Effect<Action> {
switch action {
case .logoutButtonTapped:
return .run {
// TODO: 이후 Auth 모듈로 분리하면서 코드 리팩토링 예정
do {
try Auth.auth().signOut()

if GIDSignIn.sharedInstance.currentUser != nil {
GIDSignIn.sharedInstance.signOut()
}
return .didSuccessLogout
} catch {
return .didFailLogout
}
}

case .withdrawalButtonTapped:
return .run {
// TODO: 이후 Auth 모듈로 분리하면서 코드 리팩토링 예정
do {
// TODO: 재인증 과정 필요
// try await Auth.auth().currentUser?.reauthenticate(with: credential)
try await Auth.auth().currentUser?.delete()

if GIDSignIn.sharedInstance.currentUser != nil {
try await GIDSignIn.sharedInstance.disconnect()
GIDSignIn.sharedInstance.signOut()
}

return .didSuccessWithdrawal
} catch {
return .didFailWithdrawal
}
}

case .didSuccessLogout:
authUserManager.clear()
return .none

// TODO: 로그아웃 실패에 대한 처리 필요
case .didFailLogout:
return .none

case .didSuccessWithdrawal:
authUserManager.clear()
return .none

// TODO: 회원탈퇴 실패에 대한 처리 필요
case .didFailWithdrawal:
return .none
}
}

private func handleEffect(_ effect: Effect<Action>) {
switch effect {
case .none:
break
case .run(let action):
Task.detached { [weak self] in
let newAction = await action()
await self?.send(newAction)
}
}
}
}

extension MyPageViewModel {
struct AuthUserState {
var name: String = ""
var email: String = ""
}
}
Loading