Skip to content

Commit bee0cf5

Browse files
authored
[#624] 버튼의 인터렉션에 의한 작업 중의 UI는 버튼에 뜨도록 수정한다 (#629)
* ui: 로그인 버튼에 ProgressView를 띄우도록 수정 * ui: SettingsView에 각 버트에 ProgressView 적용 * fix: AccountView 로딩 표시 조정 * PushNotifiactionSettingsView 로딩 표시 조정 * refactor: 낙관적 업데이트로 인한 체크마크 선 표시 후 ProgressView가 뜨는 현상 해결 * fix: AccountViewd에서 loading 액션 시 상시 Provider가 nil이 되어 ProgressView가 보이지 않는 현상 해결 * fix: ProgressView와 버튼 크기가 달라 row가 왔다갔다 하는 현상 해결 * ui: TodoEditorView에 Todo 데이터를 업로드하면 툴바 우측 버튼 위치에 ProgressView가 보이도록 수정 * ui: HomeView에서 웹페이지 추가 시 툴바 위치에 LoadingView가 뜨도록 수정 * ui: PushNotficationSettingsView에서 피커용 시트 툴바에 ProgressView 추가 * fix: SettingsView 로딩 row 상태 해제 * fix: Settings 로딩 상태 정리 시점 수정 * chore: CI 재실행
1 parent 8612d24 commit bee0cf5

20 files changed

Lines changed: 427 additions & 106 deletions

Application/DevLogPresentation/Sources/Common/Component/LoginButton.swift

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,49 @@ struct LoginButton: View {
1212
@State private var logo: Image?
1313
@State private var text = ""
1414
@ScaledMetric(relativeTo: .body) private var height = CGFloat(22)
15+
private let showsProgressView: Bool
1516
private let action: () -> Void
1617

1718
init(
1819
logo: Image? = nil,
1920
text: String = "",
21+
showsProgressView: Bool = false,
2022
action: @escaping () -> Void = {}
2123
) {
2224
self._logo = State(initialValue: logo)
2325
self._text = State(initialValue: text)
26+
self.showsProgressView = showsProgressView
2427
self.action = action
2528
}
2629

2730
var body: some View {
2831
Button {
2932
action()
3033
} label: {
31-
Text(text)
32-
.foregroundStyle(Color.primary)
33-
.font(.system(.body))
34-
.contentShape(.capsule)
35-
.frame(width: 300, height: height + 16)
36-
.overlay {
37-
ZStack(alignment: .leading) {
38-
Capsule()
39-
.stroke(Color.gray, lineWidth: 1)
40-
if let logo = logo {
41-
logo
42-
.resizable()
43-
.scaledToFit()
44-
.frame(width: height, height: height)
45-
.padding(.leading)
46-
}
34+
ZStack {
35+
Text(text)
36+
.opacity(showsProgressView ? 0 : 1)
37+
if showsProgressView {
38+
ProgressView()
39+
}
40+
}
41+
.foregroundStyle(Color.primary)
42+
.font(.system(.body))
43+
.contentShape(.capsule)
44+
.frame(width: 300, height: height + 16)
45+
.overlay {
46+
ZStack(alignment: .leading) {
47+
Capsule()
48+
.stroke(Color.gray, lineWidth: 1)
49+
if let logo, !showsProgressView {
50+
logo
51+
.resizable()
52+
.scaledToFit()
53+
.frame(width: height, height: height)
54+
.padding(.leading)
4755
}
4856
}
57+
}
4958
}
5059
}
5160
}

Application/DevLogPresentation/Sources/Home/Editor/TodoEditorView.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,19 @@ struct TodoEditorView: View {
6767
Image(systemName: "info.circle")
6868
}
6969
}
70-
ToolbarTrailingButton {
71-
submit()
70+
if store.isLoading {
71+
if #available(iOS 26.0, *) {
72+
ToolbarSpacer(.fixed, placement: .topBarTrailing)
73+
}
74+
ToolbarItem(placement: .topBarTrailing) {
75+
ProgressView()
76+
}
77+
} else {
78+
ToolbarTrailingButton {
79+
submit()
80+
}
81+
.disabled(!store.isReadyToSubmit)
7282
}
73-
.disabled(!store.isReadyToSubmit || store.isLoading)
7483
}
7584
.alert($store.scope(state: \.alert, action: \.alert))
7685
}

Application/DevLogPresentation/Sources/Home/Home/HomeFeature+Effects.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ extension HomeFeature {
7676
trackAnalyticsEventUseCase.execute(.webPageCreate)
7777
let pages = try await fetchWebPagesUseCase.execute("")
7878
await send(.store(.updateWebPages(pages.map(WebPageItem.init(from:)))))
79+
await send(.store(.setSheet(nil)))
7980
} catch {
8081
await send(.store(.setAlert(isPresented: true, type: .error)))
8182
}

Application/DevLogPresentation/Sources/Home/Home/HomeFeature.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ private extension HomeFeature {
253253
Self.setAlert(&state, isPresented: true, type: .invalidURL)
254254
return .none
255255
}
256-
state.sheet = nil
257256
Self.setAlert(&state, isPresented: false, type: nil)
258257
return addWebPageEffect(normalizedURL)
259258
case .deleteWebPage(let page):

Application/DevLogPresentation/Sources/Home/Home/HomeView.swift

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@ struct HomeView: View {
3838
.alert($store.scope(state: \.alert, action: \.alert))
3939
.sheet(item: $store.scope(state: \.sheet, action: \.sheet), content: sheetContent)
4040
.fullScreenCover(item: $store.scope(state: \.fullScreenCover, action: \.fullScreenCover), content: coverContent)
41-
.overlay {
42-
if store.isAppending {
43-
LoadingView()
44-
}
45-
}
4641
}
4742

4843
private var todoSection: some View {
@@ -234,9 +229,18 @@ struct HomeView: View {
234229
.navigationTitle(Text(String(localized: "home_webpage_input_title")))
235230
.navigationBarTitleDisplayMode(.inline) // 설정 안하면 섹션 위에 내비게이션 large 만큼 영역 먹음
236231
.toolbar {
237-
ToolbarItem(placement: .topBarTrailing) {
238-
Button(String(localized: "home_add")) {
239-
store.send(.view(.addWebPage))
232+
if store.isAppending {
233+
if #available(iOS 26.0, *) {
234+
ToolbarSpacer(.fixed, placement: .topBarTrailing)
235+
}
236+
ToolbarItem(placement: .topBarTrailing) {
237+
ProgressView()
238+
}
239+
} else {
240+
ToolbarItem(placement: .topBarTrailing) {
241+
Button(String(localized: "home_add")) {
242+
store.send(.view(.addWebPage))
243+
}
240244
}
241245
}
242246
}

Application/DevLogPresentation/Sources/Login/LoginFeature.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct LoginFeature {
1414
@ObservableState
1515
struct State: Equatable {
1616
@Presents var alert: AlertState<Never>?
17+
var activeSignInProvider: AuthProvider?
1718
var loading = LoadingFeature.State()
1819

1920
var isLoading: Bool {
@@ -44,11 +45,15 @@ struct LoginFeature {
4445
case .alert:
4546
break
4647
case .tapSignInButton(let provider):
48+
guard !state.isLoading else { return .none }
49+
state.activeSignInProvider = provider
4750
return signInEffect(provider)
4851
case .signInFailed(let alertType):
4952
state.alert = Self.alertState(for: alertType)
5053
case .loading:
51-
break
54+
if !state.isLoading {
55+
state.activeSignInProvider = nil
56+
}
5257
}
5358
return .none
5459
}

Application/DevLogPresentation/Sources/Login/LoginView.swift

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,38 +25,55 @@ struct LoginView: View {
2525
}
2626

2727
var body: some View {
28-
ZStack {
29-
VStack {
30-
Spacer()
31-
Image("Primary")
32-
.resizable()
33-
.scaledToFit()
34-
.frame(width: sceneWidth / 5)
35-
Spacer()
36-
VStack(spacing: 20) {
37-
LoginButton(logo: Image("Google"), text: String(localized: "login_google_sign_in")) {
38-
store.send(.tapSignInButton(.google))
39-
}
40-
41-
LoginButton(logo: Image("Github"), text: String(localized: "login_github_sign_in")) {
42-
store.send(.tapSignInButton(.github))
43-
}
44-
45-
LoginButton(logo: Image("Apple"), text: String(localized: "login_apple_sign_in")) {
46-
store.send(.tapSignInButton(.apple))
47-
}
48-
}
49-
.padding(.bottom, 30)
50-
Text(String(localized: "login_terms_notice"))
51-
.font(.caption2)
52-
.foregroundStyle(Color.gray)
53-
.multilineTextAlignment(.center)
54-
.padding(.vertical)
55-
}
56-
if store.isLoading {
57-
LoadingView()
28+
VStack {
29+
Spacer()
30+
Image("Primary")
31+
.resizable()
32+
.scaledToFit()
33+
.frame(width: sceneWidth / 5)
34+
Spacer()
35+
VStack(spacing: 20) {
36+
signInButton(
37+
provider: .google,
38+
logo: Image("Google"),
39+
text: String(localized: "login_google_sign_in")
40+
)
41+
42+
signInButton(
43+
provider: .github,
44+
logo: Image("Github"),
45+
text: String(localized: "login_github_sign_in")
46+
)
47+
48+
signInButton(
49+
provider: .apple,
50+
logo: Image("Apple"),
51+
text: String(localized: "login_apple_sign_in")
52+
)
5853
}
54+
.padding(.bottom, 30)
55+
Text(String(localized: "login_terms_notice"))
56+
.font(.caption2)
57+
.foregroundStyle(Color.gray)
58+
.multilineTextAlignment(.center)
59+
.padding(.vertical)
5960
}
6061
.alert($store.scope(state: \.alert, action: \.alert))
6162
}
63+
64+
private func signInButton(
65+
provider: AuthProvider,
66+
logo: Image,
67+
text: String
68+
) -> some View {
69+
LoginButton(
70+
logo: logo,
71+
text: text,
72+
showsProgressView: store.activeSignInProvider == provider
73+
) {
74+
store.send(.tapSignInButton(provider))
75+
}
76+
.disabled(store.isLoading)
77+
.opacity(store.isLoading ? 0.5 : 1)
78+
}
6279
}

Application/DevLogPresentation/Sources/Settings/AccountFeature.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ struct AccountFeature {
1717
var currentProvider: AuthProvider?
1818
var connectedProviders: [AuthProvider] = []
1919
var disconnectedProviders: [AuthProvider] = []
20+
var activeLoadingProvider: AuthProvider?
2021
var loading = LoadingFeature.State()
2122

2223
var isLoading: Bool {
@@ -56,8 +57,12 @@ struct AccountFeature {
5657
case .onAppear:
5758
return fetchProvidersEffect()
5859
case .linkWithProvider(let provider):
60+
guard !state.isLoading else { return .none }
61+
state.activeLoadingProvider = provider
5962
return linkProviderEffect(provider)
6063
case .unlinkFromProvider(let provider):
64+
guard !state.isLoading else { return .none }
65+
state.activeLoadingProvider = provider
6166
return unlinkProviderEffect(provider)
6267
case .setAlert(let type):
6368
state.alert = Self.alertState(for: type)
@@ -66,6 +71,10 @@ struct AccountFeature {
6671
state.connectedProviders = allProviders.filter { $0 != currentProvider }
6772
state.disconnectedProviders = AuthProvider.allCases
6873
.filter { !allProviders.contains($0) }
74+
case .loading(.end):
75+
if !state.isLoading {
76+
state.activeLoadingProvider = nil
77+
}
6978
case .loading:
7079
break
7180
}

Application/DevLogPresentation/Sources/Settings/AccountView.swift

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct AccountView: View {
2525
let providers = AuthProvider.allCases.filter { $0 != store.currentProvider }
2626
ForEach(providers, id: \.self) { provider in
2727
let isConnected = store.connectedProviders.contains(provider)
28+
let showProgressView = store.isLoading && store.activeLoadingProvider == provider
2829
HStack {
2930
providerContent(provider)
3031
Spacer()
@@ -38,14 +39,21 @@ struct AccountView: View {
3839
Text(isConnected
3940
? String(localized: "account_disconnect")
4041
: String(localized: "account_connect"))
41-
.font(.caption.weight(.semibold))
42-
.foregroundStyle(.white)
43-
.padding(.horizontal, 12)
44-
.padding(.vertical, 6)
45-
.background(isConnected ? Color.red : .blue)
46-
.clipShape(.capsule)
42+
.font(.caption.weight(.semibold))
43+
.foregroundStyle(.white)
44+
.padding(.horizontal, 12)
45+
.padding(.vertical, 6)
46+
.background(isConnected ? Color.red : .blue)
47+
.clipShape(.capsule)
4748
}
4849
.buttonStyle(.plain)
50+
.disabled(store.isLoading)
51+
.opacity(showProgressView ? 0 : 1)
52+
.overlay {
53+
if showProgressView {
54+
ProgressView()
55+
}
56+
}
4957
}
5058
}
5159
}
@@ -55,11 +63,6 @@ struct AccountView: View {
5563
.navigationTitle(String(localized: "nav_account"))
5664
.onAppear { store.send(.onAppear) }
5765
.alert($store.scope(state: \.alert, action: \.alert))
58-
.overlay {
59-
if store.isLoading {
60-
LoadingView()
61-
}
62-
}
6366
}
6467

6568
private func formattedProviderName(_ provider: AuthProvider) -> String {

0 commit comments

Comments
 (0)