Skip to content

Commit

Permalink
Merge pull request #148 from TeamHY2/Feature/#145-ProfileEdit
Browse files Browse the repository at this point in the history
  • Loading branch information
Seokki-Kwon authored Jan 15, 2025
2 parents b5449cc + 714b7dc commit 190338a
Show file tree
Hide file tree
Showing 25 changed files with 366 additions and 139 deletions.
Binary file modified .DS_Store
Binary file not shown.
28 changes: 16 additions & 12 deletions HongikYeolgong2.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"location" : "https://github.com/amplitude/Amplitude-Swift",
"state" : {
"branch" : "main",
"revision" : "4097b2a7a0b8fc786c6feddc598f9f379de997af"
"revision" : "98a5f020359d6e4c02f774a57d31b2fc7d062f46"
}
},
{
Expand Down Expand Up @@ -69,17 +69,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
"state" : {
"revision" : "2e02253fd1ce99145bcbf1bb367ccf61bd0ca46b",
"version" : "11.6.0"
"revision" : "0d885d28250fb1196b614bc9455079b75c531f72",
"version" : "11.7.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "4f234bcbdae841d7015258fbbf8e7743a39b8200",
"version" : "11.4.0"
"revision" : "be0881ff728eca210ccb628092af400c086abda3",
"version" : "11.7.0"
}
},
{
Expand Down Expand Up @@ -114,8 +114,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "5cfe5f090c982de9c58605d2a82a4fc77b774fbd",
"version" : "4.1.0"
"revision" : "85b7b231882c3c472b8bda4fb495324d3f19bab6",
"version" : "4.2.0"
}
},
{
Expand Down
15 changes: 15 additions & 0 deletions HongikYeolgong2/Data/DTO/Auth/ProfileEditResponseDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// ProfileEditResponseDTO.swift
// HongikYeolgong2
//
// Created by 권석기 on 1/14/25.
//

import Foundation

struct ProfileEditResponseDTO: Decodable {
let id: Int
let username: String
let nickname: String
let department: String
}
14 changes: 14 additions & 0 deletions HongikYeolgong2/Data/Repositories/Auth/AuthRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ import Foundation
import Combine

final class AuthRepositoryImpl: AuthRepository {
func profileEdit(signUpReqDto: SignUpRequestDTO) -> AnyPublisher<ProfileEditResponseDTO, NetworkError> {
return Future<ProfileEditResponseDTO, NetworkError> { promise in
Task {
do {
let response: BaseResponse<ProfileEditResponseDTO> = try await NetworkService.shared.request(endpoint: UserEndpoint.profileEdit(signUpReqDto: signUpReqDto))
promise(.success(response.data))
} catch let error as NetworkError {
promise(.failure(error))
}
}
}
.eraseToAnyPublisher()
}


/// 소셜로그인
/// - Parameter loginReqDto: 로그인 요청 DTO(이메일, identityToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ final class UserDataMigrationInteractor: UserDataInteractor {
self.appleLoginService = appleLoginService
self.socialLoginRepository = socialLoginRepository
}



/// 기존 유저를 검색하고 ID를 반환합니다.
/// - Parameter idToken: identityToken
/// - Returns: 유저ID
Expand Down Expand Up @@ -130,6 +129,18 @@ final class UserDataMigrationInteractor: UserDataInteractor {
.store(in: cancleBag)
}

func profileEdit(nickname: String, department: Department, loadbleSubject: LoadableSubject<Bool>) {
authRepository
.profileEdit(signUpReqDto: .init(nickname: nickname, department: department.rawValue))
.receive(on: DispatchQueue.main)
.sinkToLoadble(loadbleSubject) { [weak self] newUser in
guard let self = self else { return }
appState[\.userData.nickname] = newUser.nickname
appState[\.userData.department] = newUser.department
}
.store(in: cancleBag)
}

/// 로그아웃
func logout() {
appState[\.userSession] = .unauthenticated
Expand Down
5 changes: 5 additions & 0 deletions HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@ protocol UserDataInteractor: AnyObject {
func validateUserNickname(inputNickname: String, nickname: Binding<Nickname>)
func getUserProfile()
func withdraw(isLoading: LoadableSubject<Bool>)
func profileEdit(nickname: String, department: Department, loadbleSubject: LoadableSubject<Bool>)
}

final class UserDataInteractorImpl: UserDataInteractor {
func validateUserNickname(inputNickname: String, nickname: Binding<Nickname>) {

}

func profileEdit(nickname: String, department: Department, loadbleSubject: LoadableSubject<Bool>) {

}


private let cancleBag = CancelBag()
private let appState: Store<AppState>
Expand Down
1 change: 1 addition & 0 deletions HongikYeolgong2/Domain/Interfaces/AuthRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Combine
protocol AuthRepository {
func signIn(loginReqDto: LoginRequestDTO) -> AnyPublisher<LoginResponseDTO, NetworkError>
func signUp(signUpReqDto: SignUpRequestDTO) -> AnyPublisher<SignUpResponseDTO, NetworkError>
func profileEdit(signUpReqDto: SignUpRequestDTO) -> AnyPublisher<ProfileEditResponseDTO, NetworkError>
func checkUserNickname(nickname: String) -> AnyPublisher<Bool, NetworkError>
func getUser() -> AnyPublisher<SignUpResponseDTO, NetworkError>
func validToken() -> AnyPublisher<TokenValidResponseDTO, NetworkError>
Expand Down
1 change: 1 addition & 0 deletions HongikYeolgong2/Models/Page.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ import Foundation

enum Page: Hashable {
case webView(title: String, url: String)
case profile
case signUp
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct OnboardingView: View {
.navigationDestination(for: Page.self) { page in
switch page {
case .signUp:
SignUpView()
ProfileEditView()
default:
EmptyView()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@
import SwiftUI

struct SubmitButton: View {
let action: () -> ()
let isEdit: Bool
let action: () -> Void
let disabled: Bool

var body: some View {
Button(action: action) {
Image(.submitButtonEnable)
Image(isEdit ? .editButtonEnable : .submitButtonEnable)
.resizable()
.frame(height: 50.adjustToScreenHeight)
}
}
.disabled(disabled)
.overlay(disabled ? Color.dark.opacity(0.6) : nil)
}
Expand Down
196 changes: 196 additions & 0 deletions HongikYeolgong2/Presentation/Auth/SignUp/ProfileEditView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
//
// SignInView.swift
// HongikYeolgong2
//
// Created by 권석기 on 10/3/24.
//

import SwiftUI

struct ProfileEditView: View {
@Environment(\.injected.interactors.userDataInteractor) var userDataInteractor

@State private var nickname: Nickname = .none
@State private var department: Department = .none
@State private var inputNickname = ""
@State private var inputDepartment = ""
@State private var loadState: Loadable<Bool> = .notRequest
@State private var isPresented: Bool = false
@State private var isEditing = false
@Environment(\.presentationMode) var dismiss

let isEdit: Bool

@FocusState private var focused

private var isDepartmentSelecte: Bool {
(Department.allDepartments.contains(department.rawValue) || Department.allDepartments.contains(inputDepartment))
}

private var isNicknameCheked: Bool {
nickname == .available
}

private var isEditCompleted: Bool {
isEdit && isEditing
}

init(nickname: String, department: String) {
self.inputNickname = nickname
self.inputDepartment = department
self.department = Department(rawValue: department) ?? .none
self.nickname = .none
self.isEdit = true
}

init() {
self.isEdit = false
}

var body: some View {
VStack(spacing: 0) {
VStack(spacing: 0) {
Spacer()
.frame(maxHeight: 52 + 23.adjustToScreenHeight)

VStack(alignment: .leading, spacing: 0) {
FormLabel(title: "닉네임")

Spacer()
.frame(height: 8.adjustToScreenHeight)

HStack(spacing: 10.adjustToScreenHeight) {
BaseTextField(
text: $inputNickname,
placeholder: "닉네임을 입력해주세요.",
isError: nickname.isError
)

DuplicateCheckButton(
action: { userDataInteractor.checkUserNickname(inputNickname: inputNickname, nickname: $nickname) },
disabled: nickname.isCheckable
)
.disabled(!(nickname == .checkAvailable))
}

Spacer()
.frame(height: 4.adjustToScreenHeight)

FormDescription(
message: nickname.message,
color: nickname.textColor
)
}
.layoutPriority(1)

Spacer()
.frame(height: 12.adjustToScreenHeight)

VStack(alignment: .leading, spacing: 8.adjustToScreenHeight) {
FormLabel(title: "학과")

DropDownPicker(
text: $inputDepartment,
seletedItem: Binding(
get: { department.rawValue },
set: { department = .init(rawValue: $0) ?? .none }
),
placeholder: "",
items: Department.allDepartments
)
}
.layoutPriority(2)
}

Spacer()

SubmitButton(
isEdit: isEdit,
action: editButtonTap,
disabled: !((isNicknameCheked && isDepartmentSelecte) || isEditCompleted)
)
.padding(.bottom, 20.adjustToScreenHeight)
}
.overlay(alignment: .topLeading, content: {
if isEdit {
HStack {
Button(action: {
dismiss.wrappedValue.dismiss()
}, label: {
Image(.icProfileLeft)
Text("프로필 변경")
.font(.suite(size: 18, weight: .bold))
.foregroundStyle(.gray100)
})
Spacer()
}
.frame(
maxWidth: .infinity,
maxHeight: 52.adjustToScreenHeight,
alignment: .leading
)
.background(Color.black)
} else {
Text("회원가입")
.font(.suite(size: 18, weight: .bold))
.foregroundStyle(.gray100)
.frame(
maxWidth: .infinity,
maxHeight: 52.adjustToScreenHeight,
alignment: .leading
)
.background(Color.black)
}
})
.toolbar(.hidden, for: .navigationBar)
.padding(.horizontal, 32.adjustToScreenWidth)
.systemOverlay(isPresented: $isPresented) {
ModalView(isPresented: $isPresented,
title: "프로필 변경을 진행하실건가요?",
confirmButtonText: "변경하기",
cancleButtonText: "돌아가기",
confirmAction: performUpdate )
}
.onTapGesture {
UIApplication.shared.hideKeyboard()
}
.onChange(of: inputNickname) {
userDataInteractor.validateUserNickname(inputNickname: $0, nickname: $nickname)
}
.onChange(of: nickname) { nickname in
if isEdit && nickname == .available {
isEditing = true
}
}
.onChange(of: inputDepartment) { department in
guard Department.allDepartments.contains(department) else { return }
isEditing = true
}
.onChange(of: department) { department in
guard department != .none else {
isEditing = false
return
}
isEditing = true
}
.onChange(of: loadState.value) { value in
guard let isSuccess = value else { return }
if isSuccess {
dismiss.wrappedValue.dismiss()
}
}

}

func editButtonTap() {
if isEdit {
isPresented = true
} else {
userDataInteractor.signUp(nickname: inputNickname, department: department, loadbleSubject: $loadState)
}
}

func performUpdate() {
userDataInteractor.profileEdit(nickname: inputNickname, department: department, loadbleSubject: $loadState)
}
}
Loading

0 comments on commit 190338a

Please sign in to comment.