Skip to content

Commit

Permalink
Merge pull request #83 from Nexters/feature/add-registration(#20)
Browse files Browse the repository at this point in the history
개인정보 등록 뷰
  • Loading branch information
enebin authored Aug 29, 2023
2 parents b8c6943 + 62fd070 commit 3e8698f
Show file tree
Hide file tree
Showing 11 changed files with 644 additions and 75 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// DismissKeyboardOnTap.swift
// Core
//
// Created by Young Bin on 2023/08/24.
// Copyright © 2023 team.humanwave. All rights reserved.
//

import SwiftUI

public struct DismissKeyboardOnTap: ViewModifier {
public init() {}

public func body(content: Content) -> some View {
content
.gesture(
TapGesture().onEnded {
UIApplication.shared.sendAction(
#selector(UIResponder.resignFirstResponder),
to: nil,
from: nil,
for: nil
)
}
)
}
}
73 changes: 38 additions & 35 deletions Projects/Domain/Sources/User/KeymeUserStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,55 +13,58 @@ protocol StorageKeyType {
}

public final class KeymeUserStorage {
@Dependency(\.localStorage) private var localStorage

var nickname: String?
var acesssToken: String?
private let localStorage: CoreLocalStorage

public init() {
self.nickname = self.get(.nickname)
self.acesssToken = self.get(.acesssToken)
init(localStorage: CoreLocalStorage) {
self.localStorage = localStorage
}

private func get(_ key: UserStorageKey) -> Any? {
localStorage.get(key)
}

public func get<T>(_ key: UserStorageKey) -> T? {
return localStorage.get(key) as? T
private func set(_ value: Any?, forKey key: UserStorageKey) {
localStorage.set(value, forKey: key)
}

public func set<T>(_ value: T?, forKey key: UserStorageKey) {
switch key {
case .acesssToken:
guard let flag = value as? String else { return }
self.acesssToken = flag
case .nickname:
guard let name = value as? String else { return }
self.nickname = name
private enum UserStorageKey: String, StorageKeyType {
case accessToken
case nickname
case profileImageURL
case profileThumbnailURL

public var name: String {
return "UserStorageKey_\(self.rawValue)"
}

localStorage.set(value, forKey: key)
}
}

public extension KeymeUserStorage {
enum UserStorageKey: StorageKeyType {
case acesssToken
case nickname

var name: String {
switch self {
case .acesssToken:
return "UserStorageKey_isLoggedIn"
case .nickname:
return "UserStorageKey_nickname"
}
}
var accessToken: String? {
get { get(.accessToken) as? String }
set { set(newValue, forKey: .accessToken) }
}

var nickname: String? {
get { get(.nickname) as? String }
set { set(newValue, forKey: .nickname) }
}

var profileImageURL: URL? {
get { get(.profileImageURL) as? URL }
set { set(newValue, forKey: .profileImageURL) }
}

var profileThumbnailURL: URL? {
get { get(.profileThumbnailURL) as? URL }
set { set(newValue, forKey: .profileThumbnailURL) }
}
}

extension KeymeUserStorage: DependencyKey {
public static var liveValue = KeymeUserStorage()
public static var testValue: KeymeUserStorage {
KeymeUserStorage() // FIXME:
}
public static var liveValue = KeymeUserStorage(localStorage: CoreLocalStorage.liveValue)
public static var testValue = KeymeUserStorage(
localStorage: CoreLocalStorage.testValue(storage: .init(suiteName: "TestStorage")!))
}

extension DependencyValues {
Expand Down
33 changes: 33 additions & 0 deletions Projects/Features/Sources/Registration/Modifiers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Modifiers.swift
// Features
//
// Created by 고도현 on 2023/08/12.
// Copyright © 2023 team.humanwave. All rights reserved.
//

import SwiftUI

// 최대 글자 수를 넘기면 좌, 우로 떨리는 애니메이션
struct Shake: ViewModifier {
@Binding var isShake: Bool

func body(content: Content) -> some View {
content
.offset(x: isShake ? -10 : 0) // 좌측으로 이동
.animation(
Animation
.easeInOut(duration: 0.1)
.repeatCount(3, autoreverses: true),
value: isShake)
.onChange(of: isShake) { newValue in
if !newValue {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
withAnimation {
self.isShake = false
}
}
}
}
}
}
128 changes: 128 additions & 0 deletions Projects/Features/Sources/Registration/RegisterFeature.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//
// RegisterFeature.swift
// Features
//
// Created by 이영빈 on 2023/08/23.
// Copyright © 2023 team.humanwave. All rights reserved.
//

import Foundation
import ComposableArchitecture
import Network

public struct RegistrationFeature: Reducer {
@Dependency(\.keymeAPIManager) var network
@Dependency(\.continuousClock) var clock

enum CancelID { case debouncedNicknameUpdate }

public init() {}

public struct State: Equatable {
var status: Status = .notDetermined
var isNicknameAvailable: Bool?
var canRegister: Bool {
return isNicknameAvailable == true
}

var thumbnailURL: URL?
var originalImageURL: URL?

var nicknameTextFieldString: String = ""

enum Status: Equatable {
case notDetermined
case needsRegister
case complete
}
}

public enum Action: Equatable {
case debouncedNicknameUpdate(text: String)

case checkDuplicatedNickname(String)
case checkDuplicatedNicknameResponse(Bool)

case registerProfileImage(Data)
case registerProfileImageResponse(thumbnailURL: URL, originalImageURL: URL)

case finishRegister(nickname: String, thumbnailURL: URL?, originalImageURL: URL?)
case finishRegisterResponse(id: Int, friendCode: String)
}

public var body: some Reducer<State, Action> {
Reduce { state, action in
switch action {
case .debouncedNicknameUpdate(let nicknameString):
state.nicknameTextFieldString = nicknameString
return .run { send in
try await withTaskCancellation(
id: CancelID.debouncedNicknameUpdate,
cancelInFlight: true
) {
try await self.clock.sleep(for: .seconds(0.7))

await send(.checkDuplicatedNickname(nicknameString))
}
}

// MARK: checkDuplicatedNickname
case .checkDuplicatedNickname(let nickname):
return .run(priority: .userInitiated) { send in
let result = try await network.request(
.registration(.checkDuplicatedNickname(nickname)),
object: VerifyNicknameDTO.self
)

await send(.checkDuplicatedNicknameResponse(result.data.valid))
}

case .checkDuplicatedNicknameResponse(let isNicknameDuplicated):
state.isNicknameAvailable = isNicknameDuplicated

// MARK: registerProfileImage
case .registerProfileImage(let imageData):
return .run { send in
let result = try await network.request(
.registration(.uploadImage(imageData)),
object: ImageUploadDTO.self)

if
let thumbnailURL = URL(string: result.data.thumbnailUrl),
let originalImageURL = URL(string: result.data.originalUrl)
{
await send(
.registerProfileImageResponse(
thumbnailURL: thumbnailURL,
originalImageURL: originalImageURL))
}
}

case .registerProfileImageResponse(let thumnailURL, let originalImageURL):
state.thumbnailURL = thumnailURL
state.originalImageURL = originalImageURL

// MARK: finishRegister
case .finishRegister(let nickname, let thumbnailURL, let originalImageURL):
return .run { send in
let result = try await network.request(
.registration(.updateMemberDetails(
nickname: nickname,
profileImage: thumbnailURL?.absoluteString,
profileThumbnail: originalImageURL?.absoluteString)),
object: MemberUpdateDTO.self)

await send(
.finishRegisterResponse(
id: result.data.id,
friendCode: result.data.friendCode ?? "")) // TODO: 나중에 non-null 값 필요
}

case .finishRegisterResponse:
state.status = .complete
}

return .none
}
}
}
Loading

0 comments on commit 3e8698f

Please sign in to comment.