feat: 更新Swift助手样式和动态视图组件
- 在swift-assistant-style.mdc中更新上下文信息,简化描述并保留关键信息。 - 在swift-swiftui-dev-rules.mdc中将alwaysApply设置为false,调整开发规则。 - 在Info.plist中移除冗余的CFBundleDisplayName和CFBundleName键,保持文件整洁。 - 在FeedFeature.swift中添加调试日志,增强API响应的可追踪性。 - 在HomeFeature.swift中新增Feed状态和相关actions,优化状态管理。 - 在FeedView.swift中直接使用store状态,提升组件性能和可读性。 - 在HomeView.swift中更新FeedView的store传递方式,确保状态一致性。 - 更新Xcode项目配置,调整代码签名和Swift版本,确保兼容性。
This commit is contained in:
@@ -3,56 +3,30 @@ description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# CONTEXT
|
||||
I wish to receive advice using the latest tools and seek step-by-step guidance to fully understand the implementation process.
|
||||
|
||||
# CONTEXT
|
||||
|
||||
I am a native Chinese speaker who has just begun learning Swift 6 and Xcode 15+, and I am enthusiastic about exploring new technologies. I wish to receive advice using the latest tools and
|
||||
seek step-by-step guidance to fully understand the implementation process. Since many excellent code resources are in English, I hope my questions can be thoroughly understood. Therefore,
|
||||
I would like the AI assistant to think and reason in English, then translate the English responses into Chinese for me.
|
||||
|
||||
---
|
||||
|
||||
# OBJECTIVE
|
||||
|
||||
As an expert AI programming assistant, your task is to provide me with clear and readable SwiftUI code. You should:
|
||||
|
||||
- Utilize the latest versions of SwiftUI and Swift, being familiar with the newest features and best practices.
|
||||
- Provide careful and accurate answers that are well-founded and thoughtfully considered.
|
||||
- **Explicitly use the Chain-of-Thought (CoT) method in your reasoning and answers, explaining your thought process step by step.**
|
||||
- Strictly adhere to my requirements and meticulously complete the tasks.
|
||||
- Begin by outlining your proposed approach with detailed steps or pseudocode.
|
||||
- Upon confirming the plan, proceed to write the code.
|
||||
|
||||
---
|
||||
|
||||
|
||||
# STYLE
|
||||
|
||||
- Keep answers concise and direct, minimizing unnecessary wording.
|
||||
- Emphasize code readability over performance optimization.
|
||||
- Maintain a professional and supportive tone, ensuring clarity of content.
|
||||
|
||||
---
|
||||
|
||||
# TONE
|
||||
|
||||
- Be positive and encouraging, helping me improve my programming skills.
|
||||
- Be professional and patient, assisting me in understanding each step.
|
||||
|
||||
---
|
||||
|
||||
# AUDIENCE
|
||||
|
||||
The target audience is me—a native Chinese developer eager to learn Swift 6 and Xcode 15+, seeking guidance and advice on utilizing the latest technologies.
|
||||
|
||||
---
|
||||
The target audience is me, a native Chinese developer eager to learn Swift 6 and Xcode 15.9, seeking guidance and advice on utilizing the latest technologies.
|
||||
|
||||
# RESPONSE FORMAT
|
||||
|
||||
- **Utilize the Chain-of-Thought (CoT) method to reason and respond, explaining your thought process step by step.**
|
||||
- Conduct reasoning, thinking, and code writing in English.
|
||||
- The final reply should translate the English into Chinese for me.
|
||||
- The reply should include:
|
||||
|
||||
1. **Step-by-Step Plan**: Describe the implementation process with detailed pseudocode or step-by-step explanations, showcasing your thought process.
|
||||
2. **Code Implementation**: Provide correct, up-to-date, error-free, fully functional, runnable, secure, and efficient code. The code should:
|
||||
- Include all necessary imports and properly name key components.
|
||||
@@ -60,10 +34,3 @@ alwaysApply: true
|
||||
3. **Concise Response**: Minimize unnecessary verbosity, focusing only on essential information.
|
||||
|
||||
- If a correct answer may not exist, please point it out. If you do not know the answer, please honestly inform me rather than guessing.
|
||||
|
||||
---
|
||||
|
||||
# START ANALYSIS
|
||||
|
||||
If you understand, please prepare to assist me and await my question.
|
||||
|
@@ -1,7 +1,5 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
alwaysApply: false
|
||||
---
|
||||
You are an expert iOS developer using Swift and SwiftUI. Follow these guidelines:
|
||||
|
||||
|
@@ -439,9 +439,10 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = yana/yana.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = EKM7RAGNA6;
|
||||
DEVELOPMENT_TEAM = Z7UCRF23F3;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -458,7 +459,7 @@
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers\"",
|
||||
);
|
||||
INFOPLIST_FILE = yana/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = EParti;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "E-PARTi";
|
||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "此App将可发现和连接到您所用网络上的设备。";
|
||||
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "“eparty”需要您的同意,才可以进行定位服务,访问网络状态";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
@@ -473,15 +474,16 @@
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
OTHER_LIBTOOLFLAGS = "-framework \"SystemConfiguration\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stupidmonkey.yana.yana;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.junpeiqi.eparty;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "yana/yana-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 5.9;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -494,9 +496,10 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = yana/yana.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = EKM7RAGNA6;
|
||||
DEVELOPMENT_TEAM = Z7UCRF23F3;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -513,7 +516,7 @@
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers\"",
|
||||
);
|
||||
INFOPLIST_FILE = yana/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = EParti;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "E-PARTi";
|
||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "此App将可发现和连接到您所用网络上的设备。";
|
||||
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "“eparty”需要您的同意,才可以进行定位服务,访问网络状态";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
@@ -528,15 +531,16 @@
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
OTHER_LIBTOOLFLAGS = "-framework \"SystemConfiguration\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stupidmonkey.yana.yana;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.junpeiqi.eparty;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "yana/yana-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 5.9;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Release;
|
||||
@@ -558,7 +562,7 @@
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 5.9;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/yana.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/yana";
|
||||
};
|
||||
@@ -581,7 +585,7 @@
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 5.9;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/yana.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/yana";
|
||||
};
|
||||
|
91
yana.xcodeproj/xcshareddata/xcschemes/yana.xcscheme
Normal file
91
yana.xcodeproj/xcshareddata/xcschemes/yana.xcscheme
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4C3E651E2DB61F7A00E5A455"
|
||||
BuildableName = "yana.app"
|
||||
BlueprintName = "yana"
|
||||
ReferencedContainer = "container:yana.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4C4C8FBC2DE5AF9200384527"
|
||||
BuildableName = "yanaAPITests.xctest"
|
||||
BlueprintName = "yanaAPITests"
|
||||
ReferencedContainer = "container:yana.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4C3E651E2DB61F7A00E5A455"
|
||||
BuildableName = "yana.app"
|
||||
BlueprintName = "yana"
|
||||
ReferencedContainer = "container:yana.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4C3E651E2DB61F7A00E5A455"
|
||||
BuildableName = "yana.app"
|
||||
BlueprintName = "yana"
|
||||
ReferencedContainer = "container:yana.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@@ -10,5 +10,18 @@
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>4C3E651E2DB61F7A00E5A455</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>4C4C8FBC2DE5AF9200384527</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@@ -3,38 +3,4 @@
|
||||
uuid = "A60FAB2A-3184-45B2-920F-A3D7A086CF95"
|
||||
type = "0"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "4D63F38A-4F7C-46D9-8CAF-BCA831664FA0"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "yana/APIs/APIService.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "126"
|
||||
endingLineNumber = "126"
|
||||
landmarkName = "request(_:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "19930D63-5B42-4287-8B22-ADF87CAD40E3"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "yana/APIs/APIService.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "112"
|
||||
endingLineNumber = "112"
|
||||
landmarkName = "request(_:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
|
@@ -74,32 +74,50 @@ struct FeedFeature {
|
||||
case let .momentsResponse(.success(response)):
|
||||
state.isLoading = false
|
||||
|
||||
// 添加调试日志
|
||||
debugInfo("📱 FeedFeature: API 响应成功")
|
||||
debugInfo("📱 FeedFeature: response.code = \(response.code)")
|
||||
debugInfo("📱 FeedFeature: response.message = \(response.message)")
|
||||
debugInfo("📱 FeedFeature: response.data = \(response.data != nil ? "有数据" : "无数据")")
|
||||
|
||||
// 检查响应状态
|
||||
guard response.code == 200, let data = response.data else {
|
||||
state.error = response.message.isEmpty ? "获取动态失败" : response.message
|
||||
let errorMsg = response.message.isEmpty ? "获取动态失败" : response.message
|
||||
state.error = errorMsg
|
||||
debugError("❌ FeedFeature: API 响应失败 - code: \(response.code), message: \(errorMsg)")
|
||||
return .none
|
||||
}
|
||||
|
||||
// 添加数据调试日志
|
||||
debugInfo("📱 FeedFeature: data.dynamicList.count = \(data.dynamicList.count)")
|
||||
debugInfo("📱 FeedFeature: data.nextDynamicId = \(data.nextDynamicId)")
|
||||
|
||||
// 判断是刷新还是加载更多
|
||||
let isRefresh = state.nextDynamicId == 0
|
||||
debugInfo("📱 FeedFeature: isRefresh = \(isRefresh)")
|
||||
|
||||
if isRefresh {
|
||||
// 刷新:替换所有数据
|
||||
state.moments = data.dynamicList
|
||||
debugInfo(" FeedFeature: 刷新数据,moments.count = \(state.moments.count)")
|
||||
} else {
|
||||
// 加载更多:追加到现有数据
|
||||
let oldCount = state.moments.count
|
||||
state.moments.append(contentsOf: data.dynamicList)
|
||||
debugInfo(" FeedFeature: 加载更多,moments.count: \(oldCount) -> \(state.moments.count)")
|
||||
}
|
||||
|
||||
// 更新分页状态
|
||||
state.nextDynamicId = data.nextDynamicId
|
||||
state.hasMoreData = !data.dynamicList.isEmpty
|
||||
|
||||
debugInfo("📱 FeedFeature: 更新完成 - nextDynamicId: \(state.nextDynamicId), hasMoreData: \(state.hasMoreData)")
|
||||
return .none
|
||||
|
||||
case let .momentsResponse(.failure(error)):
|
||||
state.isLoading = false
|
||||
state.error = error.localizedDescription
|
||||
debugError("❌ FeedFeature: API 请求失败 - \(error.localizedDescription)")
|
||||
return .none
|
||||
|
||||
case .clearError:
|
||||
|
@@ -13,6 +13,9 @@ struct HomeFeature {
|
||||
// 设置页面相关状态
|
||||
var isSettingPresented = false
|
||||
var settingState = SettingFeature.State()
|
||||
|
||||
// 新增:Feed 状态
|
||||
var feedState = FeedFeature.State()
|
||||
}
|
||||
|
||||
enum Action: Equatable {
|
||||
@@ -27,13 +30,21 @@ struct HomeFeature {
|
||||
// 设置页面相关actions
|
||||
case settingDismissed
|
||||
case setting(SettingFeature.Action)
|
||||
|
||||
// 新增:Feed actions
|
||||
case feed(FeedFeature.Action)
|
||||
}
|
||||
|
||||
var body: some ReducerOf<Self> {
|
||||
Scope(state: \.settingState, action: \.setting) {
|
||||
Scope(state: \ .settingState, action: \ .setting) {
|
||||
SettingFeature()
|
||||
}
|
||||
|
||||
// 新增:Feed Scope
|
||||
Scope(state: \ .feedState, action: \ .feed) {
|
||||
FeedFeature()
|
||||
}
|
||||
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case .onAppear:
|
||||
@@ -79,6 +90,9 @@ struct HomeFeature {
|
||||
case .setting:
|
||||
// 由子reducer处理
|
||||
return .none
|
||||
case .feed(_):
|
||||
// FeedFeature 的 action 已由 Scope 自动处理,无需额外处理
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,4 +101,4 @@ struct HomeFeature {
|
||||
// MARK: - Notification Extension
|
||||
extension Notification.Name {
|
||||
static let homeLogout = Notification.Name("homeLogout")
|
||||
}
|
||||
}
|
||||
|
@@ -2,10 +2,6 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>E-PARTi</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>E-PARTi</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
@@ -5,7 +5,7 @@ struct FeedView: View {
|
||||
let store: StoreOf<FeedFeature>
|
||||
|
||||
var body: some View {
|
||||
WithViewStore(store, observe: { $0 }) { viewStore in
|
||||
WithPerceptionTracking {
|
||||
GeometryReader { geometry in
|
||||
ScrollView {
|
||||
VStack(spacing: 20) {
|
||||
@@ -44,44 +44,46 @@ struct FeedView: View {
|
||||
.padding(.horizontal, 30)
|
||||
.padding(.top, 20)
|
||||
|
||||
// 真实动态数据
|
||||
LazyVStack(spacing: 16) {
|
||||
if viewStore.moments.isEmpty {
|
||||
// 空状态
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "heart.text.square")
|
||||
.font(.system(size: 40))
|
||||
.foregroundColor(.white.opacity(0.6))
|
||||
|
||||
Text("暂无动态内容")
|
||||
.font(.system(size: 16))
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
|
||||
if let error = viewStore.error {
|
||||
Text("错误: \(error)")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.red.opacity(0.8))
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 20)
|
||||
// 真实动态数据 - 直接使用store状态
|
||||
WithPerceptionTracking {
|
||||
LazyVStack(spacing: 16) {
|
||||
if store.moments.isEmpty {
|
||||
// 空状态
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "heart.text.square")
|
||||
.font(.system(size: 40))
|
||||
.foregroundColor(.white.opacity(0.6))
|
||||
|
||||
Text("暂无动态内容")
|
||||
.font(.system(size: 16))
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
|
||||
if let error = store.error {
|
||||
Text("错误: \(error)")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.red.opacity(0.8))
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
}
|
||||
.padding(.top, 40)
|
||||
} else {
|
||||
// 显示真实动态数据
|
||||
ForEach(Array(store.moments.enumerated()), id: \.element.dynamicId) { index, moment in
|
||||
OptimizedDynamicCardView(
|
||||
moment: moment,
|
||||
allMoments: store.moments,
|
||||
currentIndex: index
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(.top, 40)
|
||||
} else {
|
||||
// 显示真实动态数据
|
||||
ForEach(Array(viewStore.moments.enumerated()), id: \.element.dynamicId) { index, moment in
|
||||
OptimizedDynamicCardView(
|
||||
moment: moment,
|
||||
allMoments: viewStore.moments,
|
||||
currentIndex: index
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 30)
|
||||
|
||||
// 加载状态
|
||||
if viewStore.isLoading {
|
||||
// 加载状态 - 直接使用store状态
|
||||
if store.isLoading {
|
||||
HStack {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||||
@@ -97,11 +99,11 @@ struct FeedView: View {
|
||||
}
|
||||
}
|
||||
.refreshable {
|
||||
viewStore.send(.loadLatestMoments)
|
||||
store.send(.loadLatestMoments)
|
||||
}
|
||||
.onAppear {
|
||||
store.send(.onAppear)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
viewStore.send(.onAppear)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,9 +294,11 @@ struct OptimizedImageGrid: View {
|
||||
let imageSize: CGFloat = (availableWidth - spacing * 2) / 3
|
||||
let columns = Array(repeating: GridItem(.fixed(imageSize), spacing: spacing), count: 3)
|
||||
|
||||
LazyVGrid(columns: columns, spacing: spacing) {
|
||||
ForEach(images.prefix(9), id: \.id) { image in
|
||||
SquareImageView(image: image, size: imageSize)
|
||||
WithPerceptionTracking {
|
||||
LazyVGrid(columns: columns, spacing: spacing) {
|
||||
ForEach(images.prefix(9), id: \.id) { image in
|
||||
SquareImageView(image: image, size: imageSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,23 +405,25 @@ struct RealDynamicCardView: View {
|
||||
|
||||
// 图片网格
|
||||
if let images = moment.dynamicResList, !images.isEmpty {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: min(images.count, 3)), spacing: 8) {
|
||||
ForEach(images.prefix(9), id: \.id) { image in
|
||||
AsyncImage(url: URL(string: image.resUrl)) { imageView in
|
||||
imageView
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
} placeholder: {
|
||||
Rectangle()
|
||||
.fill(Color.gray.opacity(0.3))
|
||||
.overlay(
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .white.opacity(0.6)))
|
||||
)
|
||||
WithPerceptionTracking {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: min(images.count, 3)), spacing: 8) {
|
||||
ForEach(images.prefix(9), id: \.id) { image in
|
||||
AsyncImage(url: URL(string: image.resUrl)) { imageView in
|
||||
imageView
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
} placeholder: {
|
||||
Rectangle()
|
||||
.fill(Color.gray.opacity(0.3))
|
||||
.overlay(
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .white.opacity(0.6)))
|
||||
)
|
||||
}
|
||||
.frame(height: 100)
|
||||
.clipped()
|
||||
.cornerRadius(8)
|
||||
}
|
||||
.frame(height: 100)
|
||||
.clipped()
|
||||
.cornerRadius(8)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -513,15 +519,17 @@ struct DynamicCardView: View {
|
||||
.multilineTextAlignment(.leading)
|
||||
|
||||
// 图片网格
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: 3), spacing: 8) {
|
||||
ForEach(0..<3) { imageIndex in
|
||||
Rectangle()
|
||||
.fill(Color.gray.opacity(0.3))
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.overlay(
|
||||
Image(systemName: "photo")
|
||||
.foregroundColor(.white.opacity(0.6))
|
||||
)
|
||||
WithPerceptionTracking {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: 3), spacing: 8) {
|
||||
ForEach(0..<3) { imageIndex in
|
||||
Rectangle()
|
||||
.fill(Color.gray.opacity(0.3))
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.overlay(
|
||||
Image(systemName: "photo")
|
||||
.foregroundColor(.white.opacity(0.6))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,4 +573,4 @@ struct DynamicCardView: View {
|
||||
FeedFeature()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -23,9 +23,7 @@ struct HomeView: View {
|
||||
switch selectedTab {
|
||||
case .feed:
|
||||
FeedView(
|
||||
store: Store(initialState: FeedFeature.State()) {
|
||||
FeedFeature()
|
||||
}
|
||||
store: store.scope(state: \.feedState, action: \.feed)
|
||||
)
|
||||
.transition(.opacity)
|
||||
case .me:
|
||||
|
Reference in New Issue
Block a user