feat: 新增CreateFeedView优化任务总结文档及相关功能实现
- 在CreateFeedView中优化发布按钮样式,增加圆角背景和渐变色。 - 移除内容输入区域的深灰色背景,提升UI体验。 - 实现点击发布按钮时自动收起键盘功能。 - 添加发布成功通知机制,确保外层刷新列表数据。 - 更新相关Feature以支持跨Feature通信和状态管理。
This commit is contained in:
43
issues/CreateFeedView优化.md
Normal file
43
issues/CreateFeedView优化.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# CreateFeedView 优化任务总结
|
||||
|
||||
## 任务要求
|
||||
1. 发布按钮增加圆角背景,高45,左右距离俯视图16,背景为左到右渐变色 #F854FC-#500FFF
|
||||
2. 移除内容输入区域的深灰色背景
|
||||
3. 点击发布按钮时,收起键盘
|
||||
4. 发布按钮触发api并成功后,要自动收起createfeedview,并通知外层刷新列表数据
|
||||
|
||||
## 实施内容
|
||||
|
||||
### 1. UI样式修改 (CreateFeedView.swift)
|
||||
- ✅ 发布按钮样式:高度45px,左右边距16px,渐变色背景 #F854FC-#500FFF
|
||||
- ✅ 移除内容输入区域的深灰色背景
|
||||
- ✅ 添加键盘收起功能:使用@FocusState管理焦点状态
|
||||
|
||||
### 2. 发布成功通知机制
|
||||
- ✅ CreateFeedFeature添加publishSuccess Action
|
||||
- ✅ 发布成功后发送通知:NotificationCenter.default.post
|
||||
- ✅ FeedListFeature监听通知并转发给MainFeature
|
||||
- ✅ MainFeature同时刷新FeedList和Me页面数据
|
||||
|
||||
### 3. 架构设计
|
||||
```
|
||||
CreateFeedFeature.publishSuccess
|
||||
↓ (NotificationCenter)
|
||||
FeedListFeature.createFeedPublishSuccess
|
||||
↓ (TCA Action)
|
||||
MainFeature.feedList(.createFeedPublishSuccess)
|
||||
↓ (Effect.merge)
|
||||
FeedListFeature.reload + MeFeature.refresh
|
||||
```
|
||||
|
||||
## 技术要点
|
||||
1. 使用@FocusState管理键盘焦点,点击发布按钮时自动收起键盘
|
||||
2. 使用NotificationCenter进行跨Feature通信
|
||||
3. 通过TCA的Effect.merge同时触发多个刷新操作
|
||||
4. 保持TCA架构的清晰分层
|
||||
|
||||
## 测试建议
|
||||
1. 测试发布按钮样式是否正确显示
|
||||
2. 测试点击发布按钮时键盘是否收起
|
||||
3. 测试发布成功后是否自动关闭页面
|
||||
4. 测试FeedList和Me页面是否自动刷新显示新数据
|
@@ -49,6 +49,9 @@ struct CreateFeedFeature {
|
||||
case imageUploadCompleted([String], [UIImage]) // urls, images
|
||||
case imageUploadFailed(Error)
|
||||
case publishContent
|
||||
|
||||
// 新增:发布成功通知
|
||||
case publishSuccess
|
||||
}
|
||||
|
||||
@Dependency(\.apiService) var apiService
|
||||
@@ -216,7 +219,11 @@ struct CreateFeedFeature {
|
||||
case .publishResponse(.success(let response)):
|
||||
state.isLoading = false
|
||||
if response.code == 200 {
|
||||
return .send(.dismissView)
|
||||
// 发布成功,先发送通知,然后关闭页面
|
||||
return .merge(
|
||||
.send(.publishSuccess),
|
||||
.send(.dismissView)
|
||||
)
|
||||
} else {
|
||||
state.errorMessage = response.message.isEmpty ? "发布失败" : response.message
|
||||
return .none
|
||||
@@ -238,6 +245,11 @@ struct CreateFeedFeature {
|
||||
return .run { _ in
|
||||
await dismiss()
|
||||
}
|
||||
case .publishSuccess:
|
||||
// 发送通知给外层刷新列表
|
||||
return .run { _ in
|
||||
NotificationCenter.default.post(name: .init("CreateFeedPublishSuccess"), object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,6 +278,8 @@ extension CreateFeedFeature.Action: Equatable {
|
||||
return a.localizedDescription == b.localizedDescription
|
||||
case (.publishContent, .publishContent):
|
||||
return true
|
||||
case (.publishSuccess, .publishSuccess):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
@@ -47,6 +47,8 @@ struct FeedListFeature {
|
||||
// 新增:点赞相关Action
|
||||
case likeDynamic(Int, Int, Int, Int) // dynamicId, uid, likedUid, worldId
|
||||
case likeResponse(TaskResult<LikeDynamicResponse>, dynamicId: Int, loadingId: UUID?)
|
||||
// 新增:CreateFeed发布成功通知
|
||||
case createFeedPublishSuccess
|
||||
// 预留后续 Action
|
||||
}
|
||||
|
||||
@@ -142,6 +144,9 @@ struct FeedListFeature {
|
||||
case .editFeedDismissed:
|
||||
state.isEditFeedPresented = false
|
||||
return .none
|
||||
case .createFeedPublishSuccess:
|
||||
// CreateFeed发布成功,触发刷新
|
||||
return .send(.reload)
|
||||
case .testButtonTapped:
|
||||
debugInfoSync("[LOG] FeedListFeature testButtonTapped")
|
||||
return .none
|
||||
|
@@ -74,6 +74,12 @@ struct MainFeature {
|
||||
case .feedList(.testButtonTapped):
|
||||
state.navigationPath.append(.testView)
|
||||
return .none
|
||||
case .feedList(.createFeedPublishSuccess):
|
||||
// CreateFeed发布成功,刷新FeedList和Me页数据
|
||||
return .merge(
|
||||
.send(.feedList(.reload)),
|
||||
.send(.me(.refresh))
|
||||
)
|
||||
case .feedList:
|
||||
return .none
|
||||
case let .accountModelLoaded(accountModel):
|
||||
|
@@ -6,6 +6,7 @@ struct CreateFeedView: View {
|
||||
let store: StoreOf<CreateFeedFeature>
|
||||
@State private var keyboardHeight: CGFloat = 0
|
||||
@State private var isKeyboardVisible: Bool = false
|
||||
@FocusState private var isTextEditorFocused: Bool
|
||||
|
||||
var body: some View {
|
||||
WithPerceptionTracking {
|
||||
@@ -18,7 +19,7 @@ struct CreateFeedView: View {
|
||||
|
||||
// 主要内容区域
|
||||
VStack(spacing: 20) {
|
||||
ContentInputSection(store: store)
|
||||
ContentInputSection(store: store, isFocused: $isTextEditorFocused)
|
||||
ImageSelectionSection(store: store)
|
||||
LoadingAndErrorSection(store: store)
|
||||
|
||||
@@ -30,7 +31,7 @@ struct CreateFeedView: View {
|
||||
|
||||
// 底部发布按钮 - 只在键盘隐藏时显示
|
||||
if !isKeyboardVisible {
|
||||
PublishButtonSection(store: store, keyboardHeight: keyboardHeight, geometry: geometry)
|
||||
PublishButtonSection(store: store, keyboardHeight: keyboardHeight, geometry: geometry, isFocused: $isTextEditorFocused)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +54,7 @@ struct CreateFeedView: View {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
if isKeyboardVisible {
|
||||
Button(action: {
|
||||
isTextEditorFocused = false // 收起键盘
|
||||
store.send(.publishButtonTapped)
|
||||
}) {
|
||||
HStack(spacing: 4) {
|
||||
@@ -114,15 +116,13 @@ struct CreateFeedView: View {
|
||||
// MARK: - 内容输入区域组件
|
||||
struct ContentInputSection: View {
|
||||
let store: StoreOf<CreateFeedFeature>
|
||||
@FocusState.Binding var isFocused: Bool
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
// 文本输入框
|
||||
// 文本输入框 - 移除深灰色背景
|
||||
ZStack(alignment: .topLeading) {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(Color.white.opacity(0.1))
|
||||
.frame(height: 200)
|
||||
|
||||
if store.content.isEmpty {
|
||||
Text(NSLocalizedString("createFeed.enterContent", comment: "Enter Content"))
|
||||
.foregroundColor(.white.opacity(0.5))
|
||||
@@ -137,6 +137,7 @@ struct ContentInputSection: View {
|
||||
.padding(.vertical, 8)
|
||||
.scrollContentBackground(.hidden)
|
||||
.frame(height: 200)
|
||||
.focused($isFocused)
|
||||
}
|
||||
|
||||
// 字符计数
|
||||
@@ -249,10 +250,12 @@ struct PublishButtonSection: View {
|
||||
let store: StoreOf<CreateFeedFeature>
|
||||
let keyboardHeight: CGFloat
|
||||
let geometry: GeometryProxy
|
||||
@FocusState.Binding var isFocused: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Button(action: {
|
||||
isFocused = false // 收起键盘
|
||||
store.send(.publishButtonTapped)
|
||||
}) {
|
||||
HStack {
|
||||
@@ -270,13 +273,22 @@ struct PublishButtonSection: View {
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.background(Color(hex: 0x0C0527))
|
||||
.cornerRadius(25)
|
||||
.frame(height: 45)
|
||||
.background(
|
||||
LinearGradient(
|
||||
gradient: Gradient(colors: [
|
||||
Color(hex: 0xF854FC),
|
||||
Color(hex: 0x500FFF)
|
||||
]),
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
)
|
||||
.cornerRadius(22.5)
|
||||
.disabled(store.isLoading || store.isUploadingImages || !store.canPublish)
|
||||
.opacity(buttonOpacity)
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.bottom, bottomPadding)
|
||||
}
|
||||
.background(Color(hex: 0x0C0527))
|
||||
|
@@ -262,6 +262,9 @@ struct FeedListView: View {
|
||||
CreateFeedFeature()
|
||||
}
|
||||
)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .init("CreateFeedPublishSuccess"))) { _ in
|
||||
store.send(.createFeedPublishSuccess)
|
||||
}
|
||||
}
|
||||
// 新增:详情页导航
|
||||
.navigationDestination(isPresented: viewStore.binding(get: \.showDetail, send: { _ in .detailDismissed })) {
|
||||
|
Reference in New Issue
Block a user